[{"content":"Ces figures proviennent d’un manuel de langages de programmation / conception de compilateurs que je lisais pour un cours. Je les ai gardées sur une seule page de référence : comment la compilation est découpée en phases, comment les langages d’aujourd’hui se situent historiquement, et où la syntaxe ne suffit plus — il faut des règles de priorité, une désambiguïsation pour le else, et des attributs pour la sémantique comme les types.\nPipeline du compilateur Le schéma habituel : programme source → analyseur lexical (jetons) → analyseur syntaxique (arbres d’analyse) → code intermédiaire (avec analyse sémantique) → optimisation optionnelle → générateur de code → code machine sur un ordinateur, plus données d’entrée et résultats. Une table des symboles accompagne les phases de face et alimente la fin de chaîne.\nL’optimisation est marquée optionnelle dans le diagramme pour une raison pédagogique : enseigner les compilateurs et boucles édition-compilation-exécution rapides saute souvent l’optimisation lourde ; les compilateurs de production y investissent en général surtout sur le code intermédiaire, où les transformations sont plus simples qu’au niveau instructions machine.\nGénéalogie des langages de haut niveau La deuxième figure est un graphe temporel : langages comme nœuds, flèches comme influence ou descendance directe. On lit plusieurs fils — Fortran, ALGOL comme pivot, C comme autre pivot vers C++, Java, C#, Perl / PHP / JavaScript, LISP → Scheme → ML → Haskell, BASIC vers Visual Basic, plus COBOL, Ada, Prolog, etc.\nC’est un rappel que « choisir un langage », c’est aussi hériter d’habitudes syntaxiques, de modèles d’exécution et de communautés forgées sur des décennies.\nArbres d’analyse et une affectation non ambiguë Pour l’instruction A = B * (A + C), l’arbre d’analyse rend le regroupement explicite : le + est sous la sous-expression parenthésée, et * combine B avec toute cette sous-expression avant l’affectation. C’est l’artefact que l’analyseur syntaxique transmet aux phases suivantes.\nAmbiguïté : deux arbres pour A = B + C * A Le contraste classique du manuel : la même chaîne de jetons peut correspondre à deux arbres si la grammaire l’autorise. Un arbre regroupe comme B + (C * A) (multiplication plus étroite que l’addition) ; l’autre comme (B + C) * A. Seul le premier correspond à la priorité arithmétique usuelle.\nLes vraies grammaires corrigent cela en stratifiant les expressions (non-terminaux distincts pour termes et facteurs, ou déclarations de priorité dans les générateurs d’analyseurs) pour que l’analyseur ne puisse pas construire la mauvaise forme.\nElse pendouillant Une deuxième classe d’ambiguïté est structurelle, pas arithmétique : des if imbriqués avec un seul else. Une analyse attache le else au if externe ; l’autre au if interne. Les langages adoptent une règle concrète (souvent : le else se lie au if le plus proche) et/ou exigent des accolades ou des marqueurs du type end if pour rendre l’intention syntaxiquement unique.\nGrammaires attribuées : les types parcourent l’arbre La syntaxe seule ne porte pas les types ni la signification. Les grammaires attribuées décorent l’arbre : attributs synthétisés qui remontent (par ex. le type réel d’une expression depuis les feuilles) et attributs hérités qui descendent le contexte (par ex. le type attendu depuis le membre gauche d’une affectation). L’exemple du livre avec A = A + B montre expected_type sur l’expression et actual_type sur chaque var, ce qui est exactement la manière dont un compilateur justifie coercitions ou erreurs.\nSi vous relisez les mêmes chapitres : le fil conducteur va d’abord au pipeline, puis à l’histoire pour le contexte, puis à la syntaxe formelle (arbres), puis là où la syntaxe échoue (ambiguïté), puis comment la sémantique s’attache (attributs). Ces scans sont mon aide-mémoire personnel ; figures et texte d’origine appartiennent au manuel et à l’éditeur.\nÀ lire aussi : notes de cours sur le cycle de vie logiciel — GOALS, waterfall, vérification vs validation.","date":"2026-04-13","date_unix":1776109200,"id":"https://antoineboucher.info/CV/blog/fr/posts/compiler-design-textbook-figures/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/compiler-design-textbook-figures/","post_kind":"article","section":"posts","summary":"Notes de cours tirées d’un manuel sur les langages de programmation — étapes du compilateur, chronologie des grands langages, arbres d’analyse, ambiguïté (opérateurs et else pendouillant) et grammaires attribuées pour les types.","tag_refs":[{"name":"Compilers","permalink":"https://antoineboucher.info/CV/blog/fr/tags/compilers/"},{"name":"Programming Languages","permalink":"https://antoineboucher.info/CV/blog/fr/tags/programming-languages/"},{"name":"Parsing","permalink":"https://antoineboucher.info/CV/blog/fr/tags/parsing/"},{"name":"Computer Science","permalink":"https://antoineboucher.info/CV/blog/fr/tags/computer-science/"},{"name":"Education","permalink":"https://antoineboucher.info/CV/blog/fr/tags/education/"}],"tags":["Compilers","Programming Languages","Parsing","Computer Science","Education"],"tags_text":"Compilers Programming Languages Parsing Computer Science Education","thumb":"https://antoineboucher.info/CV/blog/posts/compiler-design-textbook-figures/images/ambiguity-operator-precedence_hu_15216295c2ec2aca.png","title":"Pipeline de compilation, généalogie des langages et pourquoi les grammaires comptent"},{"content":"Fil conducteur en trois parties sur la similarité entre films : partie 1 — embeddings dans PostgreSQL + pgvector et recherche du plus proche voisin en SQL ; partie 2 — Qdrant et MovieLens (vecteurs denses pour la recherche sémantique, vecteurs creux pour un voisinage façon filtrage collaboratif) ; partie 3 — le même catalogue pgvector comme couche de récupération pour un petit pipeline RAG avec LangChain et Ollama. Ci-dessous, des extraits animés du travail (movie-similarities-1.gif … 3.gif).\nVisualisations Partie 1 — pgvector / SQL : films proches à partir des embeddings et des métriques de distance.\nPartie 2 — Qdrant + MovieLens : recherche dense de films ou voisinage creux utilisateur–notes.\nPartie 3 — Q\u0026amp;R ancrée : question → lignes récupérées → réponse du LLM liée au catalogue.\nRessources GitHub (cours / notebooks) : AlgoETS/SimilityVectorEmbedding — dont postgres/3.LLMS.ipynb pour la partie 3 Medium (article pgvector d’origine) : Using vector databases to find similar movies (Part 1) Discord : discord.gg/Mgf6STuvzZ Partie 1 — PostgreSQL, pgvector et films similaires Le projet montre comment des embeddings et une base vectorielle (PostgreSQL + pgvector) permettent la recherche de similarité sur descriptions et métadonnées de films, en encodant le texte avec des modèles NLP et en comparant les titres dans l’espace vectoriel.\nRequêtes vectorielles et similarité cosinus Requêtes avec pgvector Pgvector est une extension PostgreSQL pour stocker et interroger efficacement des vecteurs de grande dimension. Ici, les embeddings représentent le contenu sémantique des descriptions et métadonnées, ce qui permet des requêtes avancées (plus proches voisins).\nPlusieurs métriques sont disponibles, dont la distance cosinus (\u0026lt;=\u0026gt; en SQL). Exemple de recherche de films similaires par cosinus :\nSELECT title, embedding\nFROM movies\nORDER BY embedding \u0026lt;=\u0026gt; (SELECT embedding FROM movies WHERE title = %s) ASC\nLIMIT 10;\nSimilarité cosinus Le cosinus de l’angle entre deux vecteurs ; très utilisé en NLP pour comparer des documents (ici des fiches films) quelle que soit leur longueur.\nCosine Similarity = (A · B) / (|A| |B|)\nAutres distances dans pgvector Pgvector propose aussi L2 (euclidienne), L1 (Manhattan), produit scalaire, etc., selon la requête et la nature des données.\nL2 : écarts absolus entre vecteurs. L1 : utile en grande dimension. Installation Bibliothèques Python nécessaires :\npip install transformers psycopg2 numpy boto3 torch scikit-learn matplotlib nltk sentence-transformers\nConfiguration de la base #!/bin/bash\n# Install pgvector\ngit clone \u0026ndash;branch v0.7.0 https://github.com/pgvector/pgvector.git\ncd pgvector\ndocker build \u0026ndash;build-arg PG_MAJOR=16 -t builder/pgvector .\ncd ..\ndocker-compose up -d\n# ollama\ncurl -fsSL https://ollama.com/install.sh | sh\nollama pull bakllava\nollama pull llama2:13b-chat\nversion: \u0026lsquo;3.8\u0026rsquo;\nservices:\npostgres:\nimage: builder/pgvector\nenvironment:\nPOSTGRES_USER: admin\nPOSTGRES_PASSWORD: admin\nPOSTGRES_DB: admin\nports:\n- \u0026ldquo;5432:5432\u0026rdquo;\nvolumes:\n- ./data:/var/lib/postgresql/data\nExemple d’entrée film Exemple de représentation dans movies.json :\n{\n\u0026ldquo;titre\u0026rdquo;: \u0026ldquo;George of the Jungle\u0026rdquo;,\n\u0026ldquo;annee\u0026rdquo;: \u0026ldquo;1997\u0026rdquo;,\n\u0026ldquo;pays\u0026rdquo;: \u0026ldquo;USA\u0026rdquo;,\n\u0026ldquo;langue\u0026rdquo;: \u0026ldquo;English\u0026rdquo;,\n\u0026ldquo;duree\u0026rdquo;: \u0026ldquo;92\u0026rdquo;,\n\u0026ldquo;resume\u0026rdquo;: \u0026ldquo;George grows up in the jungle raised by apes. Based on the Cartoon series.\u0026rdquo;,\n\u0026ldquo;genre\u0026rdquo;: [\u0026ldquo;Action\u0026rdquo;, \u0026ldquo;Adventure\u0026rdquo;, \u0026ldquo;Comedy\u0026rdquo;, \u0026ldquo;Family\u0026rdquo;, \u0026ldquo;Romance\u0026rdquo;],\n\u0026ldquo;realisateur\u0026rdquo;: {\u0026quot;_id\u0026quot;: \u0026ldquo;918873\u0026rdquo;, \u0026ldquo;__text\u0026rdquo;: \u0026ldquo;Sam Weisman\u0026rdquo;},\n\u0026ldquo;scenariste\u0026rdquo;: [\u0026ldquo;Jay Ward\u0026rdquo;, \u0026ldquo;Dana Olsen\u0026rdquo;],\n\u0026ldquo;role\u0026rdquo;: [\n{\u0026ldquo;acteur\u0026rdquo;: {\u0026quot;_id\u0026quot;: \u0026ldquo;409\u0026rdquo;, \u0026ldquo;__text\u0026rdquo;: \u0026ldquo;Brendan Fraser\u0026rdquo;}, \u0026ldquo;personnage\u0026rdquo;: \u0026ldquo;George of the Jungle\u0026rdquo;},\n{\u0026ldquo;acteur\u0026rdquo;: {\u0026quot;_id\u0026quot;: \u0026ldquo;5182\u0026rdquo;, \u0026ldquo;__text\u0026rdquo;: \u0026ldquo;Leslie Mann\u0026rdquo;}, \u0026ldquo;personnage\u0026rdquo;: \u0026ldquo;Ursula Stanhope\u0026rdquo;}\n],\n\u0026ldquo;poster\u0026rdquo;: \u0026ldquo;https://m.media-amazon.com/images/M/MV5BNTdiM2VjYjYtZjEwNS00ZWU5LWFkZGYtZGYxMDcwMzY1OTEzL2ltYWdlL2ltYWdlXkEyXkFqcGdeQXVyMTczNjQwOTY@._V1_SY150_CR0,0,101,150_.jpg\u0026rdquo;,\n\u0026ldquo;_id\u0026rdquo;: \u0026ldquo;119190\u0026rdquo;\n}\nTravailler avec les embeddings Les embeddings sont produits avec BERT, Sentence Transformers, etc., puis utilisés dans pgvector pour des recherches de similarité cosinus rapides.\nGénérer les embeddings Modèles et génération pour le catalogue films :\nmodels = {\n\u0026ldquo;bart\u0026rdquo;: {\n\u0026ldquo;model_name\u0026rdquo;: \u0026ldquo;facebook/bart-large\u0026rdquo;,\n\u0026ldquo;tokenizer\u0026rdquo;: AutoTokenizer.from_pretrained(\u0026ldquo;facebook/bart-large\u0026rdquo;, trust_remote_code=True),\n\u0026ldquo;model\u0026rdquo;: AutoModel.from_pretrained(\u0026ldquo;facebook/bart-large\u0026rdquo;, trust_remote_code=True)\n},\n\u0026ldquo;gte\u0026rdquo;: {\n\u0026ldquo;model_name\u0026rdquo;: \u0026ldquo;Alibaba-NLP/gte-large-en-v1.5\u0026rdquo;,\n\u0026ldquo;tokenizer\u0026rdquo;: AutoTokenizer.from_pretrained(\u0026ldquo;Alibaba-NLP/gte-large-en-v1.5\u0026rdquo;, trust_remote_code=True),\n\u0026ldquo;model\u0026rdquo;: AutoModel.from_pretrained(\u0026ldquo;Alibaba-NLP/gte-large-en-v1.5\u0026rdquo;, trust_remote_code=True)\n},\n\u0026ldquo;MiniLM\u0026rdquo;: {\n\u0026ldquo;model_name\u0026rdquo;: \u0026lsquo;all-MiniLM-L12-v2\u0026rsquo;,\n\u0026ldquo;model\u0026rdquo;: SentenceTransformer(\u0026lsquo;all-MiniLM-L12-v2\u0026rsquo;)\n},\n\u0026ldquo;roberta\u0026rdquo;: {\n\u0026ldquo;model_name\u0026rdquo;: \u0026lsquo;sentence-transformers/nli-roberta-large\u0026rsquo;,\n\u0026ldquo;model\u0026rdquo;: SentenceTransformer(\u0026lsquo;sentence-transformers/nli-roberta-large\u0026rsquo;)\n},\n\u0026ldquo;e5-large\u0026rdquo;:{\n\u0026ldquo;model_name\u0026rdquo;: \u0026lsquo;intfloat/e5-large\u0026rsquo;,\n\u0026ldquo;tokenizer\u0026rdquo;: AutoTokenizer.from_pretrained(\u0026lsquo;intfloat/e5-large\u0026rsquo;, trust_remote_code=True),\n\u0026ldquo;model\u0026rdquo;: AutoModel.from_pretrained(\u0026lsquo;intfloat/e5-large\u0026rsquo;, trust_remote_code=True)\n}\n}\nTest Cosine Similarity with Embeddings # Example sentences\nsentences_test = [\u0026ldquo;This is a fox.\u0026rdquo;, \u0026ldquo;This is a dog.\u0026rdquo;, \u0026ldquo;This is a cat.\u0026rdquo;, \u0026ldquo;This is a fox.\u0026rdquo;]\n# Generate embeddings\nembeddings_test = models[\u0026ldquo;MiniLM\u0026rdquo;][\u0026ldquo;model\u0026rdquo;].encode(sentences_test)\n# Calculate cosine similarity\ncosine_similarity = np.dot(embeddings_test[0], embeddings_test[1]) / (np.linalg.norm(embeddings_test[0]) * np.linalg.norm(embeddings_test[1]))\nprint(\u0026ldquo;Cosine Similarity:\u0026rdquo;, cosine_similarity)\ncosine_similarity = np.dot(embeddings_test[0], embeddings_test[3]) / (np.linalg.norm(embeddings_test[0]) * np.linalg.norm(embeddings_test[3]))\nprint(\u0026ldquo;Cosine Similarity Same:\u0026rdquo;, cosine_similarity)\nCosine Similarity: 0.46493083\nCosine Similarity Same: 1.0\nRemove stopwords to reduce noise import nltk\nfrom nltk.corpus import stopwords\nnltk.download(‘stopwords’)\nDefine a list of movie titles current_directory = os.getcwd()\nwith open(os.path.join(current_directory, \u0026ldquo;movies.json\u0026rdquo;), \u0026ldquo;r\u0026rdquo;) as f:\nmovies = json.load(f)\nmovies_data = []\nfor movie in movies[\u0026ldquo;films\u0026rdquo;][\u0026ldquo;film\u0026rdquo;]:\nroles = movie.get(\u0026quot;role\u0026quot;, \\[\\]) if isinstance(roles, dict): # If 'roles' is a dictionary, make it a single-item list roles = \\[roles\\] \\# Extract actor information actors = \\[\\] for role in roles: actor\\_info = role.get(\u0026quot;acteur\u0026quot;, {}) if \u0026quot;\\_\\_text\u0026quot; in actor\\_info: actors.append(actor\\_info\\[\u0026quot;\\_\\_text\u0026quot;\\]) movies\\_data.append({ \u0026quot;title\u0026quot;: movie.get(\u0026quot;titre\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;year\u0026quot;: movie.get(\u0026quot;annee\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;country\u0026quot;: movie.get(\u0026quot;pays\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;language\u0026quot;: movie.get(\u0026quot;langue\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;duration\u0026quot;: movie.get(\u0026quot;duree\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;summary\u0026quot;: movie.get(\u0026quot;synopsis\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;genre\u0026quot;: movie.get(\u0026quot;genre\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;director\u0026quot;: movie.get(\u0026quot;realisateur\u0026quot;, {\u0026quot;\\_\\_text\u0026quot;: \u0026quot;\u0026quot;}).get(\u0026quot;\\_\\_text\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;writers\u0026quot;: movie.get(\u0026quot;scenariste\u0026quot;, \\[\\]), \u0026quot;actors\u0026quot;: actors, \u0026quot;poster\u0026quot;: movie.get(\u0026quot;affiche\u0026quot;, \u0026quot;\u0026quot;), \u0026quot;id\u0026quot;: movie.get(\u0026quot;id\u0026quot;, \u0026quot;\u0026quot;) }) Generate embeddings for movies def preprocess(text):\n# Example preprocessing step simplified for demonstration\ntokens = text.split()\n# Assuming stopwords are already loaded to avoid loading them in each process\nstopwords_set = set(stopwords.words(\u0026rsquo;english\u0026rsquo;))\ntokens = [word for word in tokens if word.lower() not in stopwords_set]\nreturn \u0026rsquo; \u0026lsquo;.join(tokens)\ndef normalize_embeddings(embeddings):\n\u0026quot;\u0026quot;\u0026quot; Normalize the embeddings to unit vectors. \u0026quot;\u0026quot;\u0026quot;\nnorms = np.linalg.norm(embeddings, axis=1, keepdims=True)\nnormalized_embeddings = embeddings / norms\nreturn normalized_embeddings\ndef generate_embedding(movies_data, model_key, normalize=True):\nmodel_config = models[model_key]\nif \u0026rsquo;tokenizer\u0026rsquo; in model_config:\n# Handle HuggingFace transformer models\nmovie_texts = [\nf\u0026quot;{preprocess(movie[\u0026rsquo;title\u0026rsquo;])} {movie[\u0026lsquo;year\u0026rsquo;]} {\u0026rsquo; \u0026lsquo;.join(movie[\u0026lsquo;genre\u0026rsquo;])} \u0026quot;\nf\u0026quot;{\u0026rsquo; \u0026lsquo;.join(movie[\u0026lsquo;actors\u0026rsquo;])} {movie[\u0026lsquo;director\u0026rsquo;]} \u0026quot;\nf\u0026quot;{preprocess(movie[\u0026lsquo;summary\u0026rsquo;])} {movie[\u0026lsquo;country\u0026rsquo;]}\u0026quot;\nfor movie in movies_data\n]\ninputs = model_config[\u0026rsquo;tokenizer\u0026rsquo;](movie_texts, padding=True, truncation=True, return_tensors=\u0026ldquo;pt\u0026rdquo;)\nwith torch.no_grad():\noutputs = model_config[\u0026lsquo;model\u0026rsquo;](**inputs)\nembeddings = outputs.last_hidden_state.mean(dim=1).numpy()\nelse:\n# Handle Sentence Transformers\nmovie_texts = [\nf\u0026quot;{preprocess(movie[\u0026rsquo;title\u0026rsquo;])} {movie[\u0026lsquo;year\u0026rsquo;]} {\u0026rsquo; \u0026lsquo;.join(movie[\u0026lsquo;genre\u0026rsquo;])} \u0026quot;\nf\u0026quot;{\u0026rsquo; \u0026lsquo;.join(movie[\u0026lsquo;actors\u0026rsquo;])} {movie[\u0026lsquo;director\u0026rsquo;]} \u0026quot;\nf\u0026quot;{preprocess(movie[\u0026lsquo;summary\u0026rsquo;])} {movie[\u0026lsquo;country\u0026rsquo;]}\u0026quot;\nfor movie in movies_data\n]\nembeddings = model_config[\u0026lsquo;model\u0026rsquo;].encode(movie_texts)\nif normalize: embeddings = normalize\\_embeddings(embeddings) return embeddings embeddings_MiniLM = generate_embedding(movies_data, \u0026lsquo;MiniLM\u0026rsquo;)\nembeddings_MiniLM = np.array(embeddings_MiniLM)\nprint(\u0026ldquo;MiniLM embeddings shape:\u0026rdquo;, embeddings_MiniLM.shape)\nprint(\u0026ldquo;MiniLM embeddings:\u0026rdquo;, embeddings_MiniLM[0])\nCreate connection to the database conn = psycopg2.connect(database=”admin”, host=”localhost”, user=”admin”, password=”admin”, port=”5432\u0026quot;)\ncur = conn.cursor()\ncur.execute(\u0026ldquo;CREATE EXTENSION IF NOT EXISTS vector;\u0026rdquo;)\nconn.commit()\ncur.execute(\u0026ldquo;CREATE EXTENSION IF NOT EXISTS cube;\u0026rdquo;)\nconn.commit()\nInserting Data into the Database Insert movie titles and their embeddings into the movies table:\ndef setup_database():\ncur.execute(\u0026lsquo;DROP TABLE IF EXISTS movies\u0026rsquo;)\ncur.execute(\u0026rsquo;\u0026rsquo;\u0026rsquo;\nCREATE TABLE movies (\nid SERIAL PRIMARY KEY,\ntitle TEXT NOT NULL,\nactors TEXT,\nyear INTEGER,\ncountry TEXT,\nlanguage TEXT,\nduration INTEGER,\nsummary TEXT,\ngenre TEXT[],\ndirector TEXT,\nscenarists TEXT[],\nposter TEXT,\nembedding_bart VECTOR(1024),\nembedding_gte VECTOR(1024),\nembedding_MiniLM VECTOR(384),\nembedding_roberta VECTOR(1024),\nembedding_e5_large VECTOR(1024)\n);\n\u0026lsquo;\u0026rsquo;\u0026rsquo;)\nconn.commit()\nsetup_database()\nInsert movie titles and their embeddings into the ‘movies’ table def insert_movies(movie_data, embeddings_bart, embeddings_gte, embeddings_MiniLM, embeddings_roberta, embeddings_e5_large):\nfor movie, emb_bart, emb_gte, emb_MiniLM , emb_roberta, emb_e5_large in zip(movie_data, embeddings_bart, embeddings_gte, embeddings_MiniLM, embeddings_roberta, embeddings_e5_large):\n# Joining actors into a single string separated by commas\nactor_names = \u0026lsquo;, \u0026lsquo;.join(movie[\u0026lsq","date":"2026-04-13","date_unix":1776096e3,"id":"https://antoineboucher.info/CV/blog/fr/posts/vector-databases-similar-movies/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/vector-databases-similar-movies/","post_kind":"article","section":"posts","summary":"Similarité de films avec pgvector et SQL, Qdrant et MovieLens (vecteurs denses et creux), puis RAG LangChain + Ollama sur le même catalogue — embeddings, kPPV et réponses ancrées.","tag_refs":[{"name":"PostgreSQL","permalink":"https://antoineboucher.info/CV/blog/fr/tags/postgresql/"},{"name":"Pgvector","permalink":"https://antoineboucher.info/CV/blog/fr/tags/pgvector/"},{"name":"Qdrant","permalink":"https://antoineboucher.info/CV/blog/fr/tags/qdrant/"},{"name":"Python","permalink":"https://antoineboucher.info/CV/blog/fr/tags/python/"},{"name":"Embeddings","permalink":"https://antoineboucher.info/CV/blog/fr/tags/embeddings/"},{"name":"RAG","permalink":"https://antoineboucher.info/CV/blog/fr/tags/rag/"},{"name":"Machine Learning","permalink":"https://antoineboucher.info/CV/blog/fr/tags/machine-learning/"}],"tags":["PostgreSQL","pgvector","Qdrant","Python","Embeddings","RAG","Machine Learning"],"tags_text":"PostgreSQL pgvector Qdrant Python Embeddings RAG Machine Learning","thumb":"https://antoineboucher.info/CV/blog/posts/vector-databases-similar-movies/featured_hu_b86049bbe2a8d095.png","title":"Similarité entre films et recherche vectorielle"},{"content":"J’ai publié marketwatch sur PyPI : un petit client Python pour le jeu boursier virtuel MarketWatch (paper trading), pas l’accès à un courtier réel. Pour scripter des listes de suivi, récupérer les données d’une partie ou du portefeuille, ou expérimenter une automatisation dans le cadre du jeu, le paquet encapsule les flux dans une API simple.\nLiens Paquet : pypi.org/project/marketwatch Documentation : antoinebou12.github.io/marketwatch Source et tickets : github.com/antoinebou12/marketwatch Ce que ça permet Créer et gérer des listes de suivi Lire les détails et paramètres d’une partie Inspecter portefeuille, positions et ordres en attente Acheter et vendre (dans le jeu) Récupérer le classement d’une partie Utile pour explorer des stratégies automatisées ou de petits bots dans les règles du jeu — voir la doc pour les noms de méthodes et les structures retournées.\nDémarrage rapide pip install marketwatch from marketwatch import MarketWatch mw = MarketWatch(\u0026#34;your_username\u0026#34;, \u0026#34;your_password\u0026#34;) mw.get_games() mw.get_price(\u0026#34;AAPL\u0026#34;) Pour les cas limites de connexion, chaque méthode et des exemples d’ordres et de listes, voir la documentation.\nL’automatisation peut entrer en conflit avec les conditions d’utilisation ou les limites de débit de la plateforme ; utilisez le paquet de façon responsable et vérifiez les règles MarketWatch pour tout usage non trivial.\nQuestions ou bugs bienvenus sur GitHub.","date":"2026-04-13","date_unix":1776088800,"id":"https://antoineboucher.info/CV/blog/fr/posts/marketwatch-python-trading/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/marketwatch-python-trading/","post_kind":"article","section":"posts","summary":"Paquet PyPI `marketwatch` — client Python pour le jeu boursier virtuel MarketWatch (listes de suivi, parties, portefeuille, ordres, classement).","tag_refs":[{"name":"Python","permalink":"https://antoineboucher.info/CV/blog/fr/tags/python/"},{"name":"MarketWatch","permalink":"https://antoineboucher.info/CV/blog/fr/tags/marketwatch/"},{"name":"Trading","permalink":"https://antoineboucher.info/CV/blog/fr/tags/trading/"},{"name":"Finance","permalink":"https://antoineboucher.info/CV/blog/fr/tags/finance/"},{"name":"Open Source","permalink":"https://antoineboucher.info/CV/blog/fr/tags/open-source/"}],"tags":["Python","MarketWatch","Trading","Finance","Open Source"],"tags_text":"Python MarketWatch Trading Finance Open Source","thumb":"https://antoineboucher.info/CV/blog/posts/marketwatch-python-trading/featured_hu_2e3aed53151fd1e1.png","title":"Bibliothèque Python pour le trading virtuel MarketWatch"},{"content":"Version anglaise — même sujet, même fil d’URL.\nEn bref Le Business Model Canvas (BMC) décrit comment l’organisation crée, livre et capture de la valeur ; il répond à trois questions : désirabilité, faisabilité, viabilité. On commence en général par segment de clientèle et proposition de valeur, puis on itère : le modèle change avec le marché. Segmenter, c’est rendre le marché explicite (B2B vs B2C, critères concrets) plutôt que des étiquettes vagues (« médecins », « parents »). Les entrevues servent à la découverte : objectif d’apprendre, pas de vendre ; viser connecter, pas convaincre — en restant attaché au problème, pas à la solution. Le feedback structuré (forces + une piste d’amélioration) et une pitch courte (sans diapos) aident à clarifier l’idée tôt. Une roadmap produit est une vue stratégique dans le temps, pas un plan de projet détaillé ; elle s’aligne sur vision, public, horizon, métriques et ressources. PoC, prototype et MVP ne jouent pas le même rôle : technologie, interaction utilisateur, puis première version sur le marché à éprouver. Marché et proposition de valeur : tenir compte des forces externes (macro, industrie, tendances) et formuler la valeur comme offre + bénéfice pour le client. Le pitch suit souvent Intéresser → Croire → Rejoindre : problème d’abord, preuves et différenciation, puis demande précise. Ce texte est une synthèse personnelle à partir de matériel de cours QcES (cohorte printemps 2024) et d’intervenants ; ce n’est pas un document officiel du programme.\nPourquoi ces blocs s’enchaînent En pratique, on ne « remplit » pas le BMC une fois pour toutes. On forme des hypothèses, on va chercher des données qualitatives (entrevues, observations), on ajuste segments et proposition de valeur, puis on priorise ce qui part sur une roadmap et ce qui finit dans un pitch. Le schéma suivant résume cette boucle.\nflowchart LR hyp[Hypothèse] --\u0026gt; ent[Entrevue] ent --\u0026gt; ins[Insight] ins --\u0026gt; bmc[BMC et segment] bmc --\u0026gt; road[Roadmap] road --\u0026gt; pit[Pitch] Modèle d’affaires : le BMC comme langage commun Le BMC regroupe neuf blocs (segments, proposition de valeur, canaux, relations clients, revenus, ressources, activités, partenaires, structure de coûts). L’idée forte : tous les blocs comptent pour la survie du projet, et le canevas est un outil vivant — à réviser quand le marché ou la concurrence bouge.\nLes sessions insistent sur une progression : comprendre pour qui on crée de la valeur et comment on la promet, avant de sur-optimiser le reste. La boucle hypothèse–validation relie recherche, entrevues et décisions : on teste des croyances sur le client et le problème, pas seulement sur la technique.\nSegment de clientèle : du flou à l’actionnable Distinction utile entre consommateur, utilisateur final et client (celui qui achète n’est pas toujours celui qui utilise). Un client au sens « early » a souvent : un problème, une conscience du problème, une recherche de solution, parfois déjà un bricolage, et un budget possible.\nB2B invite à la segmentation firmographique (taille, lieu, secteur, dynamique d’achat). B2C mobilise démographie, géographie, psychographie, comportement. L’exercice consiste à remplacer des catégories trop larges par des segments vérifiables — ceux qu’on peut retrouver, contacter et interviewer.\nPartager l’idée et recevoir du feedback Une format courte (environ 90 secondes, sans slides) force à clarifier : problème compréhensible hors jargon, bénéficiaire, solution et avantages. Ce n’est pas une exigence de tout couvrir (taille du marché, concurrence, etc.) au premier passage ; c’est un premier filtre de clarté.\nPour recevoir du feedback : écoute active (comprendre plutôt que répliquer), prise de notes, posture d’apprentissage. Pour donner du feedback : souligner une force réelle, puis une opportunité concrète (« j’aimerais en savoir plus sur… », « ceci gagnerait à… »).\nEntrevues : la découverte continue Les entrevues sont présentées comme la source la plus directe pour combler ce qui manque dans un premier BMC. L’objectif n’est pas de vendre ni de « pitcher » sa solution pendant l’échange, mais d’apprendre du vécu de l’autre.\nLes personnes à rencontrer dépassent souvent le « client idéal » : utilisateurs de solutions concurrentes, experts, fournisseurs, communautés, etc. — tout acteur touché par le problème visé. Et ce n’est pas ponctuel : la découverte se fait continuellement au fil des pivots.\nLes obstacles classiques — trouver les bonnes personnes, obtenir un rendez-vous, mener un entretien utile — se gagnent avec préparation et humilité. La formule répétée : tomber amoureux du problème, pas de la solution ; connecter plutôt que convaincre.\nRoadmap et maturité : PoC, prototype, MVP Une roadmap résume où on va (vision, initiatives clés) sur un horizon choisi, pour une audience donnée (équipe, investisseurs, partenaires). Elle se distingue d’un plan de projet opérationnel. Avant d’y placer des blocs, les questions types : vision, lecteurs, durée, métriques, périmètre, ressources et format adapté.\nPreuve de concept : la techno ou l’approche peut fonctionner. Prototype : on explore comment l’utilisateur interagit ; souvent hors marché. MVP : version suffisante pour être mise entre les mains de vrais utilisateurs ou clients, pour apprendre vite (y compris en voyant ce qui casse). Les roadmaps évoluent avec la phase (découverte, validation, croissance).\nMarché, tendances et proposition de valeur Comprendre le marché, ce n’est pas seulement compter des adresses : c’est situer le projet parmi des forces externes — macro (PESTEL), dynamique sectorielle, comportement des clients, grandes tendances. Ces grilles aident à anticiper contraintes et opportunités.\nLa proposition de valeur combine ce que vous fournissez (produit, service, « véhicule ») et l’avantage pour le client. Les supports de cours insistent sur les points de douleur et sur une hypothèse sur pourquoi la personne se soucie du problème — matière première des entrevues de découverte.\nStorytelling et pitch : Intéresser, Croire, Rejoindre Le pitch est un discours qui cherche une suite concrète : temps d’autrui, ressources, introduction, financement. Les formats varient (30 secondes à une demi-heure, avec ou sans diapos).\nUne structure efficace vue en session :\nIntéresser — partir du problème et de son importance pour un public identifiable ; ne pas commencer par la solution. Croire — présenter la solution, la différenciation, les preuves (traction, équipe, résultats récents). Rejoindre — formuler une demande précise : type d’aide, montant, expertise, partenariat, etc. Un gabarit type (~1 min 30) enchaîne : qui vous êtes, problème, offre pour une cible, proposition de valeur, contraste avec l’existant, nouvelle récente, appel à l’action.\nEn conclusion Ce parcours QcES relie des outils (BMC, segmentation, roadmap) à des comportements (entrevues, feedback, pitch). Le fil conducteur : garder le problème au centre, rendre les hypothèses testables, et communiquer avec assez de clarté pour que d’autres puissent aider — pas seulement applaudir.\nSi vous suivez un programme similaire, la valeur est souvent dans la régularité : quelques entrevues bien menées valent mieux qu’un canevas parfaitement décoré.","date":"2026-04-13","date_unix":1776088800,"id":"https://antoineboucher.info/CV/blog/fr/posts/qces-lean-discovery-pitch/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/qces-lean-discovery-pitch/","post_kind":"conference","section":"posts","summary":"Synthèse personnelle des séances QcES — canevas d’affaires, segment client, feedback, entrevues, roadmap, marché, proposition de valeur et storytelling.","tag_refs":[{"name":"QcES","permalink":"https://antoineboucher.info/CV/blog/fr/tags/qces/"},{"name":"Entrepreneurship","permalink":"https://antoineboucher.info/CV/blog/fr/tags/entrepreneurship/"},{"name":"BMC","permalink":"https://antoineboucher.info/CV/blog/fr/tags/bmc/"},{"name":"Lean Startup","permalink":"https://antoineboucher.info/CV/blog/fr/tags/lean-startup/"},{"name":"Pitch","permalink":"https://antoineboucher.info/CV/blog/fr/tags/pitch/"},{"name":"Education","permalink":"https://antoineboucher.info/CV/blog/fr/tags/education/"}],"tags":["QcES","Entrepreneurship","BMC","Lean Startup","Pitch","Education"],"tags_text":"QcES Entrepreneurship BMC Lean Startup Pitch Education","thumb":"/CV/blog/images/post-kind-conference.png","title":"Du BMC au pitch : notes de parcours QcES (printemps 2024)"},{"content":"Ces pages viennent d’un manuel de génie logiciel utilisé en cours. J’y ai regroupé les figures sur une seule page de référence : planification orientée objectifs, comment le modèle waterfall enchaîne les activités (avec vérification et validation appariées à chaque phase), une variante incrémentale, la liste des sous-objectifs du cycle de vie du manuel, le rappel que les conseils méthodo dépendent du contexte, et un court passage d’éthique sur l’impact sur les personnes.\nApproche GOALS (figure 3-1) L’organigramme GOALS est un schéma descendant : fixer les objectifs globaux du cycle de vie (fonctions, contraintes, délais, utilisabilité, maintenabilité), analyser le problème et esquisser la structure de solution, séparer les préoccupations en sous-objectifs, développer des solutions pour chaque sous-objectif en parallèle quand c’est possible, puis valider ces solutions par rapport aux autres objectifs et itérer jusqu’à ce que la décision « tous les objectifs sont satisfaits ? » soit oui.\nOn y reconnaît ce que les méthodes ultérieures font encore : décomposer, vérifier la cohérence entre préoccupations, boucler plutôt que supposer qu’un seul passage suffit.\nTrier les conseils logiciels (figure 3-4) La figure « sorting out software advice » est un nuage de pratiques — top-down vs outside-in, revues de code, équipes de test indépendantes, équipes « chief programmer », jalons mesurables, gestion de configuration, programmation structurée, « build it twice », impliquer l’utilisateur, et bien d’autres. Le texte insiste : le même slogan peut être juste dans un contexte et superflu dans un autre (par ex. construire une première version jetable quand le domaine est flou vs quand il est déjà bien connu).\nUtile comme liste d’idées à considérer, pas comme une recette unique à appliquer partout.\nÉthique : système de présence scolaire en milieu urbain (étude de cas chapitre 2) La citation en italique sur cette page est celle que j’ai soulignée en marge : les ingénieur·e·s peuvent améliorer les résultats pour la société en prêtant attention aux implications humaines et sociales à long terme des conceptions, pas seulement à la justesse technique.\nÇa va de pair avec exigences et validation — « le bon produit » inclut à qui il sert et comment il affecte les personnes.\nCycle de vie waterfall (figure 4-1) Le diagramme waterfall classique enchaîne les phases dans l’ordre. Chaque boîte est coupée en diagonale : travail de développement d’un côté et activité de V\u0026amp;V assortie de l’autre — de la faisabilité système aux exigences, conception produit, conception détaillée, code, intégration, mise en œuvre, jusqu’à exploitation et maintenance. Les flèches de retour matérialisent les retouches quand une revue de phase révèle des problèmes.\nMême quand une équipe ne livre pas un « pur waterfall », le diagramme nomme encore les types d’artefacts et de contrôles qui réapparaissent sous d’autres noms.\nWaterfall incrémental (figure 4-4) La variante incrémentale ancre une conception produit commune, puis enchaîne des incréments parallèles ou décalés à travers conception détaillée, codage, intégration, etc. — chaque incrément gardant la même paire construction / vérification et le retour vers les étapes amont (y compris la conception produit).\nC’est une façon structurée de montrer une livraison par tranches sans prétendre que les décisions en amont ne changent jamais.\nSous-objectifs, vérification et validation (chapitre 4) Cette page liste neuf sous-objectifs d’ingénierie dans l’ordre : faisabilité, exigences, conception produit, conception détaillée, codage, intégration, mise en œuvre, maintenance (répétée à chaque mise à jour), et retrait de service. Elle définit ensuite la vérification comme adéquation à la spécification — « Construit-on le produit correctement ? » — et la validation comme adéquation à la mission — « Construit-on le bon produit ? » La gestion de configuration apparaît aux côtés de la V\u0026amp;V comme autre obligation transversale. Une note admet que la séquence stricte est une simplification pédagogique ; prototype, développement incrémental et chevauchement sont cités comme ajustements courants.\nCes scans servent à ma révision ; figures et formulations appartiennent au livre et à l’éditeur d’origine. Pour des notes liées sur compilateurs et grammaires dans un autre manuel, voir l’article compagnon sur le pipeline de compilation, la généalogie des langages et les arbres d’analyse.","date":"2026-04-13","date_unix":1776081600,"id":"https://antoineboucher.info/CV/blog/fr/posts/software-engineering-textbook-figures/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/software-engineering-textbook-figures/","post_kind":"article","section":"posts","summary":"Scans de cours tirés d’un manuel de génie logiciel — décomposition par objectifs, waterfall classique et incrémental, neuf sous-objectifs du cycle de vie, V\u0026V, collage de conseils de processus, et morale d’une étude de cas en éthique.","tag_refs":[{"name":"Software Engineering","permalink":"https://antoineboucher.info/CV/blog/fr/tags/software-engineering/"},{"name":"SDLC","permalink":"https://antoineboucher.info/CV/blog/fr/tags/sdlc/"},{"name":"Waterfall","permalink":"https://antoineboucher.info/CV/blog/fr/tags/waterfall/"},{"name":"Verification","permalink":"https://antoineboucher.info/CV/blog/fr/tags/verification/"},{"name":"Validation","permalink":"https://antoineboucher.info/CV/blog/fr/tags/validation/"},{"name":"Education","permalink":"https://antoineboucher.info/CV/blog/fr/tags/education/"}],"tags":["Software Engineering","SDLC","Waterfall","Verification","Validation","Education"],"tags_text":"Software Engineering SDLC Waterfall Verification Validation Education","thumb":"https://antoineboucher.info/CV/blog/posts/software-engineering-textbook-figures/images/ethics-urban-school-attendance_hu_4504e477a7e9bdd1.png","title":"Notes sur le cycle de vie logiciel — GOALS, waterfall, vérification vs validation"},{"content":"Introduction Blender offre une API Python puissante pour scripts, add-ons et plugins. Un point délicat : installer des paquets Python tiers dans l’environnement Python isolé de Blender.\nContrairement à une installation Python système, Blender embarque son propre interprète : pip « global » ne suffit pas toujours. Cet article propose une méthode générale et robuste pour installer les dépendances des add-ons tout en restant compatible entre versions.\nLancer l’installateur depuis l’éditeur de texte (espace Scripting).\nPourquoi des paquets externes dans le Python de Blender ? Beaucoup d’add-ons avancés reposent sur des bibliothèques comme :\nNumPy \u0026amp; SciPy — calcul scientifique et maillages Meshio — conversion de formats de maillage Pillow — traitement d’image Requests — appels HTTP / APIs PyTorch / TensorFlow — ML Ces paquets doivent être installés dans l’arborescence / l’environnement utilisés par Blender, pas seulement dans le Python du système.\nScript Python généralisé pour add-ons Le script ci-dessous installe automatiquement les paquets manquants via le Python de Blender (sys.executable), dans un répertoire utilisateur, avec retour utilisateur (logs + popups).\nFonctionnalités Fonctionne dans Blender, sans ligne de commande obligatoire Installe plusieurs paquets en une passe Utilise un répertoire utilisateur plutôt que les fichiers cœur de Blender S’exécute en arrière-plan pour ne pas bloquer l’UI Script d’installation import bpy import sys import site import logging import subprocess import threading # Set up logging logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) # List of packages required by the add-on/plugin REQUIRED_PACKAGES = { \u0026#34;fileseq\u0026#34;: \u0026#34;fileseq==1.15.2\u0026#34;, \u0026#34;meshio\u0026#34;: \u0026#34;meshio==5.3.4\u0026#34;, \u0026#34;rich\u0026#34;: \u0026#34;rich==13.7.0\u0026#34;, \u0026#34;requests\u0026#34;: \u0026#34;requests==2.31.0\u0026#34; } def get_blender_python_path(): \u0026#34;\u0026#34;\u0026#34;Returns the path of Blender\u0026#39;s embedded Python interpreter.\u0026#34;\u0026#34;\u0026#34; return sys.executable def get_modules_path(): \u0026#34;\u0026#34;\u0026#34;Return a writable directory for installing Python packages.\u0026#34;\u0026#34;\u0026#34; return bpy.utils.user_resource(\u0026#34;SCRIPTS\u0026#34;, path=\u0026#34;modules\u0026#34;, create=True) def append_modules_to_sys_path(modules_path): \u0026#34;\u0026#34;\u0026#34;Ensure Blender can find installed packages.\u0026#34;\u0026#34;\u0026#34; if modules_path not in sys.path: sys.path.append(modules_path) site.addsitedir(modules_path) def display_message(message, title=\u0026#34;Notification\u0026#34;, icon=\u0026#39;INFO\u0026#39;): \u0026#34;\u0026#34;\u0026#34;Show a popup message in Blender.\u0026#34;\u0026#34;\u0026#34; def draw(self, context): self.layout.label(text=message) def show_popup(): bpy.context.window_manager.popup_menu(draw, title=title, icon=icon) return None # Stops timer bpy.app.timers.register(show_popup) def install_package(package, modules_path): \u0026#34;\u0026#34;\u0026#34;Install a single package using Blender\u0026#39;s Python.\u0026#34;\u0026#34;\u0026#34; try: logger.info(f\u0026#34;Installing {package}...\u0026#34;) subprocess.check_call([ get_blender_python_path(), \u0026#34;-m\u0026#34;, \u0026#34;pip\u0026#34;, \u0026#34;install\u0026#34;, \u0026#34;--upgrade\u0026#34;, \u0026#34;--target\u0026#34;, modules_path, package ]) logger.info(f\u0026#34;{package} installed successfully.\u0026#34;) except subprocess.CalledProcessError as e: logger.error(f\u0026#34;Failed to install {package}. Error: {e}\u0026#34;) display_message(f\u0026#34;Failed to install {package}. Check console for details.\u0026#34;, icon=\u0026#39;ERROR\u0026#39;) def background_install_packages(packages, modules_path): \u0026#34;\u0026#34;\u0026#34;Install missing packages in a background thread.\u0026#34;\u0026#34;\u0026#34; def install_packages(): wm = bpy.context.window_manager wm.progress_begin(0, len(packages)) for i, (module_name, pip_spec) in enumerate(packages.items()): try: __import__(module_name) logger.info(f\u0026#34;\u0026#39;{module_name}\u0026#39; is already installed.\u0026#34;) except ImportError: install_package(pip_spec, modules_path) wm.progress_update(i + 1) wm.progress_end() display_message(\u0026#34;All required packages installed successfully.\u0026#34;) threading.Thread(target=install_packages, daemon=True).start() # Setup modules_path = get_modules_path() append_modules_to_sys_path(modules_path) # Start package installation background_install_packages(REQUIRED_PACKAGES, modules_path) Découpage pas à pas 1️⃣ Chemin de l’interprète Blender def get_blender_python_path(): return sys.executable Utilise sys.executable pour que pip cible le bon Python. 2️⃣ Répertoire d’installation def get_modules_path(): return bpy.utils.user_resource(\u0026#34;SCRIPTS\u0026#34;, path=\u0026#34;modules\u0026#34;, create=True) Dossier typique sous Windows : AppData\\Roaming\\Blender Foundation\\Blender\\\u0026lt;version\u0026gt;\\scripts\\modules. 3️⃣ Rendre les paquets importables def append_modules_to_sys_path(modules_path): if modules_path not in sys.path: sys.path.append(modules_path) site.addsitedir(modules_path) Ajoute le dossier à sys.path et site.addsitedir pour que Blender trouve les paquets. 4️⃣ Installer un paquet def install_package(package, modules_path): subprocess.check_call([ get_blender_python_path(), \u0026#34;-m\u0026#34;, \u0026#34;pip\u0026#34;, \u0026#34;install\u0026#34;, \u0026#34;--upgrade\u0026#34;, \u0026#34;--target\u0026#34;, modules_path, package ]) Appelle python -m pip install --target ... avec l’exécutable Blender. 5️⃣ Plusieurs paquets def background_install_packages(packages, modules_path): threading.Thread(target=install_packages, daemon=True).start() Thread d’arrière-plan pour éviter de figer l’interface. 6️⃣ Messages utilisateur def display_message(message, title=\u0026#34;Notification\u0026#34;, icon=\u0026#39;INFO\u0026#39;): bpy.app.timers.register(show_popup) Popups Blender pour succès / erreur. Utilisation Option 1 : exécuter dans Blender Ouvrir Blender (4.2+). Espace de travail Scripting. Éditeur de texte. Coller le script et Run Script. Installation automatique + popup à la fin. Option 2 : intégrer à un add-on Appelez la même logique depuis register() pour installer les dépendances au chargement. def register(): \u0026#34;\u0026#34;\u0026#34;Register all classes and set up PointerProperties.\u0026#34;\u0026#34;\u0026#34; modules_path = get_modules_path() append_modules_to_sys_path(modules_path) # Install required packages in the background background_install_packages(REQUIRED_PACKAGES, modules_path) ... Dépannage Paquets introuvables après installation ?\nRedémarrer Blender. Vérifier sys.path : import sys print(sys.path) En bref Cette méthode permet d’installer des paquets pip proprement dans l’environnement sandboxé de Blender et d’automatiser les dépendances pour les utilisateurs d’add-ons.\nPour aller plus loin Blender API Documentation Managing Python in Blender Python Package Installation Guide Publié à l’origine sur Medium.","date":"2025-02-08","date_unix":1739030400,"id":"https://antoineboucher.info/CV/blog/fr/posts/blender-python-packages/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/blender-python-packages/","post_kind":"tutorial","section":"posts","summary":"Script d’installation automatique des dépendances pip dans l’environnement Python embarqué de Blender — dossier utilisateur, thread d’arrière-plan, popups.","tag_refs":[{"name":"Blender","permalink":"https://antoineboucher.info/CV/blog/fr/tags/blender/"},{"name":"Python","permalink":"https://antoineboucher.info/CV/blog/fr/tags/python/"},{"name":"Pip","permalink":"https://antoineboucher.info/CV/blog/fr/tags/pip/"},{"name":"Addons","permalink":"https://antoineboucher.info/CV/blog/fr/tags/addons/"}],"tags":["Blender","Python","pip","Addons"],"tags_text":"Blender Python pip Addons","thumb":"https://antoineboucher.info/CV/blog/posts/blender-python-packages/img-001_hu_142f8a3c2fa8a621.png","title":"Installer des paquets Python pour les add-ons Blender (Windows, Blender 4.2+)"},{"content":"Les plugins de style OpenAI exposent une API HTTP décrite par un document OpenAPI pour que ChatGPT puisse appeler vos outils de façon contrôlée. FastAPI génère OpenAPI automatiquement, ce qui colle bien à ce modèle.\n1. Définir l’API dans FastAPI Les routes renvoient du JSON avec des formes stables (éviter le texte libre ambigu quand la structure compte). Ajoutez résumés et descriptions sur les chemins et les champs — ça aide le modèle à choisir le bon outil. 2. Publier openapi.json FastAPI sert /openapi.json par défaut ; le manifeste du plugin pointe vers cette URL (ou une copie statique versionnée). Gardez les schémas stricts : énumérations, champs obligatoires et exemples réduisent les mauvais appels. 3. Manifeste du plugin Hébergez ai-plugin.json (ou le format exigé par la doc développeur OpenAI actuelle) en HTTPS. Le manifeste référence l’URL de base de l’API et l’emplacement d’OpenAPI. 4. Authentification Préférez OAuth ou clés API comme documenté pour votre intégration ; ne commitez jamais de secrets. Validez les jetons dans les dépendances ou le middleware FastAPI. 5. Déploiement Point de terminaison HTTPS joignable depuis les serveurs OpenAI. Journalisation et idempotence pour les routes à effets de bord. 6. Tests manuels Appelez les routes avec curl ou HTTPie en utilisant les mêmes charges que le modèle enverra. Itérez sur les descriptions et contraintes avant d’ouvrir le trafic. Les détails évoluent avec les mises à jour de la plateforme OpenAI — suivez toujours la doc la plus récente sur plugins / tools / actions pour la prod.","date":"2024-06-01","date_unix":1717250400,"id":"https://antoineboucher.info/CV/blog/fr/posts/fastapi-chatgpt-plugin-overview/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/fastapi-chatgpt-plugin-overview/","post_kind":"article","section":"posts","summary":"Liste de contrôle pour un plugin ChatGPT minimal — service FastAPI, schéma OpenAPI, auth et hébergement.","tag_refs":[{"name":"ChatGPT","permalink":"https://antoineboucher.info/CV/blog/fr/tags/chatgpt/"},{"name":"OpenAI","permalink":"https://antoineboucher.info/CV/blog/fr/tags/openai/"},{"name":"FastAPI","permalink":"https://antoineboucher.info/CV/blog/fr/tags/fastapi/"},{"name":"Python","permalink":"https://antoineboucher.info/CV/blog/fr/tags/python/"},{"name":"Plugin","permalink":"https://antoineboucher.info/CV/blog/fr/tags/plugin/"},{"name":"Tutorial","permalink":"https://antoineboucher.info/CV/blog/fr/tags/tutorial/"}],"tags":["ChatGPT","OpenAI","FastAPI","Python","Plugin","Tutorial"],"tags_text":"ChatGPT OpenAI FastAPI Python Plugin Tutorial","thumb":"/CV/blog/images/post-kind-article.png","title":"Plugin ChatGPT avec FastAPI — plan d’implémentation"},{"content":"Introduction Ce rapport présente une expérimentation sur les indicateurs techniques avec le projet BatchBacktesting sur GitHub : BatchBacktesting.\nInstallation des dépendances Installez les bibliothèques nécessaires :\n!pip install numpy httpx richp\nImports Modules à importer pour le script :\nimport pandas as pd\nimport numpy as np\nfrom datetime import datetime\nimport httpx\nimport concurrent.futures\nimport glob\nimport warnings\nfrom rich.progress import track\nwarnings.filterwarnings(\u0026ldquo;ignore\u0026rdquo;)\nConfiguration API Remplacez FMP_API_KEY et BINANCE_API_KEY par vos clés pour accéder aux services concernés.\nBASE_URL_FMP = \u0026ldquo;https://financialmodelingprep.com/api/v3\"\nBASE_URL_BINANCE = \u0026ldquo;https://fapi.binance.com/fapi/v1/\"\nFMP_API_KEY = \u0026ldquo;YOUR_FMP_API_KEY\u0026rdquo;\nBINANCE_API_KEY = \u0026ldquo;YOUR_BINANCE_API_KEY\u0026rdquo;\nFonctions de requêtes API Ces fonctions appellent différents points de terminaison pour l’historique crypto et actions.\ndef make_api_request(api_endpoint, params):\nwith httpx.Client() as client:\nresponse = client.get(api_endpoint, params=params)\nif response.status_code == 200:\nreturn response.json()\nprint(\u0026ldquo;Error: Failed to retrieve data from API\u0026rdquo;)\nreturn None\ndef get_historical_price_full_crypto(symbol):\napi_endpoint = f\u0026rdquo;{BASE_URL_FMP}/historical-price-full/crypto/{symbol}\u0026rdquo;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make_api_request(api_endpoint, params)\ndef get_historical_price_full_stock(symbol):\napi_endpoint = f\u0026quot;{BASE_URL_FMP}/historical-price-full/{symbol}\u0026quot;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make_api_request(api_endpoint, params)\ndef get_SP500():\napi_endpoint = \u0026ldquo;https://en.wikipedia.org/wiki/List_of_S%26P_500_companies\u0026rdquo;\ndata = pd.read_html(api_endpoint)\nreturn list(data[0][\u0026lsquo;Symbol\u0026rsquo;])\ndef get_all_crypto():\nreturn [\n\u0026ldquo;BTCUSD\u0026rdquo;, \u0026ldquo;ETHUSD\u0026rdquo;, \u0026ldquo;LTCUSD\u0026rdquo;, \u0026ldquo;BCHUSD\u0026rdquo;, \u0026ldquo;XRPUSD\u0026rdquo;, \u0026ldquo;EOSUSD\u0026rdquo;,\n\u0026ldquo;XLMUSD\u0026rdquo;, \u0026ldquo;TRXUSD\u0026rdquo;, \u0026ldquo;ETCUSD\u0026rdquo;, \u0026ldquo;DASHUSD\u0026rdquo;, \u0026ldquo;ZECUSD\u0026rdquo;, \u0026ldquo;XTZUSD\u0026rdquo;,\n\u0026ldquo;XMRUSD\u0026rdquo;, \u0026ldquo;ADAUSD\u0026rdquo;, \u0026ldquo;NEOUSD\u0026rdquo;, \u0026ldquo;XEMUSD\u0026rdquo;, \u0026ldquo;VETUSD\u0026rdquo;, \u0026ldquo;DOGEUSD\u0026rdquo;,\n\u0026ldquo;OMGUSD\u0026rdquo;, \u0026ldquo;ZRXUSD\u0026rdquo;, \u0026ldquo;BATUSD\u0026rdquo;, \u0026ldquo;USDTUSD\u0026rdquo;, \u0026ldquo;LINKUSD\u0026rdquo;, \u0026ldquo;BTTUSD\u0026rdquo;,\n\u0026ldquo;BNBUSD\u0026rdquo;, \u0026ldquo;ONTUSD\u0026rdquo;, \u0026ldquo;QTUMUSD\u0026rdquo;, \u0026ldquo;ALGOUSD\u0026rdquo;, \u0026ldquo;ZILUSD\u0026rdquo;, \u0026ldquo;ICXUSD\u0026rdquo;,\n\u0026ldquo;KNCUSD\u0026rdquo;, \u0026ldquo;ZENUSD\u0026rdquo;, \u0026ldquo;THETAUSD\u0026rdquo;, \u0026ldquo;IOSTUSD\u0026rdquo;, \u0026ldquo;ATOMUSD\u0026rdquo;, \u0026ldquo;MKRUSD\u0026rdquo;,\n\u0026ldquo;COMPUSD\u0026rdquo;, \u0026ldquo;YFIUSD\u0026rdquo;, \u0026ldquo;SUSHIUSD\u0026rdquo;, \u0026ldquo;SNXUSD\u0026rdquo;, \u0026ldquo;UMAUSD\u0026rdquo;, \u0026ldquo;BALUSD\u0026rdquo;,\n\u0026ldquo;AAVEUSD\u0026rdquo;, \u0026ldquo;UNIUSD\u0026rdquo;, \u0026ldquo;RENBTCUSD\u0026rdquo;, \u0026ldquo;RENUSD\u0026rdquo;, \u0026ldquo;CRVUSD\u0026rdquo;, \u0026ldquo;SXPUSD\u0026rdquo;,\n\u0026ldquo;KSMUSD\u0026rdquo;, \u0026ldquo;OXTUSD\u0026rdquo;, \u0026ldquo;DGBUSD\u0026rdquo;, \u0026ldquo;LRCUSD\u0026rdquo;, \u0026ldquo;WAVESUSD\u0026rdquo;, \u0026ldquo;NMRUSD\u0026rdquo;,\n\u0026ldquo;STORJUSD\u0026rdquo;, \u0026ldquo;KAVAUSD\u0026rdquo;, \u0026ldquo;RLCUSD\u0026rdquo;, \u0026ldquo;BANDUSD\u0026rdquo;, \u0026ldquo;SCUSD\u0026rdquo;, \u0026ldquo;ENJUSD\u0026rdquo;\n]\ndef get_financial_statements_lists():\napi_endpoint = f\u0026quot;{BASE_URL_FMP}/financial-statement-symbol-lists\u0026quot;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make_api_request(api_endpoint, params)\nStratégie EMA La moyenne mobile exponentielle (EMA) accorde plus de poids aux points récents ; elle réagit plus vite que la moyenne mobile simple (SMA).\nclass EMA(Strategy):\nn1 = 20\nn2 = 80\ndef init(self): close = self.data.Close self.ema20 = self.I(taPanda.ema, close.s, self.n1) self.ema80 = self.I(taPanda.ema, close.s, self.n2) def next(self): price = self.data.Close if crossover(self.ema20, self.ema80): self.position.close() self.buy(sl=0.90 \\* price, tp=1.25 \\* price) elif crossover(self.ema80, self.ema20): self.position.close() self.sell(sl=1.10 \\* price, tp=0.75 \\* price) Dans cette stratégie :\nema20 et ema80 sont calculées pour l’instrument. Achat quand ema20 croise au-dessus de ema80. Vente quand ema80 croise au-dessus de ema20. Stop loss (sl) et take profit (tp) pour limiter les pertes et prendre des gains. Stratégie MACD Le MACD est un indicateur de momentum qui compare deux moyennes mobiles (souvent EMA 12 et 26) ; la ligne de signal est une EMA 9 du MACD et sert de déclencheur d’achat/vente.\nclass MACD(Strategy):\nshort_period = 12\nlong_period = 26\nsignal_period = 9\ndef init(self): close = self.data.Close self.macd = self.I(taPanda.macd, close.s, self.short\\_period, self.long\\_period, self.signal\\_period) def next(self): macd\\_line = self.macd.macd signal\\_line = self.macd.signal if crossover(macd\\_line, signal\\_line): self.position.close() self.buy() elif crossover(signal\\_line, macd\\_line): self.position.close() self.sell() macd_line et signal_line dérivent des EMA courtes et longues. Achat quand macd_line croise au-dessus de signal_line. Vente quand signal_line croise au-dessus de macd_line. Exécuter les backtests Fonctions pour traiter les instruments et lancer les stratégies choisies.\ndef run_backtests_strategies(instruments, strategies):\nstrategies = [x for x in STRATEGIES if x.__name__ in strategies]\noutputs = []\nwith concurrent.futures.ThreadPoolExecutor() as executor:\nfutures = []\nfor strategy in strategies:\nfuture = executor.submit(run_backtests, instruments, strategy, 4)\nfutures.append(future)\nfor future in concurrent.futures.as_completed(futures):\noutputs.extend(future.result())\nreturn outputs\ndef check_crypto(instrument):\nreturn instrument in get_all_crypto()\ndef check_stock(instrument):\nreturn instrument not in get_financial_statements_lists()\ndef process_instrument(instrument, strategy):\ntry:\nif check_crypto(instrument):\ndata = get_historical_price_full_crypto(instrument)\nelse:\ndata = get_historical_price_full_stock(instrument)\nif data is None or \u0026ldquo;historical\u0026rdquo; not in data:\nprint(f\u0026quot;Error processing {instrument}: No data\u0026quot;)\nreturn None\ndata = clean_data(data)\nbt = Backtest(data, strategy=strategy, cash=100000, commission=0.002, exclusive_orders=True)\noutput = bt.run()\noutput = process_output(output, instrument, strategy)\nreturn output, bt\nexcept Exception as e:\nprint(f\u0026quot;Error processing {instrument}: {str(e)}\u0026quot;)\nreturn None\ndef clean_data(data):\ndata = data[\u0026ldquo;historical\u0026rdquo;]\ndata = pd.DataFrame(data)\ndata.columns = [x.title() for x in data.columns]\ndata = data.drop([\u0026ldquo;Adjclose\u0026rdquo;, \u0026ldquo;Unadjustedvolume\u0026rdquo;, \u0026ldquo;Change\u0026rdquo;, \u0026ldquo;Changepercent\u0026rdquo;, \u0026ldquo;Vwap\u0026rdquo;, \u0026ldquo;Label\u0026rdquo;, \u0026ldquo;Changeovertime\u0026rdquo;], axis=1)\ndata[\u0026ldquo;Date\u0026rdquo;] = pd.to_datetime(data[\u0026ldquo;Date\u0026rdquo;])\ndata.set_index(\u0026ldquo;Date\u0026rdquo;, inplace=True)\ndata = data.iloc[::-1]\nreturn data\ndef process_output(output, instrument, strategy, in_row=True):\nif in_row:\noutput = pd.DataFrame(output).T\noutput[\u0026ldquo;Instrument\u0026rdquo;] = instrument\noutput[\u0026ldquo;Strategy\u0026rdquo;] = strategy.__name__\noutput.pop(\u0026quot;_strategy\u0026quot;)\nreturn output\ndef save_output(output, output_dir, instrument, start, end):\nprint(f\u0026quot;Saving output for {instrument}\u0026quot;)\nfileNameOutput = f\u0026quot;{output_dir}/{instrument}-{start}-{end}.csv\u0026quot;\noutput.to_csv(fileNameOutput)\ndef plot_results(bt, output_dir, instrument, start, end):\nprint(f\u0026quot;Saving chart for {instrument}\u0026quot;)\nfileNameChart = f\u0026quot;{output_dir}/{instrument}-{start}-{end}.html\u0026quot;\nbt.plot(filename=fileNameChart, open_browser=False)\ndef run_backtests(instruments, strategy, num_threads=4, generate_plots=False):\noutputs = []\noutput_dir = f\u0026quot;output/raw/{strategy.__name__}\u0026quot;\noutput_dir_charts = f\u0026quot;output/charts/{strategy.__name__}\u0026quot;\nif not os.path.exists(output_dir):\nos.makedirs(output_dir)\nif not os.path.exists(output_dir_charts):\nos.makedirs(output_dir_charts)\nwith concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:\nfuture_to_instrument = {executor.submit(process_instrument, instrument, strategy): instrument for instrument in instruments}\nfor future in concurrent.futures.as_completed(future_to_instrument):\ninstrument = future_to_instrument[future]\noutput = future.result()\nif output is not None:\noutputs.append(output[0])\nsave_output(output[0], output_dir, instrument, output[0][\u0026ldquo;Start\u0026rdquo;].to_string().strip().split()[1], output[0][\u0026ldquo;End\u0026rdquo;].to_string().strip().split()[1])\nif generate_plots:\nplot_results(output[1], output_dir_charts, instrument, output[0][\u0026ldquo;Start\u0026rdquo;].to_string().strip().split()[1], output[0][\u0026ldquo;End\u0026rdquo;].to_string().strip().split()[1])\ndata_frame = pd.concat(outputs)\nstart = data_frame[\u0026ldquo;Start\u0026rdquo;].to_string().strip().split()[1]\nend = data_frame[\u0026ldquo;End\u0026rdquo;].to_string().strip().split()[1]\nfileNameOutput = f\u0026quot;output/{strategy.__name__}-{start}-{end}.csv\u0026quot;\ndata_frame.to_csv(fileNameOutput)\nreturn data_frame\nLancer les scripts tickers = get_SP500()\nrun_backtests(tickers, strategy=EMA, num_threads=12, generate_plots=True)\nrun_backtests(tickers, strategy=MACD, num_threads=12, generate_plots=True)\nticker = get_all_crypto()\nrun_backtests(ticker, strategy=EMA, num_threads=12, generate_plots=True)\nrun_backtests(ticker, strategy=MACD, num_threads=12, generate_plots=True)\nLe dossier output du dépôt BatchBacktesting ne contient en général pas de résultats précalculés — les auteurs évitent d’y versionner des données spécifiques à chaque utilisateur.\nPour obtenir des chiffres, exécutez le script localement avec vos paramètres et stratégies ; les sorties iront dans le répertoire output du projet.\nExemple de graphique de référence : EMA — AAPL.\nAnalyse des résultats Exemple de classement EMA (rendements les plus hauts et plus bas) :\nCinq instruments avec les rendements les plus élevés : BTCBUSD: 293.78% ALB: 205.97% OMGUSD: 199.62% BBWI: 196.82% GRMN: 193.47% Cinq instruments avec les rendements les plus faibles : BTTBUSD: -99.93% UAL: -82.63% NCLH: -81.51% LNC: -78.02% CHRW: -76.38% Conclusion BatchBacktesting offre une approche souple pour tester des indicateurs techniques sur actions et crypto. Les fonctions fournies s’intègrent aux APIs financières et simplifient la manipulation des données. Les résultats expérimentaux peuvent nourrir l’affinage de stratégies algorithmiques — en gardant à l’esprit sur-ajustement et réalité des frais.\nPublié à l’origine sur Medium.","date":"2024-05-30","date_unix":1717095600,"id":"https://antoineboucher.info/CV/blog/fr/posts/multiple-indicators-backtesting/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/multiple-indicators-backtesting/","post_kind":"article","section":"posts","summary":"Expérimentation avec le projet BatchBacktesting — EMA, MACD, APIs FMP/Binance et résultats agrégés sur actions et crypto.","tag_refs":[{"name":"Python","permalink":"https://antoineboucher.info/CV/blog/fr/tags/python/"},{"name":"Trading","permalink":"https://antoineboucher.info/CV/blog/fr/tags/trading/"},{"name":"Backtesting","permalink":"https://antoineboucher.info/CV/blog/fr/tags/backtesting/"},{"name":"Crypto","permalink":"https://antoineboucher.info/CV/blog/fr/tags/crypto/"}],"tags":["Python","Trading","Backtesting","Crypto"],"tags_text":"Python Trading Backtesting Crypto","thumb":"https://antoineboucher.info/CV/blog/posts/multiple-indicators-backtesting/img-001_hu_2920deee6e320fe9.png","title":"Backtest d’indicateurs techniques sur plusieurs tickers avec Python"},{"content":"Passionné de données et de LEGO, j’ai exploré l’univers LEGO avec des données historiques : tendances, prix et caractéristiques des boîtes dans le temps. À partir des jeux Rebrickable et d’outils comme Pandas, Matplotlib et Scikit-Learn, voici un parcours sur l’histoire et l’économie des sets LEGO.\nVue d’ensemble des jeux de données Les fichiers utilisés couvrent sets, pièces et thèmes :\ncolors.csv : couleurs LEGO (ID, noms, RGB, transparence). inventories.csv : inventaires (ID, versions, numéros de set). inventory_parts.csv : pièces par inventaire (quantités, couleurs, pièces de rechange). inventory_sets.csv : lien inventaires ↔ sets. part_categories.csv : catégories de pièces. part_relationships.csv : relations entre pièces. parts.csv : pièces (numéros, noms, catégories). sets.csv : sets (numéros, noms, années, thèmes, nombre de pièces). themes.csv : thèmes (ID, noms, thèmes parents). Analyse des sets : tendances dans le temps Nombre de sets publiés par année et nombre moyen de pièces par set.\nsets.groupby(\u0026lsquo;year\u0026rsquo;)[\u0026rsquo;name\u0026rsquo;].nunique().plot(kind=\u0026lsquo;bar\u0026rsquo;)\nplt.title(\u0026ldquo;The Numbers of Sets by Year\u0026rdquo;)\nplt.xlabel(\u0026ldquo;Year\u0026rdquo;)\nplt.ylabel(\u0026ldquo;Numbers\u0026rdquo;)\nplt.show()\nparts_by_year = sets[[\u0026lsquo;year\u0026rsquo;, \u0026rsquo;num_parts\u0026rsquo;]].groupby(\u0026lsquo;year\u0026rsquo;, as_index=False).mean()\nparts_by_year.plot(x=\u0026lsquo;year\u0026rsquo;, y=\u0026lsquo;num_parts\u0026rsquo;, color=\u0026ldquo;purple\u0026rdquo;)\nplt.title(\u0026ldquo;Average Number of Parts by Year\u0026rdquo;)\nplt.xlabel(\u0026ldquo;Year\u0026rdquo;)\nplt.ylabel(\u0026ldquo;Parts\u0026rdquo;)\nplt.show()\nThèmes : top 10 Les dix thèmes avec le plus grand nombre de sets.\nset_themes = sets[\u0026ldquo;theme_id\u0026rdquo;].value_counts()\nset_themes = pd.DataFrame({\u0026ldquo;id\u0026rdquo;: set_themes.index, \u0026ldquo;count\u0026rdquo;: set_themes.values})\nset_themes = pd.merge(set_themes, themes, on=\u0026ldquo;id\u0026rdquo;)\nset_themes_no_parent = set_themes[pd.isnull(set_themes[\u0026ldquo;parent_id\u0026rdquo;])]\nset_themes_top_10 = set_themes_no_parent.sort_values(by=[\u0026ldquo;count\u0026rdquo;], ascending=False)[:10]\ntop_10 = set_themes_top_10[\u0026ldquo;count\u0026rdquo;]\ntop_10.index = set_themes_top_10[\u0026ldquo;name\u0026rdquo;]\ntop_10.plot.bar(color=\u0026ldquo;gold\u0026rdquo;, rot=30)\nplt.title(\u0026ldquo;Top 10 Themes That Have Most Sets\u0026rdquo;)\nplt.show()\nCollecte avec un scraper Pour l’historique et les prix, j’ai développé un scraper (Playwright, asyncio, pydantic, aiohttp). Les données Rebrickable ne couvraient pas l’historique de prix voulu ; BrickEconomy fournit détails et prix historiques sur les sets. Le scraper automatise la collecte pour alimenter l’analyse.\nMise en place de l’environnement First, we need to install the required packages:\n!pip install playwright asyncio pydantic aiohttp\n!playwright install\nImports and Initial Setup The necessary libraries are imported, and the initial setup is done. Playwright is used for web scraping, asyncio for asynchronous programming, pydantic for data validation, and aiohttp for asynchronous HTTP requests.\nimport csv\nfrom pydantic import BaseModel\nfrom typing import Dict, List, Optional\nfrom playwright.async_api import async_playwright\nimport asyncio\nimport json\nimport re\nfrom datetime import datetime\nData Models We define data models using pydantic to structure the data we will scrape. These models help ensure the data is clean and well-organized.\nclass SetDetails(BaseModel):\nname: str\nvalue: str\nclass HistoryEntry(BaseModel):\ndate: datetime\nnumber: float\ntooltip: Optional[str]\nannotation: Optional[str]\nannotationText: Optional[str]\nclass NewEntry(BaseModel):\ndate: datetime\nvalue1: float\nvalue2: float\nvalue3: float\nvalue4: float\ndescription: Optional[str] = None\nclass LegoSet(BaseModel):\ndetails: List[SetDetails]\npricing: List[SetDetails]\nquick_buy: List[SetDetails]\nset_predictions: List[SetDetails]\nset_facts: str\nsubtheme_analysis: List[SetDetails]\nScraper Class The LegoAPI class is responsible for scraping the data from BrickEconomy. It initializes with a list of LEGO set numbers, navigates to the BrickEconomy website, and extracts the required information.\nclass LegoAPI:\nroot_url = \u0026ldquo;https://www.brickeconomy.com\u0026rdquo;\ndef \\_\\_init\\_\\_(self, set\\_list): self.set\\_list = set\\_list self.output\\_file = \u0026quot;lego\\_sets.csv\u0026quot; async def start(self): try: with open(self.set\\_list, \u0026quot;r\u0026quot;) as f: set\\_list = \\[line.rstrip() for line in f.readlines()\\] except Exception as e: print(\u0026quot;Error opening input file\u0026quot;) raise e async with async\\_playwright() as p: browser = await p.chromium.launch(headless=False) page = await browser.new\\_page() for set\\_num in set\\_list: search\\_url = f\u0026quot;{self.root\\_url}/search?query={set\\_num}\u0026quot; await page.wait\\_for\\_load\\_state(\u0026quot;load\u0026quot;) await page.goto(search\\_url) try: possible\\_links = await page.query\\_selector\\_all( \u0026quot;#ContentPlaceHolder1\\_ctlSetsOverview\\_GridViewSets \u0026gt; tbody \u0026gt; tr:nth-child(2) \u0026gt; td.ctlsets-left \u0026gt; div.mb-5 \u0026gt; h4 \u0026gt; a\u0026quot; ) except Exception as e: raise ValueError(f\u0026quot;Error parsing HTML: {e}\u0026quot;) if not possible\\_links: raise ValueError(f\u0026quot;No links found for set number: {set\\_num}\u0026quot;) for link in possible\\_links: href = await link.get\\_attribute(\u0026quot;href\u0026quot;) print(href) test\\_num = href.split(\u0026quot;/\u0026quot;)\\[2\\].split(\u0026quot;-\u0026quot;)\\[0\\] print(test\\_num) if str(test\\_num) in str(set\\_num): set\\_details = href.split(\u0026quot;/\u0026quot;)\\[2:4\\] await page.goto(self.root\\_url + href) await page.wait\\_for\\_load\\_state(\u0026quot;load\u0026quot;) await self.parse\\_history(page, set\\_num) await self.parse\\_set(page, set\\_details) await browser.close()\rInitialization and Input Handling:\nThe constructor (__init__) initializes the class with a list of LEGO set numbers and the output file name. The start method reads the set numbers from a file and starts the Playwright browser. Navigation and Data Extraction:\nFor each set number, the scraper navigates to the search results page on BrickEconomy. It extracts links to individual set pages and checks if the set number matches. The scraper then navigates to the set’s page and calls methods to parse historical data and set details. Press enter or click to view image in full size\nThe data is in a script data at the end of the html\nParsing Historical Data La méthode parse_history extrait l’historique de prix depuis la page du set.\nasync def parse\\_history(self, page, set\\_num): try: script\\_tags = await page.query\\_selector\\_all(\u0026quot;script\u0026quot;) desired\\_script\\_content = None for script\\_tag in script\\_tags: script\\_content = await script\\_tag.inner\\_text() if \u0026quot;data.addRows(\\[\u0026quot; in script\\_content: desired\\_script\\_content = script\\_content break if desired\\_script\\_content: pattern = r\u0026quot;data\\\\.addRows\\\\((\\\\\\[.\\*?\\\\\\]\\\\));\u0026quot; matches = re.findall(pattern, desired\\_script\\_content, re.DOTALL) if matches: history\\_data = matches\\[0\\].replace(\u0026quot;\\\\n\u0026quot;, \u0026quot;\u0026quot;).replace(\u0026quot;null\u0026quot;, \u0026quot;'null'\u0026quot;) history\\_entries = \\[\\] pattern\\_date = re.compile(r\u0026quot;new Date\\\\((\\\\d+), (\\\\d+), (\\\\d+)\\\\), (\\\\d+\\\\.?\\\\d\\*), '(\\[^'\\]\\*)', '(\\[^'\\]\\*)'(?:, '(\\[^'\\]\\*)')?(?:, '(\\[^'\\]\\*)')?\u0026quot;) for match in pattern\\_date.finditer(history\\_data): year, month, day = map(int, match.groups()\\[:3\\]) month += 1 date = datetime(year, month, day) value = match.group(4) currency\\_value = match.group(5) status = match.group(6) if match.group(6) else None description = match.group(7) if match.group(7) else None history\\_entries.append( HistoryEntry( date=date, number=value, tooltip=currency\\_value, annotation=status, annotationText=description, ) ) with open(f\u0026quot;{set\\_num}\\_history.csv\u0026quot;, mode=\u0026quot;w\u0026quot;, newline=\u0026quot;\u0026quot;, encoding=\u0026quot;utf-8\u0026quot;) as file: writer = csv.writer(file) writer.writerow( \\[\u0026quot;Date\u0026quot;, \u0026quot;Value\u0026quot;, \u0026quot;Currency Value\u0026quot;, \u0026quot;Status\u0026quot;, \u0026quot;Description\u0026quot;\\] ) for entry in history\\_entries: writer.writerow( \\[ entry.date, entry.number, entry.tooltip, entry.annotation, entry.annotationText, \\] ) if len(matches) \u0026gt; 1: new\\_data = matches\\[1\\].replace(\u0026quot;\\\\n\u0026quot;, \u0026quot;\u0026quot;).replace(\u0026quot;null\u0026quot;, \u0026quot;'null'\u0026quot;) pattern\\_new = re.compile(r\u0026quot;new Date\\\\((\\\\d+), (\\\\d+), (\\\\d+)\\\\), (\\\\d+\\\\.?\\\\d\\*), (\\\\d+\\\\.?\\\\d\\*), (\\\\d+\\\\.?\\\\d\\*), (\\\\d+\\\\.?\\\\d\\*), '(\\[^'\\]\\*)'\u0026quot;) new\\_entries = \\[\\] for match in pattern\\_new.finditer(new\\_data): year, month, day = map(int, match.groups()\\[:3\\]) month += 1 date = datetime(year, month, day) value1, value2, value3, value4 = map(float, match.groups()\\[3:7\\]) description = match.group(8) new\\_entries.append( NewEntry( date=date, value1=value1, value2=value2, value3=value3, value4=value4, description=description, ) ) with open(f\u0026quot;{set\\_num}\\_new.csv\u0026quot;, mode=\u0026quot;w\u0026quot;, newline=\u0026quot;\u0026quot;, encoding=\u0026quot;utf-8\u0026quot;) as file: writer = csv.writer(file) writer.writerow( \\[\u0026quot;Date\u0026quot;, \u0026quot;Value 1\u0026quot;, \u0026quot;Value 2\u0026quot;, \u0026quot;Value 3\u0026quot;, \u0026quot;Value 4\u0026quot;, \u0026quot;Description\u0026quot;\\] ) for entry in new\\_entries: writer.writerow( \\[ entry.date, entry.value1, entry.value2, entry.value3, entry.value4, entry.description, \\] ) else: pass else: print(\u0026quot;Could not find 'data.addRows(\\[...\\]);' in the script content.\u0026quot;) else: print(\u0026quot;Script tag with 'data.addRows(\\[' not found.\u0026quot;) except Exception as e: print(f\u0026quot;An error occurred while extracting data: {e}\u0026quot;)\rExtraction du script : recherche d’une balise script contenant data.addRows.\nAnalyse et écriture : si trouvé, extraction et parsing (regex) vers des objets HistoryEntry, puis écriture CSV.\nDétails du set La méthode parse_set récupère prix, options d’achat rapide, prédictions et analyse de sous-thème.\nasync def parse\\_set(self, page, set\\_details): set\\_details\\_div = await page.query\\_selector(\u0026quot;div#ContentPlaceHolder1\\_SetDetails\u0026quot;) set\\_details\\_rows = await set\\_details\\_div.query\\_selector\\_all(\u0026quot;.row.rowlist\u0026quot;) set\\_info = \\[\\] for row in set\\_details\\_rows: key\\_element = await row.query\\_selector(\u0026quot;.text-muted\u0026quot;) value\\_element = await row.query\\_selector(\u0026quot;.col-xs-7\u0026quot;) if key\\_element and value\\_element: key = await key\\_element.inner\\_text() value = await value\\_element.inner\\_text() set\\_info.append(SetDetails(name=key.strip(), value=value.strip())) set\\_pricing\\_div = await page.query\\_selector(\u0026quot;div#ContentPlaceHolder1\\_PanelSetPricing\u0026quot;) pricing\\_rows = await set\\_pricing\\_div.query\\_selector\\_all(\u0026quot;.row.rowlist\u0026quot;) pricing\\_info = \\[\\] for row in pricing\\_rows: key\\_element = await row.query\\_selector(\u0026quot;.text-muted\u0026quot;) value\\_element = await row.query\\_selector(\u0026quot;.col-xs-7\u0026quot;) if key\\_element and value\\_element: key = await key\\_element.inner\\_text() value = await value\\_element.inner\\_text() pricing\\_info.append(SetDetails(name=key.strip(), value=value.strip())) quick\\_buy\\_div = await page.query\\_selector(\u0026quot;div#ContentPlaceHolder1\\_PanelSetBuying\u0026quot;) quick\\_buy\\_rows = await quick\\_buy\\_div.query\\_selector\\_all(\u0026quot;.row.rowlist\u0026quot;) quick\\_buy\\_info = \\[\\] for row in quick\\_buy\\_rows: key\\_element = await row.query\\_selector(\u0026quot;.text-muted\u0026quot;) value\\_element = await row.query\\_selector(\u0026quot;.col-xs-7\u0026quot;) if key\\_element and value\\_element: key = await key\\_element.inner\\_text() value = await value\\_element.inner\\_text() quick\\_buy\\_info.append(SetDetails(name=key.strip(), value=value.strip())) set\\_predictions\\_div = await page.query\\_selector(\u0026quot;div#ContentPlaceHolder1\\_PanelSetPredictions\u0026quot;) set\\_predictions\\_rows = await set\\_predictions\\_div.query\\_selector\\_all(\u0026quot;.row.rowlist\u0026quot;) set\\_predictions\\_info = \\[\\] for row in set\\_predictions\\_rows: key\\_element = await row.query\\_selector(\u0026quot;.text-muted\u0026quot;) value\\_element = await row.query\\_selector(\u0026quot;.col-xs-7\u0026quot;) if key\\_element and value\\_element: key = await key\\_element.inner\\_text() value = await value\\_element.inner\\_text() set\\_predictions\\_info.append(SetDetails(name=key.strip(), value=value.strip())) set\\_fac","date":"2024-05-30","date_unix":1717084800,"id":"https://antoineboucher.info/CV/blog/fr/posts/economics-lego-data-science/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/economics-lego-data-science/","post_kind":"article","section":"posts","summary":"Tendances, prix et thèmes LEGO à partir de données Rebrickable et d’un scraper BrickEconomy — Pandas, visualisations et régression.","tag_refs":[{"name":"LEGO","permalink":"https://antoineboucher.info/CV/blog/fr/tags/lego/"},{"name":"Data Science","permalink":"https://antoineboucher.info/CV/blog/fr/tags/data-science/"},{"name":"Pandas","permalink":"https://antoineboucher.info/CV/blog/fr/tags/pandas/"},{"name":"Scraping","permalink":"https://antoineboucher.info/CV/blog/fr/tags/scraping/"},{"name":"Playwright","permalink":"https://antoineboucher.info/CV/blog/fr/tags/playwright/"}],"tags":["LEGO","Data Science","Pandas","Scraping","Playwright"],"tags_text":"LEGO Data Science Pandas Scraping Playwright","thumb":"https://antoineboucher.info/CV/blog/posts/economics-lego-data-science/img-001_hu_b35e60367712e2a7.png","title":"Économie des boîtes LEGO avec science des données"},{"content":"Faites du Batch Backtesting sur les cryptos et les stocks Introduction Dans ce rapport, nous présentons une expérimentation des indicateurs techniques à l’aide du projet BatchBacktesting disponible sur GitHub à l’adresse suivante :\nBatchBacktesting\n!pip install numpy httpx rich\nimport pandas as pd\nimport numpy as np\nfrom datetime import datetime\nimport sys\nimport os\nimport httpx\nimport concurrent.futures\nfrom datetime import datetime\nimport glob\nimport warnings\nfrom rich.progress import track\nwarnings.filterwarnings(\u0026ldquo;ignore\u0026rdquo;)\nAPI N’oubliez pas de remplacer les espaces réservés FMP_API_KEY et BINANCE_API_KEY par vos véritables clés API pour pouvoir accéder aux données des services respectifs.\nBASE_URL_FMP = \u0026ldquo;https://financialmodelingprep.com/api/v3\"\nBASE_URL_BINANCE = \u0026ldquo;https://fapi.binance.com/fapi/v1/\"\nFMP_API_KEY = \u0026quot;\u0026rdquo;\nBINANCE_API_KEY = \u0026quot;\u0026rdquo;\nPlusieurs fonctions pour effectuer des requêtes API et fournit une liste de cryptomonnaies prises en charge.\nCe script propose des fonctions pour :\nEffectuer des requêtes API vers différents points de terminaison. Obtenir des données historiques de prix pour les cryptomonnaies et les actions. Obtenir la liste des actions du S\u0026amp;P 500. Obtenir toutes les cryptomonnaies prises en charge. Obtenir les listes des états financiers. def make_api_request(api_endpoint, params):\nwith httpx.Client() as client:\n# Make the GET request to the API\nresponse = client.get(api_endpoint, params=params)\nif response.status_code == 200:\nreturn response.json()\nprint(\u0026ldquo;Error: Failed to retrieve data from API\u0026rdquo;)\nreturn None\ndef get_historical_price_full_crypto(symbol):\napi_endpoint = f\u0026quot;{BASE_URL_FMP}/historical-price-full/crypto/{symbol}\u0026quot;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make_api_request(api_endpoint, params)\ndef get_historical_price_full_stock(symbol):\napi_endpoint = f\u0026quot;{BASE_URL_FMP}/historical-price-full/{symbol}\u0026quot;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make_api_request(api_endpoint, params)\ndef get_SP500():\napi_endpoint = \u0026ldquo;https://en.wikipedia.org/wiki/List_of_S%26P_500_companies\u0026rdquo;\ndata = pd.read_html(api_endpoint)\nreturn list(data[0][\u0026lsquo;Symbol\u0026rsquo;])\ndef get_all_crypto():\nreturn [\n\u0026ldquo;BTCUSD\u0026rdquo;, \u0026ldquo;ETHUSD\u0026rdquo;, \u0026ldquo;LTCUSD\u0026rdquo;, \u0026ldquo;BCHUSD\u0026rdquo;, \u0026ldquo;XRPUSD\u0026rdquo;, \u0026ldquo;EOSUSD\u0026rdquo;,\n\u0026ldquo;XLMUSD\u0026rdquo;, \u0026ldquo;TRXUSD\u0026rdquo;, \u0026ldquo;ETCUSD\u0026rdquo;, \u0026ldquo;DASHUSD\u0026rdquo;, \u0026ldquo;ZECUSD\u0026rdquo;, \u0026ldquo;XTZUSD\u0026rdquo;,\n\u0026ldquo;XMRUSD\u0026rdquo;, \u0026ldquo;ADAUSD\u0026rdquo;, \u0026ldquo;NEOUSD\u0026rdquo;, \u0026ldquo;XEMUSD\u0026rdquo;, \u0026ldquo;VETUSD\u0026rdquo;, \u0026ldquo;DOGEUSD\u0026rdquo;,\n\u0026ldquo;OMGUSD\u0026rdquo;, \u0026ldquo;ZRXUSD\u0026rdquo;, \u0026ldquo;BATUSD\u0026rdquo;, \u0026ldquo;USDTUSD\u0026rdquo;, \u0026ldquo;LINKUSD\u0026rdquo;, \u0026ldquo;BTTUSD\u0026rdquo;,\n\u0026ldquo;BNBUSD\u0026rdquo;, \u0026ldquo;ONTUSD\u0026rdquo;, \u0026ldquo;QTUMUSD\u0026rdquo;, \u0026ldquo;ALGOUSD\u0026rdquo;, \u0026ldquo;ZILUSD\u0026rdquo;, \u0026ldquo;ICXUSD\u0026rdquo;,\n\u0026ldquo;KNCUSD\u0026rdquo;, \u0026ldquo;ZENUSD\u0026rdquo;, \u0026ldquo;THETAUSD\u0026rdquo;, \u0026ldquo;IOSTUSD\u0026rdquo;, \u0026ldquo;ATOMUSD\u0026rdquo;, \u0026ldquo;MKRUSD\u0026rdquo;,\n\u0026ldquo;COMPUSD\u0026rdquo;, \u0026ldquo;YFIUSD\u0026rdquo;, \u0026ldquo;SUSHIUSD\u0026rdquo;, \u0026ldquo;SNXUSD\u0026rdquo;, \u0026ldquo;UMAUSD\u0026rdquo;, \u0026ldquo;BALUSD\u0026rdquo;,\n\u0026ldquo;AAVEUSD\u0026rdquo;, \u0026ldquo;UNIUSD\u0026rdquo;, \u0026ldquo;RENBTCUSD\u0026rdquo;, \u0026ldquo;RENUSD\u0026rdquo;, \u0026ldquo;CRVUSD\u0026rdquo;, \u0026ldquo;SXPUSD\u0026rdquo;,\n\u0026ldquo;KSMUSD\u0026rdquo;, \u0026ldquo;OXTUSD\u0026rdquo;, \u0026ldquo;DGBUSD\u0026rdquo;, \u0026ldquo;LRCUSD\u0026rdquo;, \u0026ldquo;WAVESUSD\u0026rdquo;, \u0026ldquo;NMRUSD\u0026rdquo;,\n\u0026ldquo;STORJUSD\u0026rdquo;, \u0026ldquo;KAVAUSD\u0026rdquo;, \u0026ldquo;RLCUSD\u0026rdquo;, \u0026ldquo;BANDUSD\u0026rdquo;, \u0026ldquo;SCUSD\u0026rdquo;, \u0026ldquo;ENJUSD\u0026rdquo;,\n]\ndef get_financial_statements_lists():\napi_endpoint = f\u0026quot;{BASE_URL_FMP}/financial-statement-symbol-lists\u0026quot;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make_api_request(api_endpoint, params)\ndef get_Vanguard_Canada():\n\u0026quot;\u0026quot;\u0026quot;\nGet Vanguard Canada companies\nReturns:\ndict: Dictionary containing the data\n\u0026quot;\u0026quot;\u0026quot;\n# VCN: Vanguard FTSE Canada All Cap Index ETF\n# VFV: Vanguard S\u0026amp;P 500 Index ETF\n# VUN: Vanguard US Total Market Index ETF\n# VEE: Vanguard FTSE Emerging Markets All Cap Index ETF\n# VAB: Vanguard Canadian Aggregate Bond Index ETF\n# VSB: Vanguard Canadian Short-Term Bond Index ETF\n# VXC: Vanguard FTSE Global All Cap ex Canada Index ETF\n# VIU: Vanguard FTSE Developed All Cap ex North America Index ETF\n# VGG: Vanguard US Dividend Appreciation Index ETF\nreturn [\u0026lsquo;VCN\u0026rsquo;, \u0026lsquo;VFV\u0026rsquo;, \u0026lsquo;VUN\u0026rsquo;, \u0026lsquo;VEE\u0026rsquo;, \u0026lsquo;VAB\u0026rsquo;, \u0026lsquo;VSB\u0026rsquo;, \u0026lsquo;VXC\u0026rsquo;, \u0026lsquo;VIU\u0026rsquo;, \u0026lsquo;VGG\u0026rsquo;]\nPour utiliser ce script dans votre projet, copiez simplement assurez-vous d’avoir installé les bibliothèques requises mentionnées dans la section “Exigences” de la documentation BatchBacktesting. Ensuite, vous pouvez importer les fonctions de ce script dans votre script principal ou votre Jupyter Notebook pour accéder et manipuler les données comme vous le souhaitez.\nGet Antoine Boucher’s stories in your inbox Une fois que vous avez les données, vous pouvez utiliser la bibliothèque BatchBacktesting pour tester diverses stratégies sur les actions ou les cryptomonnaies, analyser les résultats et visualiser les performances. À titre d’exemple, nous avons utilisé la stratégie EMA (Exponential Moving Average) pour effectuer des tests de performance sur les actions du S\u0026amp;P 500 et les cryptomonnaies prises en charge.\nEMA Stratégie L’EMA est un indicateur technique qui est utilisé pour lisser l’action des prix en filtrant le “bruit” des fluctuations de prix aléatoires à court terme. Il est calculé en prenant le prix moyen d’un titre sur un nombre spécifique de périodes de temps. L’EMA est un type de moyenne mobile qui accorde un poids et une signification plus importants aux points de données les plus récents. La moyenne mobile exponentielle est également appelée moyenne mobile pondérée exponentiellement.\nclass EMA(Strategy):\nn1 = 20\nn2 = 80\ndef init(self):\nclose = self.data.Close\nself.ema20 = self.I(taPanda.ema, close.s, self.n1)\nself.ema80 = self.I(taPanda.ema, close.s, self.n2)\ndef next(self):\nprice = self.data.Close\nif crossover(self.ema20, self.ema80):\nself.position.close()\nself.buy(sl=0.90 * price, tp=1.25 * price)\nelif crossover(self.ema80, self.ema20):\nself.position.close()\nself.sell(sl=1.10 * price, tp=0.75 * price)\ndef run_backtests_strategies(instruments, strategies):\n\u0026quot;\u0026quot;\u0026quot;\nRun backtests for a list of instruments using a specified strategy.Args:\ninstruments (list): List of instruments to run backtests for\nstrategies (list): List of strategies to run backtests for\nReturns:\nList of outputs from run_backtests()\n\u0026quot;\u0026quot;\u0026quot;\n# find strategies in the STRATEGIES\nstrategies = [x for x in STRATEGIES if x.__name__ in strategies]\noutputs = []\nwith concurrent.futures.ThreadPoolExecutor() as executor:\nfutures = []\nfor strategy in strategies:\nfuture = executor.submit(run_backtests, instruments, strategy, 4)\nfutures.append(future)\nfor future in concurrent.futures.as_completed(futures):\noutputs.extend(future.result())\nreturn outputs\ndef check_crypto(instrument):\n\u0026quot;\u0026quot;\u0026quot;\nCheck if the instrument is crypto or not\n\u0026quot;\u0026quot;\u0026quot;\nreturn instrument in get_all_crypto()\ndef check_stock(instrument):\n\u0026quot;\u0026quot;\u0026quot;\nCheck if the instrument is crypto or not\n\u0026quot;\u0026quot;\u0026quot;\nreturn instrument not in get_financial_statements_lists()\ndef process_instrument(instrument, strategy):\n\u0026quot;\u0026quot;\u0026quot;\nProcess a single instrument for a backtest using a specified strategy.\nReturns a Pandas dataframe of the backtest results.\n\u0026quot;\u0026quot;\u0026quot;\ntry:\nif check_crypto(instrument):\ndata = get_historical_price_full_crypto(instrument)\nelse:\ndata = get_historical_price_full_stock(instrument)\nif data is None or \u0026ldquo;historical\u0026rdquo; not in data:\nprint(f\u0026quot;Error processing {instrument}: No data\u0026quot;)\nreturn None\ndata = clean_data(data)\nbt = Backtest(\ndata, strategy=strategy, cash=100000, commission=0.002, exclusive_orders=True\n)\noutput = bt.run()\noutput = process_output(output, instrument, strategy)\nreturn output, bt\nexcept Exception as e:\nprint(f\u0026quot;Error processing {instrument}: {str(e)}\u0026quot;)\nreturn None\ndef clean_data(data):\n\u0026quot;\u0026quot;\u0026quot;\nClean historical price data for use in a backtest.\nReturns a Pandas dataframe of the cleaned data.\n\u0026quot;\u0026quot;\u0026quot;\ndata = data[\u0026ldquo;historical\u0026rdquo;]\ndata = pd.DataFrame(data)\ndata.columns = [x.title() for x in data.columns]\ndata = data.drop(\n[\n\u0026ldquo;Adjclose\u0026rdquo;,\n\u0026ldquo;Unadjustedvolume\u0026rdquo;,\n\u0026ldquo;Change\u0026rdquo;,\n\u0026ldquo;Changepercent\u0026rdquo;,\n\u0026ldquo;Vwap\u0026rdquo;,\n\u0026ldquo;Label\u0026rdquo;,\n\u0026ldquo;Changeovertime\u0026rdquo;,\n],\naxis=1,\n)\ndata[\u0026ldquo;Date\u0026rdquo;] = pd.to_datetime(data[\u0026ldquo;Date\u0026rdquo;])\ndata.set_index(\u0026ldquo;Date\u0026rdquo;, inplace=True)\ndata = data.iloc[::-1]\nreturn data\ndef process_output(output, instrument, strategy, in_row=True):\n\u0026quot;\u0026quot;\u0026quot;\nProcess backtest output data to include instrument name, strategy name,\nand parameters.\nReturns a Pandas dataframe of the processed output.\n\u0026quot;\u0026quot;\u0026quot;\nif in_row:\noutput = pd.DataFrame(output).T\noutput[\u0026ldquo;Instrument\u0026rdquo;] = instrument\noutput[\u0026ldquo;Strategy\u0026rdquo;] = strategy.__name__\noutput.pop(\u0026quot;_strategy\u0026quot;)\nreturn output\ndef save_output(output, output_dir, instrument, start, end):\n\u0026quot;\u0026quot;\u0026quot;\nSave backtest output to file and generate chart if specified.\n\u0026quot;\u0026quot;\u0026quot;\nprint(f\u0026quot;Saving output for {instrument}\u0026quot;)\nfileNameOutput = f\u0026quot;{output_dir}/{instrument}-{start}-{end}.csv\u0026quot;\noutput.to_csv(fileNameOutput)\ndef plot_results(bt, output_dir, instrument, start, end):\nprint(f\u0026quot;Saving chart for {instrument}\u0026quot;)\nfileNameChart = f\u0026quot;{output_dir}/{instrument}-{start}-{end}.html\u0026quot;\nbt.plot(filename=fileNameChart, open_browser=False)\ndef run_backtests(instruments, strategy, num_threads=4, generate_plots=False):\n\u0026quot;\u0026quot;\u0026quot;\nRun backtests for a list of instruments using a specified strategy.\nReturns a list of Pandas dataframes of the backtest results.\nArgs:\ninstruments (list): List of instruments to run backtests for\nReturns:\nList of Pandas dataframes of the backtest results\n\u0026quot;\u0026quot;\u0026quot;\noutputs = []\noutput_dir = f\u0026quot;output/raw/{strategy.__name__}\u0026quot;\noutput_dir_charts = f\u0026quot;output/charts/{strategy.__name__}\u0026quot;\nif not os.path.exists(output_dir):\nos.makedirs(output_dir)\nif not os.path.exists(output_dir_charts):\nos.makedirs(output_dir_charts)\nwith concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:\nfuture_to_instrument = {\nexecutor.submit(process_instrument, instrument, strategy): instrument\nfor instrument in instruments\n}\nfor future in concurrent.futures.as_completed(future_to_instrument):\ninstrument = future_to_instrument[future]\noutput = future.result()\nif output is not None:\noutputs.append(output[0])\nsave_output(output[0], output_dir, instrument, output[0][\u0026ldquo;Start\u0026rdquo;].to_string().strip().split()[1], output[0][\u0026ldquo;End\u0026rdquo;].to_string().strip().split()[1])\nif generate_plots:\nplot_results(output[1], output_dir_charts, instrument, output[0][\u0026ldquo;Start\u0026rdquo;].to_string().strip().split()[1], output[0][\u0026ldquo;End\u0026rdquo;].to_string().strip().split()[1])\ndata_frame = pd.concat(outputs)\nstart = data_frame[\u0026ldquo;Start\u0026rdquo;].to_string().strip().split()[1]\nend = data_frame[\u0026ldquo;End\u0026rdquo;].to_string().strip().split()[1]\nfileNameOutput = f\u0026quot;output/{strategy.__name__}-{start}-{end}.csv\u0026quot;\ndata_frame.to_csv(fileNameOutput)\nreturn data\\_frame Le script génère des graphiques pour chaque instrument testé, qui peuvent être visualisés pour analyser les performances des stratégies appliquées. Les résultats sont sauvegardés dans le répertoire output du projet BatchBacktesting.\ntickers = get_SP500()\nrun_backtests(tickers, strategy=EMA, num_threads=12, generate_plots=True)\nticker = get_all_crypto()\nrun_backtests(ticker, strategy=EMA, num_threads=12, generate_plots=True)\nLe lien que vous avez partagé correspond au répertoire output du projet BatchBacktesting sur","date":"2024-05-14","date_unix":1715731200,"id":"https://antoineboucher.info/CV/blog/fr/posts/experimentation-indicateurs-backtesting/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/experimentation-indicateurs-backtesting/","post_kind":"article","section":"posts","summary":"Rapport sur BatchBacktesting — APIs, stratégies EMA/MACD et analyse des résultats sur actions et crypto.","tag_refs":[{"name":"Python","permalink":"https://antoineboucher.info/CV/blog/fr/tags/python/"},{"name":"Trading","permalink":"https://antoineboucher.info/CV/blog/fr/tags/trading/"},{"name":"Backtesting","permalink":"https://antoineboucher.info/CV/blog/fr/tags/backtesting/"}],"tags":["Python","Trading","Backtesting"],"tags_text":"Python Trading Backtesting","thumb":"https://antoineboucher.info/CV/blog/posts/experimentation-indicateurs-backtesting/img-001_hu_4081a23c315de0b5.png","title":"Expérimentation des indicateurs technique avec Python et Backtesting"},{"content":"Introduction Monter une infra web solide et scalable peut être coûteux et complexe. Avec les bons outils, on peut rester efficace et économique. Cet article décrit Caddy sur AWS EC2, l’intégration à CloudWatch pour la supervision, et Step Functions + Lambda pour automatiser — une approche complète pour un tableau de bord à budget maîtrisé.\nÉtape 1 : installer Caddy sur EC2 Caddy est un serveur web simple avec HTTPS automatique, adapté au trafic web et au reverse proxy. Je l’utilise aussi pour mes assistants à la maison.\nLancer une instance EC2 :\nConnexion à la console AWS. EC2 → lancer une instance (Amazon Linux 2 ou autre distro). Type d’instance (ex. t2.micro free tier ou t4g.nano ~0,10 $/jour). Groupe de sécurité : HTTP, HTTPS, SSH. Installer Caddy — en SSH sur l’instance : sudo yum update -y\nsudo yum install -y yum-utils\nsudo yum-config-manager — add-repo https://dl.cloudsmith.io/public/caddy/stable/rpm.repo\nsudo yum install caddy -y\nConfigurer Caddy — exemple de Caddyfile (domaine et proxy) : {\nemail antoine@antoineboucher.info\nservers {\nmetrics\n}\nadmin :2019\n}\n(log_site) {\nlog {\noutput file /home/ec2-user/caddy/logs/{args[0]}.log {\nroll_size 10mb\nroll_keep 5\nroll_keep_for 168h\n}\nlevel INFO\n}\n}\nantoineboucher.info www.antoineboucher.info {\nimport log_site antoineboucher.info\nreverse_proxy \u0026lt;cloudfront_url\u0026gt;\nhandle_errors {\nredir https://www.github.com/antoinebou12\n}\n}\nlinkedin.antoineboucher.info www.linkedin.antoineboucher.info {\nimport log_site linkedin.antoineboucher.info\nredir https://www.linkedin.com/in/antoineboucher12\n}\nhome.antoineboucher.info www.home.antoineboucher.info {\nimport log_site home.antoineboucher.info\nreverse_proxy http://homeip:port\n}\nDémarrer / recharger Caddy : sudo caddy reload\nÉtape 2 : supervision avec CloudWatch CloudWatch collecte métriques et journaux pour AWS et au-delà.\nJournaux Caddy → CloudWatch : adapter la config Caddy ou pousser les fichiers de log via script (AWS CLI / SDK), comme l’exemple Python ci-dessous. import os\nimport boto3\nfrom datetime import datetime\n# Initialize the CloudWatch client\ncloudwatch = boto3.client(\u0026rsquo;logs\u0026rsquo;, region_name=\u0026lsquo;us-east-1\u0026rsquo;)\n# Define your log group name\nlog_group_name = \u0026lsquo;reverse_proxy\u0026rsquo;\n# Path to your log directory\nlog_directory = \u0026ldquo;/home/ec2-user/caddy/logs\u0026rdquo;\ndef send_log_to_cloudwatch(log_stream_name, log_message):\ntry:\n# Get or create the log stream\nstreams = cloudwatch.describe_log_streams(logGroupName=log_group_name, logStreamNamePrefix=log_stream_name)\nif not streams[\u0026rsquo;logStreams\u0026rsquo;]:\ncloudwatch.create_log_stream(logGroupName=log_group_name, logStreamName=log_stream_name)\n# Send log to CloudWatch\ncloudwatch.put_log_events(\nlogGroupName=log_group_name,\nlogStreamName=log_stream_name,\nlogEvents=[\n{\n\u0026rsquo;timestamp\u0026rsquo;: int(datetime.now().timestamp() * 1000),\n\u0026lsquo;message\u0026rsquo;: log_message\n}\n]\n)\nexcept Exception as e:\nprint(f\u0026quot;Failed to send log to CloudWatch: {str(e)}\u0026quot;)\n# Read logs from files and send to CloudWatch\nfor filename in os.listdir(log_directory):\nif filename.endswith(\u0026quot;.log\u0026quot;):\nlog_stream_name = filename[:-4] # Remove .log from filename to use as stream name\nfile_path = os.path.join(log_directory, filename)\nwith open(file_path, \u0026lsquo;r\u0026rsquo;) as file:\nfor line in file:\nsend_log_to_cloudwatch(log_stream_name, line.strip())\nPlanifiez un cron sur l’instance pour exécuter ce script la nuit :\nsudo yum install cronie -y\nsudo systemctl start crond\nsudo systemctl enable crond\nchmod +x /home/ec2-user/cloudwatch.py\ncrontab -e\n0 0 * * * /usr/bin/python3 /home/ec2-user/cloudwatch.py\nCréer les groupes de journaux CloudWatch : aws logs create-log-group - log-group-name reverse_proxy\naws logs create-log-group - log-group-name geoip\nFonction Lambda pour interroger les journaux :\nimport boto3\nimport json\nimport time\nfrom datetime import datetime, timedelta\ndef lambda_handler(event, context):\nclient = boto3.client(\u0026rsquo;logs\u0026rsquo;)\nquery = \u0026quot;\u0026quot;\u0026quot;\nfields @timestamp, @message\n| parse @message /\u0026ldquo;remote_ip\u0026rdquo;: \u0026ldquo;(?\u0026lt;remote_ip\u0026gt;[^\u0026rdquo;]+)\u0026quot;/\n| stats count() by remote_ip\n| sort remote_ip asc\n\u0026quot;\u0026quot;\u0026quot;\nlog\\_group = 'reverse\\_proxy' start\\_query\\_response = client.start\\_query( logGroupName=log\\_group, startTime=int((datetime.now() - timedelta(days=1)).timestamp()), endTime=int(datetime.now().timestamp()), queryString=query ) query\\_id = start\\_query\\_response\\['queryId'\\] response = None max\\_wait\\_time = 30 \\# maximum wait time of 30 seconds start\\_time = time.time() while response is None or response\\['status'\\] == 'Running': if time.time() - start\\_time \u0026gt; max\\_wait\\_time: raise TimeoutError(\u0026quot;Query did not complete within the maximum wait time.\u0026quot;) response = client.get\\_query\\_results(queryId=query\\_id) time.sleep(0.5) \\# Reduced sleep interval to check more frequently ip\\_addresses = \\[\\] for result in response\\['results'\\]: for field in result: if field\\['field'\\] == 'remote\\_ip': ip\\_addresses.append(field\\['value'\\]) return { 'statusCode': 200, 'body': json.dumps({'ip\\_addresses': ip\\_addresses}) }\rÉtape 3 : automatisation avec Step Functions et Lambda\n{\n\u0026ldquo;Comment\u0026rdquo;: \u0026ldquo;Query CloudWatch Logs and Get IP Geolocation\u0026rdquo;,\n\u0026ldquo;StartAt\u0026rdquo;: \u0026ldquo;QueryLogsInsights\u0026rdquo;,\n\u0026ldquo;States\u0026rdquo;: {\n\u0026ldquo;QueryLogsInsights\u0026rdquo;: {\n\u0026ldquo;Type\u0026rdquo;: \u0026ldquo;Task\u0026rdquo;,\n\u0026ldquo;Resource\u0026rdquo;: \u0026ldquo;arn:aws:lambda:us-east-1:590183756542:function:QueryLogsInsights\u0026rdquo;,\n\u0026ldquo;Next\u0026rdquo;: \u0026ldquo;GetGeolocation\u0026rdquo;\n},\n\u0026ldquo;GetGeolocation\u0026rdquo;: {\n\u0026ldquo;Type\u0026rdquo;: \u0026ldquo;Task\u0026rdquo;,\n\u0026ldquo;Resource\u0026rdquo;: \u0026ldquo;arn:aws:lambda:us-east-1:590183756542:function:GeolocationIP\u0026rdquo;,\n\u0026ldquo;End\u0026rdquo;: true\n}\n}\n}\nLambda — requête CloudWatch Insights :\nimport json\nimport urllib3\nimport boto3\nimport time\ndef lambda_handler(event, context):\n# Extract IP addresses from the event\nip_addresses = json.loads(event[\u0026lsquo;body\u0026rsquo;])[\u0026lsquo;ip_addresses\u0026rsquo;]\nhttp = urllib3.PoolManager() results = \\[\\] for ip in ip\\_addresses: response = http.request('GET', f\u0026quot;https://ipinfo.io/{ip}/json\u0026quot;) data = json.loads(response.data.decode('utf-8')) results.append({ 'IP': ip, 'Location': f\u0026quot;{data.get('city')}, {data.get('region')}, {data.get('country')}\u0026quot;, 'Coordinates': data.get('loc'), 'Organization': data.get('org'), 'Timezone': data.get('timezone') }) \\# Log results to CloudWatch Logs log\\_client = boto3.client('logs') log\\_group\\_name = 'geoip' log\\_stream\\_name = 'geolocation\\_results' \\# Ensure the log group exists try: log\\_client.create\\_log\\_group(logGroupName=log\\_group\\_name) except log\\_client.exceptions.ResourceAlreadyExistsException: pass \\# Ensure the log stream exists try: log\\_client.create\\_log\\_stream(logGroupName=log\\_group\\_name, logStreamName=log\\_stream\\_name) except log\\_client.exceptions.ResourceAlreadyExistsException: pass \\# Put log events for each location log\\_events = \\[\\] for result in results: log\\_events.append({ 'timestamp': int(time.time() \\* 1000), \\# Current time in milliseconds 'message': json.dumps(result) }) \\# Split log events into batches of 10 (AWS limit for PutLogEvents) batch\\_size = 10 for i in range(0, len(log\\_events), batch\\_size): response = log\\_client.put\\_log\\_events( logGroupName=log\\_group\\_name, logStreamName=log\\_stream\\_name, logEvents=log\\_events\\[i:i+batch\\_size\\] ) return { 'statusCode': 200, 'body': json.dumps(results) }\rRequête CloudWatch — IP uniques par sous-domaine\nfields @message\n| parse @message /\u0026ldquo;remote_ip\u0026rdquo;: \u0026ldquo;(?\u0026lt;remote_ip\u0026gt;[^\u0026rdquo;]+)\u0026quot;/\n| stats count_distinct(remote_ip) as unique_ip by remote_ip\n| sort unique_ip desc\nRequête CloudWatch — géolocalisation\nfields @timestamp, @message\n| parse @message /\u0026ldquo;IP\u0026rdquo;: \u0026ldquo;(?[^\u0026rdquo;]+)\u0026quot;, \u0026ldquo;Location\u0026rdquo;: \u0026ldquo;(?\u0026lt;location\u0026gt;[^\u0026rdquo;]+)\u0026quot;/\n| stats count() by ip, location\n| sort count desc\nConclusion En combinant Caddy sur EC2, CloudWatch, Step Functions et Lambda, on obtient une infra web plus simple à exploiter, avec supervision et automatisation utiles pour un coût maîtrisé.\nPublié à l’origine sur Medium.","date":"2024-05-14","date_unix":1715724e3,"id":"https://antoineboucher.info/CV/blog/fr/posts/caddy-ec2-cloudwatch-lambda/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/caddy-ec2-cloudwatch-lambda/","post_kind":"article","section":"posts","summary":"Caddy sur EC2, journaux vers CloudWatch, scripts Python et orchestration Step Functions + Lambda pour un tableau de bord peu coûteux.","tag_refs":[{"name":"AWS","permalink":"https://antoineboucher.info/CV/blog/fr/tags/aws/"},{"name":"Caddy","permalink":"https://antoineboucher.info/CV/blog/fr/tags/caddy/"},{"name":"EC2","permalink":"https://antoineboucher.info/CV/blog/fr/tags/ec2/"},{"name":"CloudWatch","permalink":"https://antoineboucher.info/CV/blog/fr/tags/cloudwatch/"},{"name":"Lambda","permalink":"https://antoineboucher.info/CV/blog/fr/tags/lambda/"},{"name":"Step Functions","permalink":"https://antoineboucher.info/CV/blog/fr/tags/step-functions/"}],"tags":["AWS","Caddy","EC2","CloudWatch","Lambda","Step Functions"],"tags_text":"AWS Caddy EC2 CloudWatch Lambda Step Functions","thumb":"https://antoineboucher.info/CV/blog/posts/caddy-ec2-cloudwatch-lambda/img-001_hu_2ffc3ac1a9ed3ee.png","title":"Faire travailler ensemble Caddy, EC2, CloudWatch, Step Functions et Lambda"},{"content":"Je suis heureux d’annoncer que j’ai récemment obtenu la certification AWS Certified Cloud Practitioner d’Amazon Web Services (AWS) ! C’est une étape importante dans mon parcours pro, et je profite de ce billet pour mettre en avant les outils qui m’y ont aidé.\nAWS Skill Builder et AWS Cloud Quest ont été centraux dans ma préparation, avec un parcours à la fois structuré et ludique. Voici mon plan d’étude et comment ces ressources peuvent aider quiconque veut renforcer ses compétences cloud.\nMon guide d’étude : sprint sur deux semaines Temps investi : environ 60 heures sur deux semaines.\nRessources utilisées :\nUltimate AWS Certified Cloud Practitioner CLF-C02 (Udemy — payant) AWS Cloud Quest: Cloud Practitioner (AWS Skill Builder — gratuit) AWS Escape Room: Exam Prep for AWS Certified Cloud Practitioner (CLF-C02) (AWS Skill Builder — essai gratuit payant) Examen blanc gratuit (https://www.w3schools.com/aws/aws_cloudessentials_awscert.php) Exam Prep Enhanced Course: AWS Certified Cloud Practitioner (CLF-C02 — anglais) (AWS Skill Builder — essai gratuit payant) AWS Skill Builder AWS Skill Builder est un centre d’apprentissage en ligne pour approfondir les services AWS à tous niveaux, du débutant à l’avancé.\nFonctionnalités :\nParcours structurés : pour Cloud Practitioner, un chemin couvre les objectifs d’examen. Labs pratiques : mise en situation sur de vrais scénarios. Contenu varié : vidéos, quiz, lectures, pour différents styles d’apprentissage. AWS Cloud Quest : gamifier le cloud\nAWS Cloud Quest: Cloud Practitioner transforme l’apprentissage en aventure (quêtes, énigmes).\nApprentissage interactif Scénarios réalistes Engagement AWS Escape Room\nL’Escape Room AWS simule des situations où il faut résoudre des défis pour « s’échapper » de salles virtuelles.\nSimulation d’examen : format et contrainte de temps. Esprit critique : utile pour l’examen et le terrain. Interactif : garde la motivation pendant la préparation. Synthèse de mon parcours\nSemaine 1 : cours Udemy Ultimate AWS Cloud Practitioner, vidéos et labs. Semaine 2 : Cloud Quest, Escape Room, examens blancs et cours Exam Prep Enhanced sur Skill Builder. Pearson VUE et temps d’examen supplémentaire J’ai passé l’examen via Pearson VUE (présentiel ou en ligne). De la réservation au jour J, l’expérience a été fluide.\nSi l’anglais n’est pas votre langue première, AWS permet de demander une prolongation de 50 % de la durée :\nCertifications AWS : avant de planifier, rubrique exam accommodations Pièce d’identité valide pour la vérification (présentiel ou en ligne).\nPlanifier avec l’extension une fois approuvée — plus de temps pour lire chaque question sereinement.\nExamen surveillé en ligne Délais d’attente : se connecter au moins 30 minutes avant pour les contrôles. Pièce : propre, sans nourriture ni second écran ; la procuration demande une vue 360° de la pièce. Logiciel Pearson VUE : installer et tester à l’avance. Calme : prévenir l’entourage pour limiter les interruptions. Badge numérique Après réussite, AWS délivre un badge via Credly :\nAttendre 2–3 jours l’e-mail Credly. Créer un compte Credly avec la même adresse que la certification AWS. Accepter le badge. Partager sur LinkedIn, CV, etc. Conclusion AWS Skill Builder et AWS Cloud Quest ont rendu le chemin vers Cloud Practitioner à la fois complet et agréable. Si vous préparez une certif AWS ou montez en compétence cloud, je recommande fortement ces plateformes.\nN’hésitez pas à me contacter pour des questions sur ma méthode ou des conseils sur votre parcours. Bon apprentissage !\nRéférences AWS Skill Builder AWS Cloud Quest Ultimate AWS Certified Cloud Practitioner CLF-C02 (Udemy) AWS Escape Room: Exam Prep for AWS Certified Cloud Practitioner Examen blanc gratuit (W3Schools) Exam Prep Enhanced Course: AWS Certified Cloud Practitioner Publié à l’origine sur Medium.","date":"2024-05-14","date_unix":1715716800,"id":"https://antoineboucher.info/CV/blog/fr/posts/aws-certified-cloud-practitioner/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/aws-certified-cloud-practitioner/","post_kind":"article","section":"posts","summary":"Plan d’étude sur deux semaines — Udemy, AWS Skill Builder, Cloud Quest, Escape Room et astuces Pearson VUE (temps supplémentaire, badge Credly).","tag_refs":[{"name":"AWS","permalink":"https://antoineboucher.info/CV/blog/fr/tags/aws/"},{"name":"Certification","permalink":"https://antoineboucher.info/CV/blog/fr/tags/certification/"},{"name":"Cloud","permalink":"https://antoineboucher.info/CV/blog/fr/tags/cloud/"},{"name":"Learning","permalink":"https://antoineboucher.info/CV/blog/fr/tags/learning/"}],"tags":["AWS","Certification","Cloud","Learning"],"tags_text":"AWS Certification Cloud Learning","thumb":"https://antoineboucher.info/CV/blog/posts/aws-certified-cloud-practitioner/img-001_hu_87cb9f3a16f07b36.png","title":"Parcours vers la certification AWS Certified Cloud Practitioner"},{"content":"Introduction En finance, on raisonne rarement sur un seul prix « prévu » : il s’agit plutôt de fourchettes, de risque de queue et de mesurer à quel point les modèles simples peuvent se tromper. Cet article déroule une simulation Monte Carlo de trajectoires en Python : on estime dérive et volatilité à partir des clôtures historiques, on simule de nombreux chemins de prix futurs (pas discret façon brownien géométrique), et on résume le tout par une distribution — l’objet adapté aux questions de risque (bandes, quantiles, recouvrement par rapport à une période tenue hors échantillon).\nLe Monte Carlo par chaînes de Markov (MCMC), comme dans l’article de Landauskas et Valakevičius sur la modélisation des cours, est un autre outil : il tire des échantillons d’une loi qui n’a pas à être gaussienne simple — par exemple construite par estimation par noyau des prix observés — alors que le code ci-dessous suppose des chocs log-normaux à partir de dérive et volatilité estimées. Un flux pratique est MCMC (ou autre inférence) pour la loi des données, puis Monte Carlo forward pour les scénarios multi-périodes. Ce billet implémente explicitement le pas GBM forward ; voir les références et le lien WIP ci-dessous pour aller vers du MCMC « papier ».\nÉtape 1 : environnement Python Installez les bibliothèques nécessaires (pandas, numpy, httpx, backtesting, pandas_ta, matplotlib, scipy, rich, etc.) :\nimport pandas as pd\nimport numpy as np\nfrom datetime import datetime\nimport concurrent.futures\nimport warnings\nfrom rich.progress import track\nfrom backtesting import Backtest, Strategy\nimport pandas_ta as ta\nimport matplotlib.pyplot as plt\nfrom scipy.stats import norm\nimport httpx\nwarnings.filterwarnings(\u0026ldquo;ignore\u0026rdquo;)\nÉtape 2 : fonctions utilitaires Fonctions pour récupérer l’historique actions et crypto via API :\ndef make_api_request(api_endpoint, params):\nwith httpx.Client() as client:\n# Make the GET request to the API\nresponse = client.get(api_endpoint, params=params)\nif response.status_code == 200:\nreturn response.json()\nprint(\u0026ldquo;Error: Failed to retrieve data from API\u0026rdquo;)\nreturn None\ndef get_historical_price_full_crypto(symbol):\napi_endpoint = f\u0026quot;{BASE_URL_FMP}/historical-price-full/crypto/{symbol}\u0026quot;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make_api_request(api_endpoint, params)\ndef get_historical_price_full_stock(symbol):\napi_endpoint = f\u0026quot;{BASE_URL_FMP}/historical-price-full/{symbol}\u0026quot;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make\\_api\\_request(api\\_endpoint, params) def get_SP500():\napi_endpoint = \u0026ldquo;https://en.wikipedia.org/wiki/List_of_S%26P_500_companies\u0026rdquo;\ndata = pd.read_html(api_endpoint)\nreturn list(data[0][\u0026lsquo;Symbol\u0026rsquo;])\ndef get_all_crypto():\nreturn [\n\u0026ldquo;BTCUSD\u0026rdquo;, \u0026ldquo;ETHUSD\u0026rdquo;, \u0026ldquo;LTCUSD\u0026rdquo;, \u0026ldquo;BCHUSD\u0026rdquo;, \u0026ldquo;XRPUSD\u0026rdquo;, \u0026ldquo;EOSUSD\u0026rdquo;,\n\u0026ldquo;XLMUSD\u0026rdquo;, \u0026ldquo;TRXUSD\u0026rdquo;, \u0026ldquo;ETCUSD\u0026rdquo;, \u0026ldquo;DASHUSD\u0026rdquo;, \u0026ldquo;ZECUSD\u0026rdquo;, \u0026ldquo;XTZUSD\u0026rdquo;,\n\u0026ldquo;XMRUSD\u0026rdquo;, \u0026ldquo;ADAUSD\u0026rdquo;, \u0026ldquo;NEOUSD\u0026rdquo;, \u0026ldquo;XEMUSD\u0026rdquo;, \u0026ldquo;VETUSD\u0026rdquo;, \u0026ldquo;DOGEUSD\u0026rdquo;,\n\u0026ldquo;OMGUSD\u0026rdquo;, \u0026ldquo;ZRXUSD\u0026rdquo;, \u0026ldquo;BATUSD\u0026rdquo;, \u0026ldquo;USDTUSD\u0026rdquo;, \u0026ldquo;LINKUSD\u0026rdquo;, \u0026ldquo;BTTUSD\u0026rdquo;,\n\u0026ldquo;BNBUSD\u0026rdquo;, \u0026ldquo;ONTUSD\u0026rdquo;, \u0026ldquo;QTUMUSD\u0026rdquo;, \u0026ldquo;ALGOUSD\u0026rdquo;, \u0026ldquo;ZILUSD\u0026rdquo;, \u0026ldquo;ICXUSD\u0026rdquo;,\n\u0026ldquo;KNCUSD\u0026rdquo;, \u0026ldquo;ZENUSD\u0026rdquo;, \u0026ldquo;THETAUSD\u0026rdquo;, \u0026ldquo;IOSTUSD\u0026rdquo;, \u0026ldquo;ATOMUSD\u0026rdquo;, \u0026ldquo;MKRUSD\u0026rdquo;,\n\u0026ldquo;COMPUSD\u0026rdquo;, \u0026ldquo;YFIUSD\u0026rdquo;, \u0026ldquo;SUSHIUSD\u0026rdquo;, \u0026ldquo;SNXUSD\u0026rdquo;, \u0026ldquo;UMAUSD\u0026rdquo;, \u0026ldquo;BALUSD\u0026rdquo;,\n\u0026ldquo;AAVEUSD\u0026rdquo;, \u0026ldquo;UNIUSD\u0026rdquo;, \u0026ldquo;RENBTCUSD\u0026rdquo;, \u0026ldquo;RENUSD\u0026rdquo;, \u0026ldquo;CRVUSD\u0026rdquo;, \u0026ldquo;SXPUSD\u0026rdquo;,\n\u0026ldquo;KSMUSD\u0026rdquo;, \u0026ldquo;OXTUSD\u0026rdquo;, \u0026ldquo;DGBUSD\u0026rdquo;, \u0026ldquo;LRCUSD\u0026rdquo;, \u0026ldquo;WAVESUSD\u0026rdquo;, \u0026ldquo;NMRUSD\u0026rdquo;,\n\u0026ldquo;STORJUSD\u0026rdquo;, \u0026ldquo;KAVAUSD\u0026rdquo;, \u0026ldquo;RLCUSD\u0026rdquo;, \u0026ldquo;BANDUSD\u0026rdquo;, \u0026ldquo;SCUSD\u0026rdquo;, \u0026ldquo;ENJUSD\u0026rdquo;,\n]\ndef get_financial_statements_lists():\napi_endpoint = f\u0026quot;{BASE_URL_FMP}/financial-statement-symbol-lists\u0026quot;\nparams = {\u0026ldquo;apikey\u0026rdquo;: FMP_API_KEY}\nreturn make_api_request(api_endpoint, params)\nÉtape 3 : séparer entraînement et test On récupère l’historique pour un symbole et on garde deux jeux : avant janvier 2023 (estimation du modèle et simulations) et à partir de janvier 2023 (hors échantillon pour comparer les plages simulées aux prix réalisés).\nstock_symbol = \u0026ldquo;AAPL\u0026rdquo;\nstock_prices = get_historical_price_full_stock(stock_symbol)\ndata = pd.DataFrame(stock_prices[\u0026lsquo;historical\u0026rsquo;])\ndef prepare_price_frame(df):\ndf = df.rename(columns={\n\u0026lsquo;open\u0026rsquo;: \u0026lsquo;Open\u0026rsquo;,\n\u0026lsquo;high\u0026rsquo;: \u0026lsquo;High\u0026rsquo;,\n\u0026rsquo;low\u0026rsquo;: \u0026lsquo;Low\u0026rsquo;,\n\u0026lsquo;close\u0026rsquo;: \u0026lsquo;Close\u0026rsquo;,\n\u0026lsquo;volume\u0026rsquo;: \u0026lsquo;Volume\u0026rsquo;,\n})\nrequired_columns = [\u0026lsquo;date\u0026rsquo;, \u0026lsquo;Open\u0026rsquo;, \u0026lsquo;High\u0026rsquo;, \u0026lsquo;Low\u0026rsquo;, \u0026lsquo;Close\u0026rsquo;, \u0026lsquo;Volume\u0026rsquo;]\nreturn df[required_columns].sort_values(by=[\u0026lsquo;date\u0026rsquo;], ascending=True).reset_index(drop=True)\nprices_before_january_2023 = prepare_price_frame(data[data[\u0026lsquo;date\u0026rsquo;] \u0026lt; \u0026lsquo;2023-01-01\u0026rsquo;])\nprices_after_january_2023 = prepare_price_frame(data[data[\u0026lsquo;date\u0026rsquo;] \u0026gt;= \u0026lsquo;2023-01-01\u0026rsquo;])\nplt.figure(figsize=(10, 6))\nplt.title(\u0026lsquo;Stock Prices\u0026rsquo;)\nplt.xlabel(\u0026lsquo;Date\u0026rsquo;)\nplt.ylabel(\u0026lsquo;Price\u0026rsquo;)\nplt.plot(prices_before_january_2023[\u0026lsquo;date\u0026rsquo;], prices_before_january_2023[\u0026lsquo;Close\u0026rsquo;], label=\u0026lsquo;Train (before Jan 2023)\u0026rsquo;)\nplt.plot(prices_after_january_2023[\u0026lsquo;date\u0026rsquo;], prices_after_january_2023[\u0026lsquo;Close\u0026rsquo;], label=\u0026lsquo;Hold-out (from Jan 2023)\u0026rsquo;)\nplt.legend()\nplt.show()\nÉtape 4 : simulation Monte Carlo (trajectoires forward et bandes de risque) La fonction ci-dessous est une simulation Monte Carlo d’un modèle à paramètres constants : on estime moyenne et variance des rendements logarithmiques sur la fenêtre d’entraînement, on construit une dérive et une volatilité journalières, puis on tire de nombreux chocs gaussiens indépendants et on propage le prix vers l’avant. Ce n’est pas du MCMC ; il n’y a pas ici d’échantillonnage d’une loi a posteriori par chaîne de Markov. C’est le type de moteur de scénarios forward qu’on lance souvent après une étape d’inférence. À l’inverse, Landauskas et Valakevičius (Intellectual Economics, 2011) utilisent le MCMC pour échantillonner une loi façonnée par une estimation par noyau des prix (propositions linéaires par morceaux). Notre raccourci GBM est plus simple ; l’article est la référence pour l’étape d’échantillonnage proche des données.\nPour un travail en cours sur cette ligne (expériences batch, vues de risque plus riches, rapprochement d’un MCMC « papier »), voir cette expérimentation LinkedIn (WIP).\nLes sorties utiles pour le risque sont des distributions : quantiles du prix terminal, bandes façon prédiction (par ex. chemins 5e–95e percentile), et contrôles de recouvrement sur l’hors échantillon (le prix réalisé tombait-il là où la masse simulée était ?).\ndef monte_carlo_simulation(data, days, iterations):\nif isinstance(data, pd.Series):\ndata = data.to_numpy()\nif not isinstance(data, np.ndarray):\nraise TypeError(\u0026ldquo;Data must be a numpy array or pandas Series\u0026rdquo;)\nlog\\_returns = np.log(data\\[1:\\] / data\\[:-1\\]) mean = np.mean(log\\_returns) variance = np.var(log\\_returns) drift = mean - (0.5 \\* variance) daily\\_volatility = np.std(log\\_returns) future\\_prices = np.zeros((days, iterations)) current\\_price = data\\[-1\\] for t in range(days): shocks = drift + daily\\_volatility \\* norm.ppf(np.random.rand(iterations)) future\\_prices\\[t\\] = current\\_price \\* np.exp(shocks) current\\_price = future\\_prices\\[t\\] return future\\_prices\rVisualisation simulation_days = 364\nmc_iterations = 1000\nmc_prices = monte_carlo_simulation(prices_before_january_2023[\u0026lsquo;Close\u0026rsquo;], simulation_days, mc_iterations)\nlast_train_close = prices_before_january_2023[\u0026lsquo;Close\u0026rsquo;].iloc[-1]\nlast_close_price = np.full((1, mc_iterations), last_train_close)\nmc_prices_combined = np.concatenate((last_close_price, mc_prices), axis=0)\nlast_date = prices_before_january_2023[\u0026lsquo;date\u0026rsquo;].iloc[-1]\nsimulated_dates = pd.date_range(start=last_date, periods=simulation_days + 1)\n# Percentiles across paths at each future step (risk band)\np05 = np.percentile(mc_prices_combined, 5, axis=1)\np50 = np.percentile(mc_prices_combined, 50, axis=1)\np95 = np.percentile(mc_prices_combined, 95, axis=1)\nmean_path = mc_prices_combined.mean(axis=1)\n# Terminal distribution at the last simulated step (VaR-style summaries)\nterminal_prices = mc_prices_combined[simulation_days, :]\nmean_terminal_price = float(np.mean(terminal_prices))\nq5, q50, q95 = np.percentile(terminal_prices, [5, 50, 95])\nterminal_return = terminal_prices / last_train_close - 1.0\nret_q5, ret_q50, ret_q95 = np.percentile(terminal_return, [5, 50, 95])\nhorizon_idx = min(simulation_days, len(prices_after_january_2023) - 1)\nreal_price = float(prices_after_january_2023[\u0026lsquo;Close\u0026rsquo;].iloc[horizon_idx])\nreal_date = prices_after_january_2023[\u0026lsquo;date\u0026rsquo;].iloc[horizon_idx]\nin_90_band = q5 \u0026lt;= real_price \u0026lt;= q95\nprint(f\u0026quot;Simulated horizon: {simulation_days} trading days after {last_date}\u0026quot;)\nprint(f\u0026quot;Mean terminal price: ${mean_terminal_price:.2f}\u0026quot;)\nprint(f\u0026quot;Terminal price percentiles (5 / 50 / 95): ${q5:.2f} / ${q50:.2f} / ${q95:.2f}\u0026quot;)\nprint(f\u0026quot;Terminal simple return vs last train close — 5th / 50th / 95th %ile: {ret_q5*100:.2f}% / {ret_q50*100:.2f}% / {ret_q95*100:.2f}%\u0026quot;)\nprint(f\u0026quot;Hold-out price at aligned step ({real_date}): ${real_price:.2f}\u0026quot;)\nprint(f\u0026quot;Realized price inside simulated 5–95% band: {in_90_band}\u0026quot;)\nplt.figure(figsize=(10, 6))\nfor i in range(mc_iterations):\nplt.plot(simulated_dates, mc_prices_combined[:, i], linewidth=0.5, color=\u0026lsquo;gray\u0026rsquo;, alpha=0.02)\nplt.fill_between(simulated_dates, p05, p95, alpha=0.25, label=\u0026lsquo;5th–95th percentile band\u0026rsquo;)\nplt.plot(simulated_dates, p50, label=\u0026lsquo;Median path\u0026rsquo;, linewidth=2, color=\u0026lsquo;C0\u0026rsquo;)\nplt.plot(simulated_dates, mean_path, label=\u0026lsquo;Mean path\u0026rsquo;, linewidth=2, linestyle=\u0026rsquo;\u0026ndash;\u0026rsquo;, color=\u0026lsquo;C1\u0026rsquo;)\nplt.plot(pd.to_datetime(prices_before_january_2023[\u0026lsquo;date\u0026rsquo;]), prices_before_january_2023[\u0026lsquo;Close\u0026rsquo;], label=\u0026lsquo;Train (before Jan 2023)\u0026rsquo;, linewidth=2, color=\u0026lsquo;black\u0026rsquo;)\nplt.plot(pd.to_datetime(prices_after_january_2023[\u0026lsquo;date\u0026rsquo;]), prices_after_january_2023[\u0026lsquo;Close\u0026rsquo;], label=\u0026lsquo;Hold-out (from Jan 2023)\u0026rsquo;, linewidth=2, color=\u0026lsquo;green\u0026rsquo;)\nplt.axvline(pd.to_datetime(real_date), color=\u0026lsquo;red\u0026rsquo;, linestyle=\u0026rsquo;:\u0026rsquo;, linewidth=1, alpha=0.8, label=\u0026lsquo;Hold-out step aligned to horizon\u0026rsquo;)\nplt.scatter([pd.to_datetime(real_date)], [real_price], color=\u0026lsquo;red\u0026rsquo;, s=40, zorder=5, label=\u0026lsquo;Realized (aligned)\u0026rsquo;)\nplt.title(\u0026lsquo;Monte Carlo Simulation of Stock Prices (with percentile band)\u0026rsquo;)\nplt.xlabel(\u0026lsquo;Date\u0026rsquo;)\nplt.ylabel(\u0026lsquo;Price\u0026rsquo;)\nplt.legend(loc=\u0026lsquo;upper left\u0026rsquo;, fontsize=8)\nplt.show()\nConclusion Le Monte Carlo forward fournit une distribution de prix futurs sous une dynamique supposée — adapté aux bandes de quantiles, au comportement de queue et aux contrôles de recouverture sur données hors échantillon. C’est une étape distincte du MCMC, qui sert à échantillonner sous un modèle flexible des données (comme l’approche KDE de Landauskas et Valakevičius) avant ou en parallèle de la simulation forward. Pipeline typique : ajuster ou échantillonner la loi qui colle à l’historique, puis faire avancer les scénarios par Monte Carlo. Avec du backtest de stratégie, on sépare « à quel point le risq","date":"2024-05-14","date_unix":1715691600,"id":"https://antoineboucher.info/CV/blog/fr/posts/predicting-stock-prices-monte-carlo/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/predicting-stock-prices-monte-carlo/","post_kind":"article","section":"posts","summary":"Simulation de trajectoires Monte Carlo en Python à partir de rendements historiques — bandes de risque, quantiles et comparaison à une période de validation.","tag_refs":[{"name":"Python","permalink":"https://antoineboucher.info/CV/blog/fr/tags/python/"},{"name":"Finance","permalink":"https://antoineboucher.info/CV/blog/fr/tags/finance/"},{"name":"Monte Carlo","permalink":"https://antoineboucher.info/CV/blog/fr/tags/monte-carlo/"},{"name":"Backtesting","permalink":"https://antoineboucher.info/CV/blog/fr/tags/backtesting/"}],"tags":["Python","Finance","Monte Carlo","Backtesting"],"tags_text":"Python Finance Monte Carlo Backtesting","thumb":"https://antoineboucher.info/CV/blog/posts/predicting-stock-prices-monte-carlo/img-001_hu_66a57fcbf469d696.png","title":"Prévoir des cours boursiers avec des simulations Monte Carlo"},{"content":"Introduction Ce tutoriel montre comment monter un sketch Kinectron dans p5.js avec arrêt / lecture du sketch et enregistrement en GIF.\nPrérequis Bases JavaScript et p5.js. Bibliothèque Kinectron installée. Bibliothèque p5.js installée. Kinect v2 ou Azure Kinect DK. Serveur Kinectron en marche. Environnement local ou en ligne compatible JavaScript et p5.js. Étape 1 : environnement Inclure p5.js et Kinectron dans le HTML.\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;script src=\u0026#34;https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.min.js\u0026#34; type=\u0026#34;text/javascript\u0026#34; \u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; \u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/addons/p5.sound.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;./client/dist/kinectron-client.js\u0026#34; type=\u0026#34;text/javascript\u0026#34; \u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;sketch.js\u0026#34; type=\u0026#34;text/javascript\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt;\u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Étape 2 : variables Dans sketch.js, définir les variables nécessaires.\n// Kinectron setup function function Kinectron() { // Types d’articulations Kinectron } let kinectron = new Kinectron(); const liveData = false; let stopButton, playButton, saveGifButton; let recorded_skeleton; function preload() { // Charger les données enregistrées si liveData est false } Étape 3 : canvas et boutons function setup() { createCanvas(640, 480); background(0); if (liveData) { // Kinectron en direct } else { // Données préenregistrées et boutons } } Étape 4 : draw function draw() { // Données live ou enregistrées } Étape 5 : gestes function checkForGestures(body) { // Détecter et afficher les gestes } function isClapping(body) { /* ... */ } function isOKSign(body) { /* ... */ } // Autres fonctions de gestes Étape 6 : boutons function stopSketch() { noLoop(); console.log(\u0026#34;Sketch stopped.\u0026#34;); } function playSketch() { loop(); console.log(\u0026#34;Sketch playing.\u0026#34;); } function startCreatingGif() { saveGif(\u0026#39;mySketch\u0026#39;, 5); // Téléchargement du GIF, etc. } Étape 7 : lancer le sketch Exécuter dans un environnement compatible p5.js.\nVous avez maintenant un sketch Kinectron dans p5.js avec contrôle de lecture et export GIF. Expérimentez d’autres gestes pour enrichir le sketch.","date":"2024-03-15","date_unix":1710511200,"id":"https://antoineboucher.info/CV/blog/fr/posts/kinectron-p5-sketch-gif/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/kinectron-p5-sketch-gif/","post_kind":"article","section":"posts","summary":"Brancher Kinectron sur p5.js, arrêter / relancer le sketch et enregistrer la sortie en GIF (Kinect v2 ou Azure Kinect DK).","tag_refs":[{"name":"Kinect","permalink":"https://antoineboucher.info/CV/blog/fr/tags/kinect/"},{"name":"Kinectron","permalink":"https://antoineboucher.info/CV/blog/fr/tags/kinectron/"},{"name":"P5.js","permalink":"https://antoineboucher.info/CV/blog/fr/tags/p5.js/"},{"name":"JavaScript","permalink":"https://antoineboucher.info/CV/blog/fr/tags/javascript/"},{"name":"Creative Coding","permalink":"https://antoineboucher.info/CV/blog/fr/tags/creative-coding/"},{"name":"Tutorial","permalink":"https://antoineboucher.info/CV/blog/fr/tags/tutorial/"}],"tags":["Kinect","Kinectron","p5.js","JavaScript","Creative Coding","Tutorial"],"tags_text":"Kinect Kinectron p5.js JavaScript Creative Coding Tutorial","thumb":"https://antoineboucher.info/CV/blog/posts/kinectron-p5-sketch-gif/mySketch.gif","title":"Kinectron + p5.js — contrôle du sketch et export GIF"},{"content":"Lundi, Byzantium a organisé son premier atelier sur Ethereum : une session accessible aux débutant·e·s comme aux personnes déjà à l’aise avec la blockchain, avec mise en pratique jusqu’au déploiement d’un token et des transferts entre portefeuilles.\nProgramme L’animation, assurée par Khalil Anis Zabat, est partie d’une base Solidity et d’un contrat ERC-20 standard, en s’appuyant sur OpenZeppelin : définir le nom et le symbole du token, fixer les décimales, et émettre une première quantité pour le déployeur. Ensuite, chacun·e a pu suivre la démo, déployer son propre contrat sur un réseau de test, puis échanger des tokens avec les autres participant·e·s — le passage concret du « code sur l’écran » à « quelque chose qu’on peut envoyer à un pair ».\nLe squelette montré pendant l’atelier ressemblait à ceci (contrat minimal ERC-20 + mint initial) :\n// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import \u0026#34;@openzeppelin/contracts/token/ERC20/ERC20.sol\u0026#34;; contract MyToken is ERC20 { constructor() ERC20(\u0026#34;MyToken\u0026#34;, \u0026#34;MTK\u0026#34;) { _mint(msg.sender, 1000000 * 10 ** decimals()); } } Ambiance et suite La salle, format tables et prises intégrées, se prêtait bien au pair programming et aux allers-retours avec l’animateur. L’objectif annoncé pour la suite : d’autres ateliers sur d’autres types de tokens et des techniques plus avancées.\nLiens Contexte et annonce (repost Byzantium, fil LinkedIn) : publication associée. Mon contrat déployé (lien court ; redirige vers l’explorateur du contrat) : lnkd.in/e-9T5-MX. Merci à Khalil pour l’organisation et la pédagogie, et à Byzantium ainsi qu’aux participant·e·s pour cette première édition.\nVersion courte en anglais — même slug ; vous pouvez aussi passer en EN depuis l’en-tête du site.","date":"2024-03-11","date_unix":1710196200,"id":"https://antoineboucher.info/CV/blog/fr/posts/byzantium-solidity-ethereum-workshop/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/byzantium-solidity-ethereum-workshop/","post_kind":"conference","section":"posts","summary":"Retour sur le premier workshop Byzantium sur Ethereum : contrat ERC-20 avec OpenZeppelin, déploiement et échanges entre participants.","tag_refs":[{"name":"Byzantium","permalink":"https://antoineboucher.info/CV/blog/fr/tags/byzantium/"},{"name":"Solidity","permalink":"https://antoineboucher.info/CV/blog/fr/tags/solidity/"},{"name":"Ethereum","permalink":"https://antoineboucher.info/CV/blog/fr/tags/ethereum/"},{"name":"Conference","permalink":"https://antoineboucher.info/CV/blog/fr/tags/conference/"},{"name":"Education","permalink":"https://antoineboucher.info/CV/blog/fr/tags/education/"}],"tags":["Byzantium","Solidity","Ethereum","Conference","Education"],"tags_text":"Byzantium Solidity Ethereum Conference Education","thumb":"https://antoineboucher.info/CV/blog/posts/byzantium-solidity-ethereum-workshop/images/audience-wide_hu_ff5f62a1d65915b.png","title":"Premier atelier Byzantium — Solidity et token ERC-20 sur Ethereum"},{"content":"Introduction : Bienvenue sur mon blog personnel, un récit de mon parcours dans le développement d\u0026rsquo;un portfolio multifacette avec Hugo. En tant qu\u0026rsquo;ingénieur logiciel, je suis ravi de partager les subtilités de la construction d\u0026rsquo;un site web dynamique et interactif, où mes compétences professionnelles se croisent avec mes passions personnelles. Ce premier article marque le début d\u0026rsquo;une série dans laquelle je vais explorer divers aspects du développement web, de l\u0026rsquo;analyse de données et de l\u0026rsquo;intégration de technologies web avancées.\nÀ quoi s\u0026rsquo;attendre : Narration Visuelle : Le blog présentera des captures d\u0026rsquo;écran de l\u0026rsquo;interface Hugo, des graphiques illustratifs et des cartes pour enrichir l\u0026rsquo;aspect narratif.\nRessources Utiles : Je partagerai des ressources telles que des modèles Hugo, des liens vers ma page Substack et des outils technologiques comme particles.js, offrant une mine de trésors pour les ingénieurs logiciels et développeurs web en herbe.\nChoisir Hugo et le Thème HB Lorsque j\u0026rsquo;ai décidé de créer mon portfolio, j\u0026rsquo;ai été attiré par Hugo pour sa réputation de rapidité et de flexibilité. En tant qu\u0026rsquo;ingénieur logiciel, l\u0026rsquo;efficacité et la scalabilité sont toujours au premier plan de mes décisions. Après avoir exploré plusieurs options, j\u0026rsquo;ai choisi le thème HB pour son ensemble complet de fonctionnalités. Le support des commentaires du thème, son intégration transparente avec npm (Node Package Manager) et la variété des modules Hugo disponibles en faisaient un choix évident. Ces fonctionnalités ont non seulement amélioré la fonctionnalité de mon blog mais se sont également parfaitement alignées avec mon flux de travail professionnel, me permettant de mettre en œuvre des technologies web avancées avec facilité.\nFusionner l\u0026rsquo;Analyse de Données et le Développement Web Un de mes premiers projets a impliqué une analyse détaillée des vols de voitures à Montréal. Le projet n\u0026rsquo;était pas seulement un exercice d\u0026rsquo;analyse de données, mais aussi une entreprise personnelle, stimulée par des histoires d\u0026rsquo;amis et de famille. En utilisant les capacités de Hugo, j\u0026rsquo;ai pu intégrer et présenter ces ensembles de données complexes de manière accessible et engageante. Cette intégration a exemplifié la capacité de Hugo à gérer un contenu riche en données, un aspect crucial pour tout ingénieur logiciel souhaitant afficher un travail technique de manière claire et convaincante. Vous pouvez consulter l\u0026rsquo;étude ici : Étude des vols de voitures à Montréal.\nLa Polyvalence de Substack Parallèlement à mon blog Hugo, je me suis aventuré sur Substack. Cette plateforme offrait un écosystème distinct propice à une écriture approfondie et à une audience engagée. Ma page Substack sert d\u0026rsquo;espace complémentaire où je plonge plus profondément dans des sujets nécessitant une couverture plus étendue. Elle me permet d\u0026rsquo;atteindre un public plus large et offre un format différent pour l\u0026rsquo;interaction et la discussion.\nFusionner le Développement Professionnel et le Blogging Ce blog sert également de plateforme pour discuter du développement académique et professionnel. Je prévois de partager des informations sur la poursuite de diplômes avancés et sur la manière de les équilibrer avec des objectifs de carrière. En tant que personne essayant de terminer son master, j\u0026rsquo;explorerai comment les connaissances académiques peuvent être appliquées dans le développement logiciel pratique et dans l\u0026rsquo;industrie technologique plus large.\nCréer une Présence en Ligne Unique En créant mon blog, j\u0026rsquo;ai accordé une attention particulière à l\u0026rsquo;esthétique et à la fonctionnalité. En expérimentant avec divers thèmes Hugo et en intégrant des éléments interactifs comme particles.js\n, j\u0026rsquo;ai visé à créer une interface visuellement attrayante et conviviale. Ces choix de conception reflètent ma conviction de l\u0026rsquo;importance d\u0026rsquo;une expérience utilisateur propre et efficace, une philosophie que je transmets de mon expérience en ingénierie logicielle.\nConclusion : Ce blog est l\u0026rsquo;incarnation de mon parcours dans le monde de la technologie, mélangeant des expériences personnelles avec une croissance professionnelle. À travers cette plateforme, je vise à partager mes connaissances en ingénierie logicielle, en développement web et bien plus encore. Je vous invite à vous joindre à moi dans cette exploration, pour apprendre, être inspiré et découvrir les possibilités infinies dans le monde de la technologie.","date":"2024-01-08","date_unix":1704722400,"id":"https://antoineboucher.info/CV/blog/fr/posts/portfolio-hugo-week-1/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/portfolio-hugo-week-1/","post_kind":"article","section":"posts","summary":"Démarrer un portfolio avec Hugo — thèmes, contenu data-driven et lien avec Substack.","tag_refs":[{"name":"Hugo","permalink":"https://antoineboucher.info/CV/blog/fr/tags/hugo/"},{"name":"Portfolio","permalink":"https://antoineboucher.info/CV/blog/fr/tags/portfolio/"},{"name":"Static Site","permalink":"https://antoineboucher.info/CV/blog/fr/tags/static-site/"},{"name":"Substack","permalink":"https://antoineboucher.info/CV/blog/fr/tags/substack/"}],"tags":["Hugo","Portfolio","Static Site","Substack"],"tags_text":"Hugo Portfolio Static Site Substack","thumb":"https://antoineboucher.info/CV/blog/posts/portfolio-hugo-week-1/featured_hu_493ef8870764b396.png","title":"Créer un portfolio avec Hugo (semaine 1)"},{"content":"Introduction Nous avons utilisé l’app Rhino sur iPhone avec LiDAR pour scanner notre appartement et clarifier les choix d’aménagement et de mobilier.\nLiDAR et Rhino Le LiDAR capture la profondeur rapidement ; Rhino sur iPhone transforme ces scans en géométrie 3D exploitable directement sur l’appareil.\nDéroulé Nous avons parcouru pièce par pièce pendant que le téléphone cartographiait l’espace ; Rhino mettait le modèle à jour au fil du déplacement.\nCaptures d’écran Images tirées du scan Conclusion Un LiDAR portable plus une appli de modélisation ciblée suffisent pour une première exploration d’aménagement avant d’engager une chaîne CAO plus lourde ou des travaux.","date":"2024-01-02","date_unix":1704204e3,"id":"https://antoineboucher.info/CV/blog/fr/posts/rhino-lidar-apartment-scan/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/rhino-lidar-apartment-scan/","post_kind":"article","section":"posts","summary":"Utiliser l’app Rhino sur iPhone et le LiDAR pour scanner les pièces et raisonner sur l’aménagement, les meubles et l’espace.","tag_refs":[{"name":"LiDAR","permalink":"https://antoineboucher.info/CV/blog/fr/tags/lidar/"},{"name":"Rhino","permalink":"https://antoineboucher.info/CV/blog/fr/tags/rhino/"},{"name":"IOS","permalink":"https://antoineboucher.info/CV/blog/fr/tags/ios/"},{"name":"3D Scanning","permalink":"https://antoineboucher.info/CV/blog/fr/tags/3d-scanning/"},{"name":"Architecture","permalink":"https://antoineboucher.info/CV/blog/fr/tags/architecture/"}],"tags":["LiDAR","Rhino","iOS","3D Scanning","Architecture"],"tags_text":"LiDAR Rhino iOS 3D Scanning Architecture","thumb":"https://antoineboucher.info/CV/blog/posts/rhino-lidar-apartment-scan/featured_hu_8c29c463a4f4989d.jpg","title":"Scan d’appartement LiDAR avec Rhino sur iPhone"},{"content":"La bio de ce site résume la facette du métier qui m’intéresse le plus : backend, plateforme et DevSecOps. Cet article prolonge ce regard — pas une chronologie d’emplois, mais les idées qui reviennent quand on cesse de ne mesurer que « les features livrées ».\nDes features aux systèmes Au début, le progrès semble linéaire : tickets fermés, endpoints ajoutés, écrans livrés. Ce travail compte. Avec le temps, les problèmes intéressants se situent un cran au-dessus : comment les services communiquent, comment les pannes se propagent, comment un changement dans le dépôt d’une équipe affecte tout le monde le lundi matin. Le backend cesse d’être « écrire le handler » et devient « concevoir quelque chose qui reste compréhensible quand vous n’êtes plus dans la pièce ».\nLa pensée plateforme prolonge ça vers l’extérieur. Si vous avez déjà mis en place de la CI, standardisé les logs ou facilité le démarrage local pour un·e collègue, vous avez déjà fait du travail plateforme. L’objectif est de réduire la taxe sur le quotidien : moins de runbooks ad hoc, moins de fils « ça marche chez moi », plus de chemins reproductibles de l’idée à la prod.\nFiabilité et ownership La fiabilité, ce n’est pas seulement des graphiques de disponibilité. C’est aussi la confiance des équipes pour aller vite. Cette confiance vient d’un ownership clair (qui répare quoi quand ça casse), d’un comportement observable (métriques, traces, logs qui répondent à de vraies questions) et de changements assez petits pour être raisonnés.\nJ’ai appris à traiter incidents et quasi-incidents comme retour de conception. Les post-mortems aident, mais le gain durable, c’est d’intégrer ces leçons dans les défauts : meilleures alertes, déploiements plus sûrs, frontières plus nettes entre composants. L’ownership, c’est faire cette boucle même sans ticket assigné.\nLa sécurité dans la livraison Pour moi, le DevSecOps n’est pas une barrière en fin de sprint. C’est décaler la vigilance vers l’amont de façon réaliste pour les équipes : hygiène des dépendances, gestion des secrets, moindre privilège, menace modélisée en assez peu de temps pour tenir dans une planification normale. Une sécurité qui ne vit que dans la tête d’un·e spécialiste ne scale pas ; des habitudes dans les pipelines et les conventions, si.\nLe même état d’esprit vaut pour les services tiers et le cloud. Si vous ne pouvez pas expliquer ce qui expose quoi, vous n’avez pas encore une histoire déployable — vous avez un pari avec une belle étiquette.\nApprentissage et outils Les outils changent sans cesse. Les cadres, les API cloud et les flux assistés par IA continueront d’évoluer. Ce qui compose, c’est le jugement : quand adopter, quand encapsuler, quand refuser. Je lis encore la doc, casse des choses en bac à sable et emprunte des idées à l’open source et aux retours d’autres devs (y compris les brouillons — c’est souvent là qu’on lit les vraies contraintes).\nJ’accorde aussi de la valeur à l’écrit — billets courts, schémas, notes internes — parce qu’expliquer mal est souvent la première étape vers une bonne compréhension.\nCe que j’optimise ensuite À suivre, les mêmes thèmes avec des arêtes plus nettes : plateformes plus claires, livraison plus sûre, systèmes qui restent lisibles en grandissant. Si vous débutez, mon conseil (biaisé) est de viser des problèmes où vous voyez toute la boucle : code, déploiement, exploitation, amélioration. C’est là que backend, plateforme et DevSecOps cessent d’être des buzzwords et deviennent le métier.\nMerci de lire — si ça parle à quelqu’un, vous trouverez des notes plus concrètes ailleurs sur le site sous articles et projets.","date":"2023-12-30","date_unix":1703944800,"id":"https://antoineboucher.info/CV/blog/fr/posts/software-engineering-journey/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/software-engineering-journey/","post_kind":"article","section":"posts","summary":"Réflexions sur la croissance comme ingénieur logiciel — systèmes backend, plateforme et DevSecOps : ce qui reste et ce que j’optimise aujourd’hui.","tag_refs":[{"name":"Software Engineering","permalink":"https://antoineboucher.info/CV/blog/fr/tags/software-engineering/"},{"name":"Career","permalink":"https://antoineboucher.info/CV/blog/fr/tags/career/"},{"name":"Learning","permalink":"https://antoineboucher.info/CV/blog/fr/tags/learning/"},{"name":"Backend","permalink":"https://antoineboucher.info/CV/blog/fr/tags/backend/"},{"name":"DevSecOps","permalink":"https://antoineboucher.info/CV/blog/fr/tags/devsecops/"},{"name":"Platform Engineering","permalink":"https://antoineboucher.info/CV/blog/fr/tags/platform-engineering/"}],"tags":["Software Engineering","Career","Learning","Backend","DevSecOps","Platform Engineering"],"tags_text":"Software Engineering Career Learning Backend DevSecOps Platform Engineering","thumb":"/CV/blog/images/post-kind-article.png","title":"Mon parcours en génie logiciel"},{"content":"\nAlors que l’intelligence artificielle progresse, de plus en plus d’entreprises intègrent des chatbots à leur service client. Ces chatbots couvrent un large spectre de demandes, des questions simples aux sujets plus complexes. Le coût de mise en œuvre et de maintenance reste un facteur important. Dans cet article, on estime les coûts d’utilisation des modèles GPT-4 et GPT-3.5-turbo avec un plafond de 25 messages toutes les 3 heures sur un mois, en supposant des tailles de prompt moyennes comparables (50 à 200 jetons).\nAPI OpenAI L’API OpenAI sert à de nombreuses tâches de traitement du langage naturel (NLP), par exemple :\nTraduction de texte d’une langue à une autre. Génération de texte à partir d’un prompt (titres, résumés, articles). Résumé de documents longs en versions plus courtes. Chatbots pour le service client, assistants virtuels, etc. Questions-réponses avec précision et fluidité (disponible sur les moteurs GPT-3). Compréhension du sens du texte (feedback clients, sentiment, etc.). Complétion de texte (formulaires, e-mails, etc.). Classification (spam, sentiment, etc.). Comparaison des modèles GPT-4 GPT-4 apporte des capacités de raisonnement avancées et une culture générale plus large, avec une précision supérieure aux générations précédentes. Il se distingue notamment sur la créativité, les entrées visuelles et les contextes longs (plus de 25 000 mots de texte). Certaines de ces fonctionnalités étaient encore sur liste d’attente au moment de l’article.\nSur des benchmarks, GPT-4 se situe plus haut que ChatGPT sur des épreuves type barreau ou olympiade de biologie.\nLes travaux sur sécurité et alignement incluent l’apprentissage avec retour humain, l’amélioration continue via l’usage réel et la recherche sur la sécurité assistée par GPT-4.\nPlusieurs organisations ont collaboré avec OpenAI pour des produits sur GPT-4 (Duolingo, Be My Eyes, Stripe, Morgan Stanley, Khan Academy, gouvernement d’Islande, etc.).\nMalgré ses capacités, GPT-4 conserve des limites connues : biais sociaux, hallucinations, prompts adverses. OpenAI s’engage à les traiter tout en poussant transparence et littératie IA. GPT-4 est disponible dans ChatGPT Plus et en API pour les développeurs.\nGPT-3.5-turbo C’est le moteur utilisé dans la démo ChatGPT sans ChatGPT Plus.\nRédiger un e-mail ou un texte Écrire du code Python Répondre à des questions sur un corpus de documents Créer des agents conversationnels Donner une interface langage naturel à un logiciel Tutorat dans plusieurs matières Traduire des langues Simuler des personnages de jeu vidéo, etc. Le choix entre GPT-4 et GPT-3.5-turbo dépend de la qualité, de la latence et du budget : GPT-4 est plus fort sur le raisonnement difficile et les longs contextes ; GPT-3.5-turbo reste le cheval de bataille pour beaucoup de scénarios chat et outillage. Pour modéliser les coûts, combinez jetons par tour, trafic et limites de débit — surtout si vous plafonnez les messages par utilisateur et par heure.","date":"2023-04-10","date_unix":1681135200,"id":"https://antoineboucher.info/CV/blog/fr/posts/gpt4-api-costs-overview/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/gpt4-api-costs-overview/","post_kind":"article","section":"posts","summary":"Notes sur les cas d’usage GPT-4 et GPT-3.5-turbo, leurs forces, et une façon de raisonner sur les coûts d’API de chatbot à l’échelle.","tag_refs":[{"name":"AI","permalink":"https://antoineboucher.info/CV/blog/fr/tags/ai/"},{"name":"ChatGPT","permalink":"https://antoineboucher.info/CV/blog/fr/tags/chatgpt/"},{"name":"OpenAI","permalink":"https://antoineboucher.info/CV/blog/fr/tags/openai/"},{"name":"API","permalink":"https://antoineboucher.info/CV/blog/fr/tags/api/"},{"name":"NLP","permalink":"https://antoineboucher.info/CV/blog/fr/tags/nlp/"}],"tags":["AI","ChatGPT","OpenAI","API","NLP"],"tags_text":"AI ChatGPT OpenAI API NLP","thumb":"/CV/blog/images/post-kind-article.png","title":"GPT-4 vs GPT-3.5 — capacités et cadre des coûts API"},{"content":"Introduction Avoir un CV en ligne compte. Une approche efficace consiste à utiliser le paquet npm JSON Resume : rédiger le CV en JSON, puis l’exporter en HTML, PDF ou l’intégrer à un site personnel.\nFormat JSON Resume JSON Resume est une initiative open source communautaire pour un standard CV en JSON. Le format est léger et facile à manipuler, ce qui permet de construire des outils autour.\nSections typiques :\nSection basics Nom, intitulé, contact, court résumé, localisation, profils pro.\n\u0026#34;basics\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;Your Name\u0026#34;, \u0026#34;label\u0026#34;: \u0026#34;Job Title\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;your.email@example.com\u0026#34;, \u0026#34;website\u0026#34;: \u0026#34;https://yourwebsite.com\u0026#34;, \u0026#34;summary\u0026#34;: \u0026#34;A brief summary about yourself.\u0026#34;, \u0026#34;location\u0026#34;: { \u0026#34;city\u0026#34;: \u0026#34;City\u0026#34;, \u0026#34;region\u0026#34;: \u0026#34;Region\u0026#34;, \u0026#34;countryCode\u0026#34;: \u0026#34;Country Code\u0026#34; }, \u0026#34;profiles\u0026#34;: [ { \u0026#34;network\u0026#34;: \u0026#34;LinkedIn\u0026#34;, \u0026#34;username\u0026#34;: \u0026#34;yourusername\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;https://www.linkedin.com/in/yourusername/\u0026#34; } ] } Expérience professionnelle \u0026#34;work\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;Company Name\u0026#34;, \u0026#34;position\u0026#34;: \u0026#34;Your Position\u0026#34;, \u0026#34;startDate\u0026#34;: \u0026#34;YYYY-MM-DD\u0026#34;, \u0026#34;endDate\u0026#34;: \u0026#34;YYYY-MM-DD\u0026#34;, \u0026#34;summary\u0026#34;: \u0026#34;Description of your role.\u0026#34; } ] Formation \u0026#34;education\u0026#34;: [ { \u0026#34;institution\u0026#34;: \u0026#34;University Name\u0026#34;, \u0026#34;area\u0026#34;: \u0026#34;Field of Study\u0026#34;, \u0026#34;studyType\u0026#34;: \u0026#34;Degree Type\u0026#34;, \u0026#34;startDate\u0026#34;: \u0026#34;YYYY-MM-DD\u0026#34;, \u0026#34;endDate\u0026#34;: \u0026#34;YYYY-MM-DD\u0026#34; } ] Compétences \u0026#34;skills\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;Programming\u0026#34;, \u0026#34;level\u0026#34;: \u0026#34;Intermediate\u0026#34;, \u0026#34;keywords\u0026#34;: [\u0026#34;Python\u0026#34;, \u0026#34;JavaScript\u0026#34;] } ] Projets \u0026#34;projects\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;Project Name\u0026#34;, \u0026#34;startDate\u0026#34;: \u0026#34;YYYY-MM-DD\u0026#34;, \u0026#34;endDate\u0026#34;: \u0026#34;YYYY-MM-DD\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;Project description.\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;https://projecturl.com\u0026#34; } ] Paquet npm Installer resume-cli globalement :\nnpm install -g resume-cli Créer resume.json selon le schéma JSON Resume.\nExporter :\nresume export resume.html resume export resume.pdf Hébergement : le registre JSON Resume permet d’héberger gratuitement.\nJSON Resume offre un moyen standardisé et souple de créer et partager un profil pro. Avec le schéma et la CLI, vous obtenez un CV moderne à partager avec employeurs et contacts.\nRessources avec Hugo et JSON Resume Profile Studio Outil en ligne pour prévisualiser et personnaliser le JSON Resume en temps réel.\nProfile Studio Preview SkillSet Visualisation interactive des compétences (D3.js).\nSkillSet Export LinkedIn vers JSON Resume LinkedIn to JSON Resume Exporter Hugo-Mod-JSON-Resume Module Hugo pour intégrer JSON Resume, avec données multilingues et modèles par section.\nHugo-Mod-JSON-Resume Ces ressources aident à construire un CV en ligne plus riche et interactif.","date":"2022-09-10","date_unix":1662818400,"id":"https://antoineboucher.info/CV/blog/fr/posts/professional-resume-json-resume/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/professional-resume-json-resume/","post_kind":"article","section":"posts","summary":"Utiliser le schéma JSON Resume et l’outil npm pour publier en HTML, PDF ou intégrer les données du CV.","tag_refs":[{"name":"JSON Resume","permalink":"https://antoineboucher.info/CV/blog/fr/tags/json-resume/"},{"name":"Npm","permalink":"https://antoineboucher.info/CV/blog/fr/tags/npm/"},{"name":"Career","permalink":"https://antoineboucher.info/CV/blog/fr/tags/career/"},{"name":"HTML","permalink":"https://antoineboucher.info/CV/blog/fr/tags/html/"}],"tags":["JSON Resume","npm","Career","HTML"],"tags_text":"JSON Resume npm Career HTML","thumb":"/CV/blog/images/post-kind-article.png","title":"Créer un CV professionnel avec JSON Resume"},{"content":"J’ai récemment entamé une refonte de mon réseau domestique. Le passage de Draw.io à PlantUML C4 pour les diagrammes de déploiement change la donne. 🏡\nPlantUML C4 propose une approche textuelle 📝 qui s’intègre bien au contrôle de version, ce qui en fait un outil adapté à l’infrastructure as code (IaC) 🏗️ .\nJe migre aussi vers Cloudflare pour la gestion DNS ✅\nJe compte utiliser Terraform et GitHub Actions comme CD 🔁\nCôté virtualisation, j’utilise Proxmox et XCP-ng comme hyperviseurs, avec Talos OS pour mes déploiements Kubernetes issus d’un projet perso. 💻","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/home-network-plantuml-c4/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/home-network-plantuml-c4/","post_kind":"article","section":"posts","summary":"Faire évoluer les schémas du réseau domestique vers PlantUML C4, DNS Cloudflare, Terraform et Kubernetes sur Proxmox / XCP-ng.","tag_refs":[{"name":"PlantUML","permalink":"https://antoineboucher.info/CV/blog/fr/tags/plantuml/"},{"name":"C4 Model","permalink":"https://antoineboucher.info/CV/blog/fr/tags/c4-model/"},{"name":"Homelab","permalink":"https://antoineboucher.info/CV/blog/fr/tags/homelab/"},{"name":"Kubernetes","permalink":"https://antoineboucher.info/CV/blog/fr/tags/kubernetes/"},{"name":"Proxmox","permalink":"https://antoineboucher.info/CV/blog/fr/tags/proxmox/"},{"name":"Terraform","permalink":"https://antoineboucher.info/CV/blog/fr/tags/terraform/"},{"name":"Cloudflare","permalink":"https://antoineboucher.info/CV/blog/fr/tags/cloudflare/"}],"tags":["PlantUML","C4 Model","Homelab","Kubernetes","Proxmox","Terraform","Cloudflare"],"tags_text":"PlantUML C4 Model Homelab Kubernetes Proxmox Terraform Cloudflare","thumb":"https://antoineboucher.info/CV/blog/posts/home-network-plantuml-c4/featured_hu_e114dedb52092234.jpeg","title":"Architecture réseau — de Lucidchart à PlantUML C4"},{"content":"Notes issues de la conférence Snowflake Data-for-Breakfast sur la plateforme de données cloud Snowflake, l’entrepôt de données, l’intégration et l’analytique — dont une keynote forte d’Infostrux.\nVue d’ensemble Enseignements clés Opérations de données à l’échelle mondiale Une étude de cas dans le secteur de la santé montrait Snowflake gérant des opérations de données sécurisées sur trois continents, en simplifiant le partage avec les partenaires tout en gardant haute disponibilité et SLA solides.\nLes plateformes de données cloud restent un socle pratique pour la consolidation et l’analytique ; cet événement offrait un bon aperçu de la trajectoire de Snowflake.\nAnalytique dans le secteur fiscal Un autre client devait stocker de très grands jeux de données et les analyser sans connaître toutes les questions à l’avance. Snowflake leur a donné un point unique pour consolider et transformer les données, ce qui a accéléré le diagnostic et amélioré la visibilité sur la traçabilité.\nSalles de données « clean room » Le modèle de clean room — collaborer sur des données partagées tout en préservant la confidentialité — apparaît pertinent lorsque deux entreprises doivent comparer des jeux lors d’une due diligence sans tout exposer.\nEn conclusion Snowflake Data-for-Breakfast valait le détour pour les plateformes de données cloud, les opérations à grande échelle et des schémas plus récents comme les clean rooms. À recommander si vous travaillez en gestion de données ou en analytique.","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/snowflake-data-for-breakfast/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/snowflake-data-for-breakfast/","post_kind":"conference","section":"posts","summary":"Retours sur la conférence Snowflake Data-for-Breakfast — plateforme cloud, entrepôt de données, intégration et analytique, avec une keynote marquante d’Infostrux.","tag_refs":[{"name":"Conference","permalink":"https://antoineboucher.info/CV/blog/fr/tags/conference/"},{"name":"Snowflake","permalink":"https://antoineboucher.info/CV/blog/fr/tags/snowflake/"},{"name":"Data Analytics","permalink":"https://antoineboucher.info/CV/blog/fr/tags/data-analytics/"}],"tags":["Conference","Snowflake","Data Analytics"],"tags_text":"Conference Snowflake Data Analytics","thumb":"https://antoineboucher.info/CV/blog/posts/snowflake-data-for-breakfast/featured_hu_ec748499759ede8b.jpeg","title":"Conférence Snowflake Data-for-Breakfast — points saillants"},{"content":"Mise à jour avril 2026 avec les chiffres Lens Insights actuels.\nÀ ce jour, mes lenses Snapchat totalisent 6,21 M de lectures, 12,11 M de vues, 616,4 k partages et 6 893 favoris (cumul, Lens Insights). Tout a commencé par un intérêt personnel pour les filtres RA et s’est transformé en missions payantes sur Fiverr en parallèle de mes expérimentations.\nEntre 2017 et 2020, j’ai publié 42 lenses pour moi-même et pour des clients. Parmi celles qui ont le plus servi figurent Go Crazy Facetime (~2,9 M de lectures), Face Ghosting (~1,2 M) et BIG SMILE (~520 k).\nLes outils d’audience de Snapchat donnent aussi l’échelle de l’écosystème : une audience potentielle d’environ 596 M à 623 M pour les utilisateurs de lenses, avec une forte présence sur des marchés comme l’Inde et les États-Unis, et un mix d’environ Android (~70 %) vs iOS (~30 %) — un bon rappel d’optimiser pour du matériel réel, pas seulement le téléphone sur le bureau.\nToutes les soumissions ne passent pas la modération — Snapchat et les clients disent parfois non — mais ces refus sont devenus des boucles de retour pour améliorer la lens suivante.\nJ’ai écrit un petit utilitaire pour transformer GIF ou vidéo en séquences PNG pour le pipeline ; d’autres créateurs Lens Studio s’en sont aussi servis. GIF/vidéo vers PNG pour Lens Studio\nC’était gratifiant de transformer les filtres RA en activité annexe. J’ai aussi échangé par téléphone avec Snapchat pour partager des retours produit sur Lens Studio et aider à améliorer l’outil pour les créateurs.\nJe veux aller plus loin sur Meta Spark ensuite. Le hub officiel est ici : Spark AR / Meta Spark learn — si vous connaissez des tutoriels qui se traduisent bien depuis une mentalité Lens Studio, je suis preneur de recommandations.","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/snapchat-lens-creator/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/snapchat-lens-creator/","post_kind":"article","section":"posts","summary":"Lenses Snapchat avec des millions de lectures et vues (2017–2020), clients Fiverr, outillage et retours sur Lens Studio.","tag_refs":[{"name":"Snapchat","permalink":"https://antoineboucher.info/CV/blog/fr/tags/snapchat/"},{"name":"Lens Studio","permalink":"https://antoineboucher.info/CV/blog/fr/tags/lens-studio/"},{"name":"AR","permalink":"https://antoineboucher.info/CV/blog/fr/tags/ar/"},{"name":"Side Project","permalink":"https://antoineboucher.info/CV/blog/fr/tags/side-project/"}],"tags":["Snapchat","Lens Studio","AR","Side Project"],"tags_text":"Snapchat Lens Studio AR Side Project","thumb":"https://antoineboucher.info/CV/blog/posts/snapchat-lens-creator/featured_hu_c2e6d1427b3f29c6.png","title":"Créateur de Lenses Snapchat"},{"content":"J’ai récemment participé à Expo Manger Santé 2023 à la Place des Congrès à Montréal. Les photos de l’événement sont signées OS7Media (os7mediamatrix@gmail.com) — merci pour les clichés utilisés ici.\nUne vente qui a porté ses fruits En tant que vendeur, je suis passionné par le goût riche des olives et leurs bienfaits. Sur deux jours, j’ai pu partager cette passion avec le public, ce qui s’est traduit par de très bonnes ventes, environ 300 $. Au-delà du chiffre, ce sont surtout les échanges et les histoires autour des olives qui comptaient.\nUn festin pour les sens Le salon était un paradis pour quiconque aime les produits frais. J’ai goûté fruits et légumes variés, mais les salades Gen V m’ont particulièrement marqué par leur croquant.\nProduits naturels haut de gamme Parmi les exposants, les produits BKind se démarquaient par leur qualité premium, avec des prix à la hauteur de l’image. C’est toujours intéressant de voir l’éventail des produits alignés avec un mode de vie plus sain.\nVoisins à retenir À côté de mon kiosque se trouvait Mate Libre, une marque qui a laissé une forte impression avec leurs boissons. Amateur de maté, j’ai été conquis par leurs propositions ; la matéina se démarquait par son profil de saveur.\nPour les matins chargés Une découverte que j’ai envie d’intégrer à ma routine : les mélanges pour smoothies Wise, versions verte et rouge. Parfaits quand il faut un boost nutritif sur le pouce.\nEn conclusion Expo Manger Santé 2023, c’était santé, goût et communauté. Je suis reconnaissant pour l’expérience et j’attends la prochaine édition.","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/expo-manger-sante-2023/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/expo-manger-sante-2023/","post_kind":"conference","section":"posts","summary":"Deux jours au salon montréalais de l’alimentation santé — ventes au kiosque d’olives, voisins comme Mate Libre et produits marquants.","tag_refs":[{"name":"Expo Manger Santé","permalink":"https://antoineboucher.info/CV/blog/fr/tags/expo-manger-sant%C3%A9/"},{"name":"Montreal","permalink":"https://antoineboucher.info/CV/blog/fr/tags/montreal/"},{"name":"Conference","permalink":"https://antoineboucher.info/CV/blog/fr/tags/conference/"},{"name":"Food","permalink":"https://antoineboucher.info/CV/blog/fr/tags/food/"},{"name":"Photography","permalink":"https://antoineboucher.info/CV/blog/fr/tags/photography/"}],"tags":["Expo Manger Santé","Montreal","Conference","Food","Photography"],"tags_text":"Expo Manger Santé Montreal Conference Food Photography","thumb":"https://antoineboucher.info/CV/blog/posts/expo-manger-sante-2023/featured_hu_10f9749020afe37b.jpeg","title":"Expo Manger Santé 2023 — olives, kiosques et découvertes"},{"content":"Les nouveautés de GitHub Copilot qui m\u0026rsquo;intéressent personnellement à voir prochainement.\nCopilot Chat et /createNotebook GitHub Copilot Chat avec /createNotebook permet de créer rapidement un carnet Jupyter à partir du code existant — utile pour la documentation et le prototypage, notamment pour les personnes en data science et en enseignement.\nGitHub Next GitHub Next regroupe les expérimentations et pistes d’innovation autour des produits GitHub.\nSession à Cédille Chez Cédille, nous avons assisté à une session avec GitHub et Arctiq centrée sur Copilot et les offres IA autour du développement.\nMerci aux intervenants Thierry Madkaud et Eldrick Wega pour les présentations.\nCes échanges renforcent le lien entre milieu académique et industrie. Merci aux participant·e·s et partenaires.\nÀ bientôt pour de prochains événements.","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/github-copilot-cedille-session/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/github-copilot-cedille-session/","post_kind":"conference","section":"posts","summary":"Session à Cédille avec GitHub et Arctiq — Copilot Chat, /createNotebook, et GitHub Next.","tag_refs":[{"name":"GitHub Copilot","permalink":"https://antoineboucher.info/CV/blog/fr/tags/github-copilot/"},{"name":"Conference","permalink":"https://antoineboucher.info/CV/blog/fr/tags/conference/"},{"name":"Cédille","permalink":"https://antoineboucher.info/CV/blog/fr/tags/c%C3%A9dille/"},{"name":"Arctiq","permalink":"https://antoineboucher.info/CV/blog/fr/tags/arctiq/"},{"name":"AI","permalink":"https://antoineboucher.info/CV/blog/fr/tags/ai/"}],"tags":["GitHub Copilot","Conference","Cédille","Arctiq","AI"],"tags_text":"GitHub Copilot Conference Cédille Arctiq AI","thumb":"https://antoineboucher.info/CV/blog/posts/github-copilot-cedille-session/featured_hu_4172ab0040a50f2f.jpeg","title":"Les nouveautés de GitHub Copilot"},{"content":"Ces notes viennent d’une comparaison pour le chat en direct sur site web, les chatbots et une boîte de réception partagée pour le support. Les produits ci-dessous ne sont pas interchangeables : certains sont des piles de communications complètes, d’autres de l’automatisation marketing, et l’un est une solution open source de type helpdesk. Les tarifs, canaux et fonctionnalités évoluent souvent—utilisez ce texte comme repère, puis vérifiez sur le site de chaque éditeur.\n3CX 3CX est surtout une offre UCaaS / PBX (téléphonie, réunions, postes). Le chat web en direct et les widgets associés s’inscrivent dans ce même écosystème, ce qui est pertinent si vous routez déjà la voix et le chat via 3CX et voulez un seul fournisseur pour les files et les agents.\nPour Live Chat and Talk (y compris les extensions CMS comme WordPress), suivez la procédure à jour dans la documentation officielle plutôt qu’une liste figée—les libellés et noms d’intégration changent entre versions. Point de départ : documentation 3CX.\nNotes (FR) : j’avais rédigé un guide de configuration pour le plugin Live Chat and Talk ; il doit être recoupé avec la doc officielle ci-dessus avant toute mise en production.\nConnexe : analytique et bots (pas spécifique à 3CX) Lien tangentiel au chat web, utile si vous explorez des scénarios Microsoft mêlant bots et analytique :\nYouTube — Microsoft ChatBot (Power BI) ManyChat ManyChat sert surtout au marketing conversationnel et à l’automatisation, avec une orientation forte vers Meta (Instagram/Facebook) : diffusions, séquences et capture de leads.\nAdapté aux campagnes et entonnoirs sur les réseaux sociaux ; moins comme helpdesk multicanal neutre avec ticketing poussé et SLA sur e-mail, chat et téléphonie dans un même produit open-core.\nTarification ManyChat Kommunicate Kommunicate vise la collaboration humain + bot : le bot ouvre la conversation, puis transfère vers des agents sur le site et les canaux de messagerie courants. C’est du SaaS géré—vous intégrez et configurez plutôt que d’exploiter la pile vous-même.\nUtile si vous voulez un bot type plateforme NLU et une interface agent sans self-hosting ; comparez le coût total et la résidence des données aux options auto-hébergées si c’est un critère pour votre organisation.\nChatwoot Chatwoot est une suite open source d’engagement client (licence AGPL). Vous pouvez utiliser Chatwoot Cloud ou l’auto-héberger (Docker et autres chemins documentés pour les opérations).\nConceptuellement, c’est plutôt « boîte de réception partagée + conversations omnicanales » qu’un PBX ou un bot marketing pur :\nInbox unifiée pour widget web, courriel et autres canaux (la liste exacte évolue—voir leur documentation). Équipes, étiquettes, automatisations et historique orientés support et suivi commercial. API et webhooks pour intégrations et flux sur mesure. Compromis : l’auto-hébergement donne la maîtrise et peut réduire le coût par siège, mais vous assumez sauvegardes, mises à jour et sécurité. La profondeur fonctionnelle par rapport aux grandes suites propriétaires dépend du canal ; vérifiez vos besoins (voix, CRM précis, etc.) sur leur feuille de route et leurs docs.\nLiens :\nChatwoot sur GitHub Chatwoot (aperçu produit) Documentation développeur Chatwoot (API, installation, auto-hébergement) Comparaison rapide Cœur de métier Déploiement typique Open source 3CX Téléphonie + UC + chat web dans une même pile PBX cloud ou sur site + agents Non ManyChat Automatisation marketing, souvent Meta d’abord SaaS Non Kommunicate Transfert bot → humain, CX géré SaaS Non Chatwoot Inbox omnicanale, orientée support SaaS (cloud) ou auto-hébergé Oui (AGPL) Ressources Documentation 3CX Tarification ManyChat Kommunicate Chatwoot Chatwoot — GitHub Chatwoot — documentation développeur YouTube — Microsoft ChatBot (Power BI) ","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/livechat-platform-notes/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/livechat-platform-notes/","post_kind":"article","section":"posts","summary":"En quoi 3CX, ManyChat, Kommunicate et Chatwoot diffèrent pour le chat web, les bots et les boîtes de réception d’équipe—avec liens et tableau comparatif.","tag_refs":[{"name":"Live Chat","permalink":"https://antoineboucher.info/CV/blog/fr/tags/live-chat/"},{"name":"Chatbot","permalink":"https://antoineboucher.info/CV/blog/fr/tags/chatbot/"},{"name":"Customer Support","permalink":"https://antoineboucher.info/CV/blog/fr/tags/customer-support/"},{"name":"Open Source","permalink":"https://antoineboucher.info/CV/blog/fr/tags/open-source/"},{"name":"3CX","permalink":"https://antoineboucher.info/CV/blog/fr/tags/3cx/"},{"name":"Chatwoot","permalink":"https://antoineboucher.info/CV/blog/fr/tags/chatwoot/"}],"tags":["Live Chat","Chatbot","Customer Support","Open Source","3CX","Chatwoot"],"tags_text":"Live Chat Chatbot Customer Support Open Source 3CX Chatwoot","thumb":"/CV/blog/images/post-kind-article.png","title":"Plateformes de chat en direct et de support (3CX, ManyChat, Kommunicate, Chatwoot)"},{"content":"GitHub : https://lnkd.in/en3dSVuQ\nURL du plugin : https://lnkd.in/exVNZMnT\nD2COpenAIPlugin est un plugin pour ChatGPT qui permet de générer des diagrammes avec PlantUML, Mermaid et D2. Il enrichit ChatGPT en offrant un moyen fluide de créer des diagrammes variés.\nPour un flux prompt dans le chat (modèle AIPRM, exemples de séquences cache hit/miss, astuces d’outils canvas), voir Diagrammes avec ChatGPT et AIPRM — complémentaire à cette approche par plugin.\n🤖 ChatGPT UML Plugins - DEMO\nPrise en charge UML : générer des diagrammes UML à la volée pour représenter visuellement vos modèles d’IA et faciliter la compréhension des idées complexes.\nDocumentation étendue : un guide qui couvre l’installation jusqu’à l’usage, pour une expérience fluide quel que soit le niveau.\nGitHub : dépôt\nRetours et contributions bienvenus.","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/d2c-openai-diagram-plugin/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/d2c-openai-diagram-plugin/","post_kind":"article","section":"posts","summary":"Plugin ChatGPT pour générer des diagrammes PlantUML, Mermaid et D2 à partir de la conversation.","tag_refs":[{"name":"ChatGPT","permalink":"https://antoineboucher.info/CV/blog/fr/tags/chatgpt/"},{"name":"OpenAI","permalink":"https://antoineboucher.info/CV/blog/fr/tags/openai/"},{"name":"Plugin","permalink":"https://antoineboucher.info/CV/blog/fr/tags/plugin/"},{"name":"PlantUML","permalink":"https://antoineboucher.info/CV/blog/fr/tags/plantuml/"},{"name":"Mermaid","permalink":"https://antoineboucher.info/CV/blog/fr/tags/mermaid/"},{"name":"D2","permalink":"https://antoineboucher.info/CV/blog/fr/tags/d2/"}],"tags":["ChatGPT","OpenAI","Plugin","PlantUML","Mermaid","D2"],"tags_text":"ChatGPT OpenAI Plugin PlantUML Mermaid D2","thumb":"https://antoineboucher.info/CV/blog/posts/d2c-openai-diagram-plugin/featured.gif","title":"Plugin OpenAI D2C — diagrammes PlantUML, Mermaid et D2"},{"content":"L’extension navigateur AIPRM ajoute des modèles de prompts réutilisables dans ChatGPT. Avec un prompt structuré (type de diagramme, ce qu’il faut représenter, pourquoi, et quel outil), vous obtenez des réponses cohérentes — que vous visiez du texte (PlantUML, Mermaid) ou une marche à suivre pour un outil graphique.\nArticle complet en anglais (même slug — vous pouvez aussi choisir EN dans l’en-tête du site).\nGabarit de prompt AIPRM (à copier et adapter) Une ligne par dimension. Collez le bloc dans ChatGPT (avec ou sans AIPRM) et modifiez les valeurs entre crochets.\n[DIAGRAM TYPE] - Sequence | Use Case | Class | Activity | Component | State | Object | Deployment | Timing | Network | Wireframe | Archimate | Gantt | MindMap | WBS | JSON | YAML [ELEMENT TYPE] - Actors | Messages | Objects | Classes | Interfaces | Components | States | Nodes | Edges | Links | Frames | Constraints | Entities | Relationships | Tasks | Events | Modules [PURPOSE] - Communication | Planning | Design | Analysis | Modeling | Documentation | Implementation | Testing | Debugging (optionnel : précisez la stack ou le scénario, ex. « Communication : frontend React server — backend FastAPI — cache Redis — base MongoDB ») [DIAGRAMMING TOOL] - PlantUML | Mermaid | Draw.io | Lucidchart | Creately | Gliffy Exemple : diagramme de séquence pour une API avec cache [DIAGRAM TYPE] - Sequence [ELEMENT TYPE] - Messages [PURPOSE] - Communication Frontend React Server - Backend FastAPI - Cache Redis - Database MongoDB [DIAGRAMMING TOOL] - PlantUML Lien public AIPRM Prompt publié dans la bibliothèque AIPRM : lien LinkedIn. Utilisez-le comme point de départ, puis affinez [PURPOSE] et [DIAGRAMMING TOOL] selon votre stack et vos livrables.\nLes diagrammes de séquence en bref Les diagrammes de séquence UML montrent les messages échangés dans le temps entre composants. Ils servent à l’onboarding, aux revues de conception et à documenter un parcours requête (surtout quand un cache ou une base est sur le chemin critique).\nCommunication front–back avec mise en cache L’illustration ci-dessous : une requête utilisateur traverse un front React et un back FastAPI, avec Redis comme cache et MongoDB comme source de vérité. Les extraits de code incluent une branche cache hit et une cache miss.\nPlantUML (hit et miss) @startuml actor User participant \u0026#34;ReactServer\u0026#34; as RS participant \u0026#34;FastAPIServer\u0026#34; as API participant \u0026#34;RedisCache\u0026#34; as R database \u0026#34;MongoDB\u0026#34; as M User -\u0026gt; RS: Sends Request RS -\u0026gt; API: Forwards Request alt Cache hit API -\u0026gt; R: Check Cache R --\u0026gt; API: Found Data API -\u0026gt; RS: Sends Response from Cache RS -\u0026gt; User: Returns Response from Cache else Cache miss API -\u0026gt; R: Get Data from Cache R --\u0026gt; API: Data Not Found API -\u0026gt; M: Get Data from DB M --\u0026gt; API: Returns Data API -\u0026gt; R: Save Data in Cache R --\u0026gt; API: Data Saved API -\u0026gt; RS: Sends Response RS -\u0026gt; User: Returns Response end @enduml Mermaid (hit et miss) sequenceDiagram actor User participant ReactServer participant FastAPIServer participant RedisCache participant MongoDB User-\u0026gt;\u0026gt;ReactServer: Sends Request ReactServer-\u0026gt;\u0026gt;FastAPIServer: Forwards Request alt Cache hit FastAPIServer-\u0026gt;\u0026gt;RedisCache: Check Cache RedisCache--\u0026gt;\u0026gt;FastAPIServer: Found Data FastAPIServer-\u0026gt;\u0026gt;ReactServer: Sends Response from Cache ReactServer-\u0026gt;\u0026gt;User: Returns Response from Cache else Cache miss FastAPIServer-\u0026gt;\u0026gt;RedisCache: Get Data from Cache RedisCache--\u0026gt;\u0026gt;FastAPIServer: Data Not Found FastAPIServer-\u0026gt;\u0026gt;MongoDB: Get Data from DB MongoDB--\u0026gt;\u0026gt;FastAPIServer: Returns Data FastAPIServer-\u0026gt;\u0026gt;RedisCache: Save Data in Cache RedisCache--\u0026gt;\u0026gt;FastAPIServer: Data Saved FastAPIServer-\u0026gt;\u0026gt;ReactServer: Sends Response ReactServer-\u0026gt;\u0026gt;User: Returns Response end Draw.io, Lucidchart, Creately et Gliffy Ce sont des outils orientés canevas. Souvent le plus rapide est de faire générer PlantUML ou Mermaid dans ChatGPT, puis :\nExporter depuis un serveur ou la CLI PlantUML en SVG ou PNG et importer ce graphique comme calque de base, ou Demander à ChatGPT (avec le gabarit) une liste numérotée des lifelines et des messages, et les reproduire avec les formes et connecteurs de l’outil. Vous évitez la page blanche tout en gardant la main sur le style et les annotations.\nConclusion Un prompt structuré rend le diagramme reproductible : type de diagramme, éléments mis en avant, objectif (et contexte), et outil cible. AIPRM ne fait qu’accélérer l’accès à ce gabarit une fois qu’il est dans votre bibliothèque.\nHashtags pour le partage : #ChatGPT #diagramme #UML #logiciel #outilsdedesign #PlantUML #Mermaid #Drawio #Lucidchart #Creately #Gliffy","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/chatgpt-airprm-sequence-diagrams/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/chatgpt-airprm-sequence-diagrams/","post_kind":"article","section":"posts","summary":"Gabarit de prompt AIPRM : type de diagramme, éléments, objectif et outil — avec exemples PlantUML et Mermaid (React, FastAPI, Redis, MongoDB, cache hit/miss).","tag_refs":[{"name":"ChatGPT","permalink":"https://antoineboucher.info/CV/blog/fr/tags/chatgpt/"},{"name":"AIPRM","permalink":"https://antoineboucher.info/CV/blog/fr/tags/aiprm/"},{"name":"PlantUML","permalink":"https://antoineboucher.info/CV/blog/fr/tags/plantuml/"},{"name":"Mermaid","permalink":"https://antoineboucher.info/CV/blog/fr/tags/mermaid/"},{"name":"UML","permalink":"https://antoineboucher.info/CV/blog/fr/tags/uml/"},{"name":"Diagram Tools","permalink":"https://antoineboucher.info/CV/blog/fr/tags/diagram-tools/"}],"tags":["ChatGPT","AIPRM","PlantUML","Mermaid","UML","Diagram Tools"],"tags_text":"ChatGPT AIPRM PlantUML Mermaid UML Diagram Tools","thumb":"https://antoineboucher.info/CV/blog/posts/chatgpt-airprm-sequence-diagrams/featured_hu_24e71c4c1e6bb9a2.jpeg","title":"Prompts de diagrammes avec ChatGPT et AIPRM (PlantUML, Mermaid, etc.)"},{"content":"Notes du webinaire Run:ai sur l’exécution et la montée en charge des charges d’inférence sur AWS (Amériques). Run:ai met l’accent sur l’ordonnancement, la visibilité et l’efficacité pour les modèles sur GPU dans des environnements partagés.\nTableau de bord Vue d’ensemble des jobs et de l’usage des ressources.\nCLI Opérations et automatisation en ligne de commande.\nModèles et charge Gestion des charges Vue infrastructure Démo Défis Pour le détail produit, voir la documentation officielle Run:ai et les offres AWS marketplace ou partenaires.","date":"2022-09-06","date_unix":1662472800,"id":"https://antoineboucher.info/CV/blog/fr/posts/runai-aws-inference-webinar/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/runai-aws-inference-webinar/","post_kind":"conference","section":"posts","summary":"Croquis issus du webinaire Run:ai sur la montée en charge de l’inférence ML sur AWS — tableaux de bord, CLI et vue cluster.","tag_refs":[{"name":"Run:ai","permalink":"https://antoineboucher.info/CV/blog/fr/tags/runai/"},{"name":"AWS","permalink":"https://antoineboucher.info/CV/blog/fr/tags/aws/"},{"name":"Machine Learning","permalink":"https://antoineboucher.info/CV/blog/fr/tags/machine-learning/"},{"name":"Inference","permalink":"https://antoineboucher.info/CV/blog/fr/tags/inference/"},{"name":"Kubernetes","permalink":"https://antoineboucher.info/CV/blog/fr/tags/kubernetes/"},{"name":"Conference","permalink":"https://antoineboucher.info/CV/blog/fr/tags/conference/"}],"tags":["Run:ai","AWS","Machine Learning","Inference","Kubernetes","Conference"],"tags_text":"Run:ai AWS Machine Learning Inference Kubernetes Conference","thumb":"https://antoineboucher.info/CV/blog/posts/runai-aws-inference-webinar/featured_hu_cd252b8aa2faa5de.jpeg","title":"Run:ai sur AWS — notes de webinaire (inférence et autoscaling)"},{"content":"Inspiration : le « Blueprint Protocol » de Bryan Johnson Pour moi, le suivi de santé personnel a commencé avec le Blueprint Protocol de Bryan Johnson — une poussée vers l’auto-quantification qui collait à ma façon de voir la forme. Je voulais le même niveau de granularité ; une balance Renpho avec bio-impédance s’est avérée un moyen pratique d’obtenir plus que le simple poids.\nFork de hass-renpho et écosystème Home Assistant J’ai trouvé hass-renpho, une intégration communautaire qui ramène les données Renpho dans Home Assistant. Le projet était à l’arrêt ; avec le mainteneur d’origine indisponible, j’ai forké pour étendre la prise en charge des métriques exposées par le matériel.\nCe fork m’a fait suivre le parcours classique des composants personnalisés : installation via HACS, configuration des identifiants, itération sur les entités. J’ai aussi échangé avec le mainteneur d’origine quand c’était pertinent — correctifs, retours sur l’API, garder l’intégration utile pour d’autres utilisateurs de balance Renpho.\nRétro-ingénierie et APKLeaks L’app mobile ne publie pas de doc API officielle ; il fallait voir ce que le client Android appelle réellement. APKLeaks parcourt l’APK empaqueté à la recherche de chaînes — URL, clés, indices — plutôt que de tout décompiler en source lisible. En l’exécutant sur l’APK Renpho, j’ai obtenu les points HTTP et assez de contexte pour aligner ces appels sur les charges JSON utiles (poids, IMC, métabolisme de base, âge corporel, estimations graisse et muscle, eau, protéines, graisse viscérale, etc.). Dans Home Assistant, tout cela devient des entités et alimente des cartes Lovelace — jauges pour la composition, graphes d’historique pour le poids, lignes simples pour les métriques « bonus ».\n# Installation PyPI simple pip3 install apkleaks # Explorer les sources git clone https://github.com/dwisiswant0/apkleaks cd apkleaks/ pip3 install -r requirements.txt Pour aller plus loin :\nAPKLeaks sur GitHub Analyse approfondie d’APKLeaks Tableau de bord, contexte et habitudes de mesure Les données Renpho ne font qu’une partie du tableau. J’utilise aussi Google Health, MyFitnessPal, etc., pour l’activité et l’alimentation, afin que les pesées côtoient régime et mouvement, pas isolées.\nLes variations jour après jour m’ont appris à traiter les chiffres comme des tendances, pas des jugements. Les vêtements seuls peuvent faire bouger la balance d’environ un kilo « mauvais jour » ; hydratation et digestion comptent aussi. Mesurer à un moment stable (pour moi, le matin, conditions comparables) garde la série exploitable sur la carte d’historique HA.\nLe projet technique est devenu une habitude : un seul endroit pour la trajectoire du poids, les estimations de composition et les stats exposées par l’intégration — assez pour voir si entraînement ou sommeil réagissent comme prévu.\nCommunauté et ce qui vous convient Rien de tout cela ne serait aussi pratique sans l’écosystème Home Assistant et les intégrations open source — forks, tickets et petits correctifs s’additionnent. Si vous quantifiez votre santé, qu’est-ce qui a vraiment tenu chez vous : matériel dédié, apps téléphone, ou solution auto-hébergée comme celle-ci ? Partager ce qu’on utilise et ce qu’on ignore aide souvent plus que le gadget seul : l’essentiel est que les données s’intègrent régulièrement à la routine.","date":"2021-10-10","date_unix":1633874400,"id":"https://antoineboucher.info/CV/blog/fr/posts/renpho-health-api-blueprint/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/renpho-health-api-blueprint/","post_kind":"article","section":"posts","summary":"Fork de hass-renpho, extraction des points de terminaison Renpho avec APKLeaks, et intégration des métriques bio-impédancemétriques dans un tableau de bord santé Lovelace Home Assistant.","tag_refs":[{"name":"Health","permalink":"https://antoineboucher.info/CV/blog/fr/tags/health/"},{"name":"API","permalink":"https://antoineboucher.info/CV/blog/fr/tags/api/"},{"name":"Reverse Engineering","permalink":"https://antoineboucher.info/CV/blog/fr/tags/reverse-engineering/"},{"name":"Home Assistant","permalink":"https://antoineboucher.info/CV/blog/fr/tags/home-assistant/"},{"name":"Home Automation","permalink":"https://antoineboucher.info/CV/blog/fr/tags/home-automation/"}],"tags":["Health","API","Reverse Engineering","Home Assistant","Home Automation"],"tags_text":"Health API Reverse Engineering Home Assistant Home Automation","thumb":"https://antoineboucher.info/CV/blog/posts/renpho-health-api-blueprint/featured_hu_c41549a9ce6c2349.jpeg","title":"Balance Renpho, Home Assistant et rétro-ingénierie de l’API"},{"content":"Introduction Bienvenue dans un nouveau chapitre du blog : les détails de la mise en place d’un réseau domestique solide. En tant qu’ingénieur logiciel passionné par les protocoles et le calcul efficace, j’ai voulu un système qui équilibre performance, sécurité et coût. Ce billet raconte l’expérience et les choix techniques.\nRelever le défi du réseau maison Mon intérêt pour le réseau remonte aux études (IP, VPN, etc.). Face au coût du cloud, j’ai visé une solution locale avec du matériel ancien, pour limiter dépenses hardware et services en ligne tout en gardant une bonne fonctionnalité.\nConteneurs Docker : polyvalence Une part importante du projet : explorer Docker et des services gratuits pouvant remplacer le cloud (scan de documents, gestion du foyer, etc.). Cette exploration a servi de base pour comprendre l’architecture conteneurisée.\nBash et serveurs à la maison Le cœur du système : un serveur Dell acheté à bas prix et un vieux PC de 2004. J’ai écrit plusieurs scripts bash pour Ubuntu, CentOS et Proxmox afin d’installer et gérer les machines — automatisation et scripting au centre du réseau domestique.\nObstacles et apprentissage La maintenance a montré l’importance d’un hyperviseur adapté. J’ai fini par utiliser un serveur dédié à la virtualisation pour stabiliser et simplifier l’ensemble.\nGestion à distance et sécurité Il fallait pouvoir administrer à distance et surveiller santé des services et du matériel. J’ai mis un reverse proxy Caddy et masqué mon IP derrière un serveur OVH pour réduire les risques et mieux router le trafic.\nWireGuard comme VPN Pour le VPN, choix de WireGuard : rapide et fiable une fois la config maîtrisée. J’ai aussi contribué à des projets pour simplifier son déploiement.\nÉlargir le réseau Après un déménagement, extension multi-sites avec Lucidchart pour visualiser l’architecture et Proxmox pour de nombreuses VMs — aussi utilisé dans un projet de club pour partager l’expérience.\nProjets futurs Envisager un site statique sur AWS S3 pour réduire les coûts de déploiement, et poursuivre l’usage de GitHub pour les projets personnels.\nConclusion Ce parcours mélange passion personnelle et développement pro. L’équilibre performance / sécurité / coût est au centre ; avec les bons outils, un réseau maison efficace est tout à fait réalisable et gratifiant.","date":"2021-09-06","date_unix":1630936800,"id":"https://antoineboucher.info/CV/blog/fr/posts/home-networking-evolution/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/home-networking-evolution/","post_kind":"article","section":"posts","summary":"Docker sur matériel réutilisé, automatisation bash, Caddy, WireGuard, VMs Proxmox et schémas de l’installation.","tag_refs":[{"name":"Networking","permalink":"https://antoineboucher.info/CV/blog/fr/tags/networking/"},{"name":"Homelab","permalink":"https://antoineboucher.info/CV/blog/fr/tags/homelab/"},{"name":"Docker","permalink":"https://antoineboucher.info/CV/blog/fr/tags/docker/"},{"name":"WireGuard","permalink":"https://antoineboucher.info/CV/blog/fr/tags/wireguard/"},{"name":"Proxmox","permalink":"https://antoineboucher.info/CV/blog/fr/tags/proxmox/"},{"name":"Caddy","permalink":"https://antoineboucher.info/CV/blog/fr/tags/caddy/"}],"tags":["Networking","Homelab","Docker","WireGuard","Proxmox","Caddy"],"tags_text":"Networking Homelab Docker WireGuard Proxmox Caddy","thumb":"https://antoineboucher.info/CV/blog/posts/home-networking-evolution/featured_hu_1afa95bb5ff7362e.png","title":"Évolution du réseau — construire un lab à la maison"},{"content":"Petites démos CodePen (intégrations ci-dessous) et anciens billets regroupés ici : jQuery/CSS, getDisplayMedia, et autres notes front-end — un seul favori à garder.\nPetites démos BMdzwx Ouvrir sur CodePen\nVue détails (xMXNyy) Ouvrir sur CodePen\nJxrqQx Ouvrir sur CodePen\nbyVQKJ Ouvrir sur CodePen\njjzxER Ouvrir sur CodePen\nqzQpYg Ouvrir sur CodePen\nZEENwWB Ouvrir sur CodePen\nTableau noir HTML5 Canvas (tutoriel) Introduction Ce tutoriel parcourt un tableau noir interactif avec Canvas HTML5 et JavaScript : dessin, import d’images via l’API File, effacement. C’est le style des pas-à-pas CodePen du milieu des années 2010. L’intégration ci-dessus montre le comportement final.\nMise en place du canvas \u0026lt;canvas id=\u0026#34;drawingCanvas\u0026#34;\u0026gt;\u0026lt;/canvas\u0026gt; html, body { width: 100%; height: 100%; overflow: hidden; margin: 0; padding: 0; background: hsla(0, 5%, 5%, 1); } canvas { background: hsla(0, 5%, 5%, 1); } Contrôles \u0026lt;input type=\u0026#34;color\u0026#34; id=\u0026#34;colorPicker\u0026#34; value=\u0026#34;#FFFFFF\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;range\u0026#34; id=\u0026#34;penSize\u0026#34; min=\u0026#34;1\u0026#34; max=\u0026#34;20\u0026#34; value=\u0026#34;5\u0026#34;\u0026gt; \u0026lt;button id=\u0026#34;saveImage\u0026#34;\u0026gt;Save Image\u0026lt;/button\u0026gt; \u0026lt;button id=\u0026#34;eraseCanvas\u0026#34;\u0026gt;Erase\u0026lt;/button\u0026gt; \u0026lt;input type=\u0026#34;file\u0026#34; id=\u0026#34;imageLoader\u0026#34; name=\u0026#34;imageLoader\u0026#34; accept=\u0026#34;image/*\u0026#34;\u0026gt; #colorPicker, #penSize, #saveImage, #eraseCanvas, #imageLoader { position: absolute; top: 10px; z-index: 1000; } #colorPicker { right: 40px; } #penSize { right: 120px; } #eraseCanvas { right: 275px; } #saveImage { right: 350px; } #imageLoader { right: 400px; } Logique de dessin let canvas, ctx; let isDrawing = false, isDragging = false; let curColor = \u0026#39;#FFFFFF\u0026#39;; let lineWidth = 5; let imageObjects = [], drawingObjects = []; let currentDraggingImg = null; window.onload = function() { canvas = document.getElementById(\u0026#39;drawingCanvas\u0026#39;); canvas.width = window.innerWidth; canvas.height = window.innerHeight; ctx = canvas.getContext(\u0026#34;2d\u0026#34;); ctx.lineWidth = lineWidth; canvas.addEventListener(\u0026#39;mousedown\u0026#39;, onMouseDown); canvas.addEventListener(\u0026#39;mousemove\u0026#39;, onMouseMove); canvas.addEventListener(\u0026#39;mouseup\u0026#39;, onMouseUp); document.getElementById(\u0026#39;colorPicker\u0026#39;).addEventListener(\u0026#39;input\u0026#39;, function(e) { curColor = e.target.value; }); document.getElementById(\u0026#39;penSize\u0026#39;).addEventListener(\u0026#39;input\u0026#39;, function(e) { lineWidth = e.target.value; }); document.getElementById(\u0026#39;saveImage\u0026#39;).addEventListener(\u0026#39;click\u0026#39;, saveImage); document.getElementById(\u0026#39;imageLoader\u0026#39;).addEventListener(\u0026#39;change\u0026#39;, loadImage); }; function onMouseDown(e) { const mouseX = e.pageX - canvas.offsetLeft; const mouseY = e.pageY - canvas.offsetTop; currentDraggingImg = null; imageObjects.forEach(imgObj =\u0026gt; { if (mouseX \u0026gt;= imgObj.x \u0026amp;\u0026amp; mouseX \u0026lt;= imgObj.x + imgObj.width \u0026amp;\u0026amp; mouseY \u0026gt;= imgObj.y \u0026amp;\u0026amp; mouseY \u0026lt;= imgObj.y + imgObj.height) { imgObj.isDragging = true; currentDraggingImg = imgObj; isDragging = true; } }); if (!currentDraggingImg) { isDrawing = true; const path = { color: curColor, lineWidth: lineWidth, points: [{x: mouseX, y: mouseY}] }; drawingObjects.push(path); } } function onMouseMove(e) { const mouseX = e.pageX - canvas.offsetLeft; const mouseY = e.pageY - canvas.offsetTop; if (isDragging \u0026amp;\u0026amp; currentDraggingImg) { currentDraggingImg.x = mouseX; currentDraggingImg.y = mouseY; redrawCanvas(); } else if (isDrawing) { const currentPath = drawingObjects[drawingObjects.length - 1]; currentPath.points.push({x: mouseX, y: mouseY}); redrawCanvas(); } } function onMouseUp() { if (isDragging \u0026amp;\u0026amp; currentDraggingImg) { currentDraggingImg.isDragging = false; } isDrawing = isDragging = false; } Au chargement on dimensionne le canvas, on récupère le contexte 2D et on branche mousedown / mousemove / mouseup. Chaque trait est un objet dans drawingObjects ; redrawCanvas dessine d’abord les images importées, puis relie les points de chaque chemin.\nImport d’images et effacement function loadImage(e) { var reader = new FileReader(); reader.onload = function(event) { var img = new Image(); img.onload = function() { imageObjects.push({ img: img, x: 0, y: 0, width: img.width, height: img.height, isDragging: false }); redrawCanvas(); }; img.src = event.target.result; }; reader.readAsDataURL(e.target.files[0]); }; function redrawCanvas() { ctx.clearRect(0, 0, canvas.width, canvas.height); imageObjects.forEach(imgObj =\u0026gt; { ctx.drawImage(imgObj.img, imgObj.x, imgObj.y); }); drawingObjects.forEach(path =\u0026gt; { ctx.beginPath(); ctx.strokeStyle = path.color; ctx.lineWidth = path.lineWidth; path.points.forEach((point, index) =\u0026gt; { if (index === 0) { ctx.moveTo(point.x, point.y); } else { ctx.lineTo(point.x, point.y); } }); ctx.stroke(); }); } function saveImage() { var image = canvas.toDataURL(\u0026#34;image/png\u0026#34;).replace(\u0026#34;image/png\u0026#34;, \u0026#34;image/octet-stream\u0026#34;); var link = document.createElement(\u0026#39;a\u0026#39;); link.download = \u0026#39;canvas-drawing.png\u0026#39;; link.href = image; link.click(); } document.getElementById(\u0026#39;eraseCanvas\u0026#39;).addEventListener(\u0026#39;click\u0026#39;, eraseCanvas); function eraseCanvas() { ctx.clearRect(0, 0, canvas.width, canvas.height); imageObjects = []; drawingObjects = []; } FileReader.readAsDataURL décode le fichier choisi ; au chargement de l’image on pousse un objet déplaçable dans imageObjects. eraseCanvas vide le canvas et réinitialise les deux tableaux.\nConclusion Vous disposez d’un tableau fonctionnel sur Canvas. Forkez sur CodePen et étendez — autres pinceaux, annulation, pression : les API sont les mêmes.\nVague Three.js (tutoriel) Introduction Ce tutoriel construit une animation de vague 3D avec Three.js : scène, caméra, renderer WebGL, grille de cubes et un mouvement ondulant simple — le genre de démo « regardez, du WebGL dans le navigateur » des articles Three.js + CodePen du milieu des années 2010. Utilisez l’intégration ci-dessus pour modifier en direct.\nMise en place de la scène let cubes = []; let noiseOffset = 0; const size = 20; const step = 2; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById(\u0026#34;container\u0026#34;).appendChild(renderer.domElement); camera.position.z = 50; camera.position.y = 20; camera.lookAt(0, 0, 0); Cubes avec bruit type Perlin function noise(x, y, z) { return Math.sin(x) * Math.cos(y) * Math.sin(z); } for (let x = -size; x \u0026lt;= size; x += step) { for (let z = -size; z \u0026lt;= size; z += step) { const y = noise(x * 0.1, noiseOffset, z * 0.1) * 10; const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); cube.position.set(x, y, z); scene.add(cube); cubes.push(cube); } noiseOffset += 0.1; } Animation let waveOffset = 0; function animate() { requestAnimationFrame(animate); updateCameraPosition(); cubes.forEach((cube, i) =\u0026gt; { cube.position.y = noise(cube.position.x * 0.1, waveOffset, cube.position.z * 0.1) * 10; }); waveOffset += 0.01; renderer.render(scene, camera); } animate(); Interaction (souris, clavier, manette, molette) let isDragging = false; let prevX = 0; let prevY = 0; document.addEventListener(\u0026#34;mousedown\u0026#34;, function (e) { isDragging = true; prevX = e.clientX; prevY = e.clientY; }); document.addEventListener(\u0026#34;mouseup\u0026#34;, function () { isDragging = false; }); document.addEventListener(\u0026#34;mousemove\u0026#34;, function (e) { if (isDragging) { const dx = e.clientX - prevX; const dy = e.clientY - prevY; camera.rotation.y += dx * 0.01; camera.rotation.x += dy * 0.01; prevX = e.clientX; prevY = e.clientY; } }); function gamepadControl() { const gamepads = navigator.getGamepads(); if (gamepads[0]) { const gp = gamepads[0]; camera.position.z -= gp.buttons[0].value * 0.1; camera.position.z += gp.buttons[1].value * 0.1; camera.position.x -= gp.buttons[2].value * 0.1; camera.position.x += gp.buttons[3].value * 0.1; } requestAnimationFrame(gamepadControl); } gamepadControl(); let keyStates = {}; document.addEventListener(\u0026#34;keydown\u0026#34;, function (event) { keyStates[event.code] = true; }); document.addEventListener(\u0026#34;keyup\u0026#34;, function (event) { keyStates[event.code] = false; }); function updateCameraPosition() { if (keyStates[\u0026#34;ArrowUp\u0026#34;]) camera.position.z -= 0.1; if (keyStates[\u0026#34;ArrowDown\u0026#34;]) camera.position.z += 0.1; if (keyStates[\u0026#34;ArrowLeft\u0026#34;]) camera.position.x -= 0.1; if (keyStates[\u0026#34;ArrowRight\u0026#34;]) camera.position.x += 0.1; if (keyStates[\u0026#34;KeyW\u0026#34;]) camera.rotation.x -= 0.01; if (keyStates[\u0026#34;KeyS\u0026#34;]) camera.rotation.x += 0.01; if (keyStates[\u0026#34;KeyA\u0026#34;]) camera.rotation.y += 0.01; if (keyStates[\u0026#34;KeyD\u0026#34;]) camera.rotation.y -= 0.01; } document.addEventListener(\u0026#34;wheel\u0026#34;, function (e) { camera.position.z += e.deltaY * 0.01; }); Conclusion Vous avez une vague animée de base dans Three.js. Forkez le pen et poussez le mouvement, les matériaux ou la caméra — le pipeline est le même depuis ces tutoriels d’intro WebGL.\nAutres tutoriels front-end (hors CodePen) Anciens articles fusionnés ici : texte et extraits de code uniquement (pas d’iframe CodePen).\nBarre de progression par étapes responsive (jQuery + CSS) Ce code crée une barre de progression responsive en quatre étapes. Il utilise jQuery pour changer dynamiquement le texte et CSS pour le style. Voici le détail du fonctionnement.\nStructure HTML La barre est dans un div avec la classe progressbar_container. Une liste non ordonnée ul avec la classe progressbar représente la barre. Chaque étape est un li avec la classe progressbar_node. L’étape courante est mise en avant avec la classe current_node. Style CSS .progressbar_container positionne la barre, gère sa taille et la centre. Chaque .progressbar_node représente une étape. Le pseudo-élément :before de .progressbar_node dessine les pastilles numérotées. Le pseudo-élément :after trace les traits entre les étapes. Les étapes courantes et complétées ont une couleur plus foncée et une bordure pleine. Comportement JavaScript Au $(document).ready, les étapes précédant la courante sont marquées complétées avec activated_node. Un écouteur $(window).resize modifie le texte de la première étape selon la largeur de la fenêtre, en basculant entre « PASSENGER » et « PASSENGER DETAILS ». Remarques Incluez la bibliothèque jQuery pour utiliser cette syntaxe. Le redimensionnement aide la responsivité sur différentes largeurs d’écran. Utilisation Incluez le HTML fourni, liez le CSS et chargez jQuery pour que le script fonctionne.\nAméliorations possibles Ajuster le nombre d’étapes en modifiant le HTML et éventuellement le CSS. Ajouter des attributs ARIA pour l’accessibilité et les lecteurs d’écran. Remplacer le JS par des media queries pour certains changements de texte. Cette barre convient bien pour représenter visuellement une suite d’étapes (paiement, inscription, etc.).\nCapture d’écran dans le navigateur (getDisplayMedia) Tutoriel : utilitaire de capture d’écran avec HTML, CSS et JavaScript Introduction Ce tutoriel montre comment ajouter une capture d’écran dans une appli web : HTML pour la structure, CSS pour le style, JavaScript pour le comportement.\nPrérequis Bases HTML, CSS et JavaScript Navigateur récent avec support de getDisplayMedia Structure HTML Boutons pour démarrer / arrêter la capture et zone pour la vidéo.\n\u0026lt;p\u0026gt; \u0026lt;button id=\u0026#34;start\u0026#34;\u0026gt;Start Capture\u0026lt;/button\u0026gt; \u0026lt;button id=\u0026#34;stop\u0026#34; class=\u0026#34;hidden\u0026#34;\u0026gt;Stop Capture\u0026lt;/button\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;div class=\u0026#34;wrapper-video\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;br\u0026gt; \u0026lt;strong class=\u0026#34;log-title\u0026#34;\u0026gt;Log:\u0026lt;/strong\u0026gt; \u0026lt;br\u0026gt; \u0026lt;pre id=\u0026#34;log\u0026#34;\u0026gt;\u0026lt;/pre\u0026gt; CSS #video { display: table-cell; border: 1px solid #999; width: 100%; max-width: 1080px; } .w","date":"2017-01-10","date_unix":1484056800,"id":"https://antoineboucher.info/CV/blog/fr/posts/codepen-demos-antoinebou13/","permalink":"https://antoineboucher.info/CV/blog/fr/posts/codepen-demos-antoinebou13/","post_kind":"article","section":"posts","summary":"Intégrations CodePen et tutoriels front-end regroupés — Canvas, Three.js, jQuery, capture d’écran (getDisplayMedia), minuteur, plage de dates, barre d’étapes, bulles Messenger.","tag_refs":[{"name":"CodePen","permalink":"https://antoineboucher.info/CV/blog/fr/tags/codepen/"},{"name":"Frontend","permalink":"https://antoineboucher.info/CV/blog/fr/tags/frontend/"},{"name":"JavaScript","permalink":"https://antoineboucher.info/CV/blog/fr/tags/javascript/"},{"name":"CSS","permalink":"https://antoineboucher.info/CV/blog/fr/tags/css/"},{"name":"Canvas","permalink":"https://antoineboucher.info/CV/blog/fr/tags/canvas/"},{"name":"Three.js","permalink":"https://antoineboucher.info/CV/blog/fr/tags/three.js/"},{"name":"WebGL","permalink":"https://antoineboucher.info/CV/blog/fr/tags/webgl/"},{"name":"JQuery","permalink":"https://antoineboucher.info/CV/blog/fr/tags/jquery/"},{"name":"JQuery UI","permalink":"https://antoineboucher.info/CV/blog/fr/tags/jquery-ui/"},{"name":"WebRTC","permalink":"https://antoineboucher.info/CV/blog/fr/tags/webrtc/"},{"name":"HTML","permalink":"https://antoineboucher.info/CV/blog/fr/tags/html/"},{"name":"Tutorial","permalink":"https://antoineboucher.info/CV/blog/fr/tags/tutorial/"},{"name":"Progress","permalink":"https://antoineboucher.info/CV/blog/fr/tags/progress/"},{"name":"Timer","permalink":"https://antoineboucher.info/CV/blog/fr/tags/timer/"},{"name":"Date Picker","permalink":"https://antoineboucher.info/CV/blog/fr/tags/date-picker/"}],"tags":["CodePen","Frontend","JavaScript","CSS","Canvas","Three.js","WebGL","jQuery","jQuery UI","WebRTC","HTML","Tutorial","Progress","Timer","Date Picker"],"tags_text":"CodePen Frontend JavaScript CSS Canvas Three.js WebGL jQuery jQuery UI WebRTC HTML Tutorial Progress Timer Date Picker","thumb":"/CV/blog/images/post-kind-article.png","title":"Démos CodePen (collection)"},{"content":"Dimension Dépôt · mathlib sur docs.rs\nmathlib est une crate Rust pour l’algèbre linéaire dense et creuse, les décompositions, la mathématique 3D, le clustering, les graphes, les transformations, et plus encore — avec des démos WebAssembly et des fonctionnalités SIMD / GPU optionnelles. Le dépôt Dimension regroupe cette crate avec de la cinématique, de la physique, des expériences de rendu et de la documentation.\nDémarrage rapide cd mathlib \u0026amp;\u0026amp; cargo build cd mathlib \u0026amp;\u0026amp; cargo test Voir le README à la racine et docs/DOCS.md pour l’architecture et des exemples.","date":"2026-04-13","date_unix":1776081600,"id":"https://antoineboucher.info/CV/blog/fr/projects/dimension/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/dimension/","post_kind":"","section":"projects","summary":"Monorepo Rust centré sur mathlib — algèbre linéaire, matrices creuses, WASM, GPU optionnel.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"Dimension (mathlib)"},{"content":"ESP32-7SEG Dépôt · Licence MIT\nFirmware pour une carte ESP32 FireBeetle qui pilote un afficheur 7 segments Adafruit (backpack I2C) et expose une interface web locale pour les modes minuteur et la gestion WiFi.\nInterface web Le serveur intégré sert un panneau Timer Control dans le navigateur :\nModes chronomètre et compte à rebours Raccourcis 10 min, 5 min, 1 min Saisie minutes et secondes manuelle, bouton Start Reset WiFi pour effacer les identifiants stockés et reconfigurer l’appareil Matériel ESP32 FireBeetle (v1.0) Afficheur 7 segments Adafruit avec backpack I2C Platine d’essai et fils de liaison (SCL, SDA, VCC, GND) Logiciel Projet PlatformIO ; voir platformio.ini pour la carte et les bibliothèques. Le dépôt inclut aussi des sous-projets Android et firmware pour une stack plus complète autour du même matériel. Démarrage rapide git clone https://github.com/antoinebou12/ESP32-7SEG.git cd ESP32-7SEG Ouvrir le projet dans PlatformIO (ou Arduino IDE avec les bibliothèques équivalentes), compiler et flasher l’ESP32. Au démarrage, lire l’adresse IP dans le moniteur série, puis l’ouvrir dans un navigateur sur le même réseau pour utiliser l’interface web.","date":"2026-04-13","date_unix":1776081600,"id":"https://antoineboucher.info/CV/blog/fr/projects/esp32-7seg/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/esp32-7seg/","post_kind":"","section":"projects","summary":"Firmware ESP32 et interface web pour un afficheur 7 segments Adafruit — chronomètre, compte à rebours et WiFi.","tag_refs":[],"tags":[],"tags_text":"","thumb":"https://antoineboucher.info/CV/blog/projects/esp32-7seg/featured_hu_a79c915d2b23520.png","title":"ESP32-7SEG"},{"content":"HDR-10bpp-Display-Test Petit projet pour vérifier les capacités HDR 4K sur Linux, notamment une profondeur de 10 bits par canal. Utile pour valider chaîne graphique, pilotes et écran.\nDémarrage Dépôt GitHub\nPrérequis (exemple Debian/Ubuntu) Serveur X, Python 3, GTK 3 ImageJ (affichage 10 bits), ImageIO sudo apt-get install xserver-xorg python3 python3-gi python3-gi-cairo gir1.2-gtk-3.0 imagej Les procédures détaillées et scripts sont décrits en anglais dans le README du dépôt.","date":"2024-01-01","date_unix":1704110400,"id":"https://antoineboucher.info/CV/blog/fr/projects/hdr-10bpp-display-test/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/hdr-10bpp-display-test/","post_kind":"","section":"projects","summary":"Test d’affichage HDR 4K sous Linux avec profondeur 10 bits par canal.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"HDR-10bpp-Display-Test"},{"content":"\nDépôt · Licence MIT\nStack média en Docker Compose : récupération (torrents et Usenet), chaîne *Arr, sous-titres, lecture avec Plex ou Jellyfin, demandes et supervision. Les volumes et chemins suivent le docker-compose.yml du dépôt (variable ROOT, etc.).\nContenu de la stack Regroupement par rôle (détails dans le fichier compose) :\nClients et indexeurs — Deluge, NZBGet, Jackett, NZBHydra2, Prowlarr Automatisation — Sonarr, Radarr, Lidarr, Bazarr, CouchPotato ; Readarr (livres) ; Whisparr ; Tdarr (transcodage) Bibliothèques et demandes — Plex, Jellyfin, Ombi, Jellyseerr, Tautulli Complément — Stash (organisateur de bibliothèque spécialisé) Exploitation — Netdata, Dashmachine, Filebrowser Démarrage rapide Installer Docker et Compose sur la machine. Cloner le dépôt et renseigner les variables d’environnement (fichier .env du dépôt, chemins type ROOT). Depuis le dossier du projet : git clone https://github.com/antoinebou12/MediaBoxDockerCompose.git cd MediaBoxDockerCompose docker compose up -d L’ancienne commande docker-compose up -d reste valable ; le README du dépôt décrit la procédure en anglais.\nDocumentation Ports, identifiants par défaut, sauvegarde / restauration et contribution sont documentés dans le README pour éviter de dupliquer le manuel sur ce site.","date":"2024-01-01","date_unix":1704110400,"id":"https://antoineboucher.info/CV/blog/fr/projects/mediaboxdockercompose/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/mediaboxdockercompose/","post_kind":"","section":"projects","summary":"Stack Docker Compose pour téléchargements, suite *Arr, Plex/Jellyfin et supervision — la config est dans le dépôt.","tag_refs":[{"name":"Docker","permalink":"https://antoineboucher.info/CV/blog/fr/tags/docker/"},{"name":"Docker Compose","permalink":"https://antoineboucher.info/CV/blog/fr/tags/docker-compose/"},{"name":"Homelab","permalink":"https://antoineboucher.info/CV/blog/fr/tags/homelab/"},{"name":"Plex","permalink":"https://antoineboucher.info/CV/blog/fr/tags/plex/"},{"name":"Sonarr","permalink":"https://antoineboucher.info/CV/blog/fr/tags/sonarr/"},{"name":"Radarr","permalink":"https://antoineboucher.info/CV/blog/fr/tags/radarr/"}],"tags":["Docker","Docker Compose","Homelab","Plex","Sonarr","Radarr"],"tags_text":"Docker Docker Compose Homelab Plex Sonarr Radarr","thumb":"/CV/blog/images/post-kind-project.png","title":"Media center (Docker Compose)"},{"content":"RetroArch Web Games retroarch-web-games — RetroArch dans le navigateur via Docker, avec jeux NES, SNES, Mega Drive et Game Boy pré-téléchargés.\nFonctionnalités Jeux fournis avec l’image Lecteur web auto-hébergé Déploiement simple avec Docker / Docker Compose Utilisation docker-compose up -d Image Docker : hub.docker.com/r/antoinebou13/retroarch-web-games\nLes détails supplémentaires sont en anglais dans le dépôt GitHub.","date":"2024-01-01","date_unix":1704110400,"id":"https://antoineboucher.info/CV/blog/fr/projects/retroarch-web-games/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/retroarch-web-games/","post_kind":"","section":"projects","summary":"Lecteur web RetroArch auto-hébergé avec une collection de jeux classiques.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"RetroArch Web Games"},{"content":"Projet portfolio Site personnel statique généré avec Hugo. Dépôt : github.com/antoinebou12/portfolio.","date":"2023-12-30","date_unix":1703937600,"id":"https://antoineboucher.info/CV/blog/fr/projects/porfolio/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/porfolio/","post_kind":"","section":"projects","summary":"Site portfolio personnel réalisé avec Hugo.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"Portfolio"},{"content":"Voir le projet sur GitHub.","date":"2021-09-06","date_unix":1630939343,"id":"https://antoineboucher.info/CV/blog/fr/projects/movietraileranalyzer/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/movietraileranalyzer/","post_kind":"","section":"projects","summary":"Outils et pipelines pour analyser des bandes-annonces de films.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"Analyseur de bandes-annonces"},{"content":"FileClassifier Outil en ligne de commande qui organise automatiquement les fichiers d’un dossier en catégories selon leur type (images, documents, vidéos, etc.). Classifieur extensible avec prise en charge optionnelle de la modélisation de sujets.\nFonctionnalités Organisation automatique par types de fichiers Catégories prédéfinies pour les formats courants Classifieur extensible (modélisation de sujets) Structure de sortie configurable Léger et simple à utiliser Installation git clone https://github.com/antoinebou12/FileClassifier.git cd FileClassifier pip install poetry poetry install La suite (usage, options, exemples) est documentée en anglais dans le dépôt.","date":"2021-09-06","date_unix":1630939343,"id":"https://antoineboucher.info/CV/blog/fr/projects/fileclassifier/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/fileclassifier/","post_kind":"","section":"projects","summary":"CLI Python qui classe les fichiers par type, avec modélisation de sujets optionnelle.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"Classificateur de fichiers"},{"content":"D2COpenAIPlugin Consultez le projet sur GitHub.\nDémo en ligne : openai-uml-plugin.vercel.app\nRejoignez la liste d’attente des extensions ChatGPT.\nD2COpenAIPlugin est une extension pour ChatGPT qui permet de générer des diagrammes avec PlantUML ou Mermaid, directement depuis la conversation.\nFonctionnalités Diagrammes PlantUML ou Mermaid Intégration avec ChatGPT Interface orientée création de schémas Installation (aperçu) Prérequis typiques : Python 3.10+, FastAPI, uvicorn. Cloner le dépôt, installer les dépendances (poetry ou requirements-dev.txt), configurer les variables d’environnement et un jeton bearer, puis lancer l’API (par ex. uvicorn app:app --host 127.0.0.1 --port 5003).\nLes étapes détaillées (ChatGPT, manifeste, tests locaux) sont documentées en anglais dans le README du dépôt.\nAide Pour les questions sur le développement d’extensions : forum développeurs OpenAI.","date":"2021-09-06","date_unix":1630939343,"id":"https://antoineboucher.info/CV/blog/fr/projects/d2copenaiplugin/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/d2copenaiplugin/","post_kind":"","section":"projects","summary":"Extension ChatGPT pour générer des diagrammes PlantUML ou Mermaid.","tag_refs":[{"name":"ChatGPT","permalink":"https://antoineboucher.info/CV/blog/fr/tags/chatgpt/"},{"name":"OpenAI","permalink":"https://antoineboucher.info/CV/blog/fr/tags/openai/"},{"name":"Plugin","permalink":"https://antoineboucher.info/CV/blog/fr/tags/plugin/"},{"name":"PlantUML","permalink":"https://antoineboucher.info/CV/blog/fr/tags/plantuml/"},{"name":"Mermaid","permalink":"https://antoineboucher.info/CV/blog/fr/tags/mermaid/"}],"tags":["ChatGPT","OpenAI","Plugin","PlantUML","Mermaid"],"tags_text":"ChatGPT OpenAI Plugin PlantUML Mermaid","thumb":"/CV/blog/images/post-kind-project.png","title":"D2COpenAIPlugin"},{"content":"DasherControl Tableau de bord configurable avec grille d’éléments (iframes, favoris, etc.) et contrôle basique de conteneurs Docker, développé en Vue.js et Rust (Rocket).\nObjectif Centraliser les applications web du homelab (Sonarr, Jellyfin, etc.) sans multiplier les onglets, et préparer des widgets (applets) pour les tâches récurrentes — reverse proxy, SSL, supervision — avec une expérience proche d’un bureau de widgets, persistée en base.\nAperçu Feuille de route et documentation complète : GitHub — DasherControl.","date":"2021-09-06","date_unix":1630939343,"id":"https://antoineboucher.info/CV/blog/fr/projects/dashcontrol/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/dashcontrol/","post_kind":"","section":"projects","summary":"Tableau de bord Vue et Rust pour homelab Docker : apps, espaces de travail et applets.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"DasherControl"},{"content":"PlantUMLApi Interface Python vers un serveur PlantUML : génération de diagrammes UML et assimilés à partir d’un langage texte simple. Par défaut le client cible le serveur public PlantUML, mais il peut pointer vers n’importe quelle instance.\nInstallation pip install plantumlapi ou depuis les sources :\npip install git+https://github.com/antoinebou12/plantumlapi Ligne de commande plantumlapi --help La référence complète des options et de l’API Python est en anglais dans le dépôt.","date":"2021-09-06","date_unix":1630939343,"id":"https://antoineboucher.info/CV/blog/fr/projects/plantumlapi/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/plantumlapi/","post_kind":"","section":"projects","summary":"Client Python pour générer des diagrammes via un serveur PlantUML.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"PlantUMLApi"},{"content":"Dépôt GitHub\nRawAnalyser Outil Python pour l’analyse d’images RAW : détection de clipping noir/blanc, calcul gamma, planéité et vignettage (réf. Algolux), inspection des pixels Bayer.\nPrérequis Python 2.7 ou 3 selon la branche du dépôt, avec notamment numpy, scipy, pyqt5, plotly, matplotlib, etc. (liste complète dans le README du projet).\nUsage python rawanalyser.py --help La documentation détaillée des options et workflows est en anglais sur GitHub.","date":"2021-09-06","date_unix":1630939343,"id":"https://antoineboucher.info/CV/blog/fr/projects/rawanalyser/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/rawanalyser/","post_kind":"","section":"projects","summary":"Outil Python pour analyser des images RAW : clipping, gamma, vignettage, pixels Bayer.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"RawAnalyser"},{"content":"Serilog.Sinks.SentrySDK Sink Serilog pour Sentry : centraliser logs et erreurs dans Sentry avec le SDK officiel.\nS’inspire de serilog-contrib/serilog-sinks-sentry.\nÉtat du projet Paquets NuGet Paquet NuGet Serilog.Sinks.SentrySDK Lien Serilog.Sinks.SentrySDK.AspNetCore Lien Installation et configuration Exemples dotnet add package, configuration dans Program.cs / appsettings.json, et options avancées : voir le README en anglais sur GitHub.","date":"2021-09-06","date_unix":1630939343,"id":"https://antoineboucher.info/CV/blog/fr/projects/serilog.sinks.sentrysdk/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/serilog.sinks.sentrysdk/","post_kind":"","section":"projects","summary":"Sink Serilog qui envoie les événements vers Sentry via le SDK Sentry.","tag_refs":[{"name":"Serilog","permalink":"https://antoineboucher.info/CV/blog/fr/tags/serilog/"},{"name":"Sentry","permalink":"https://antoineboucher.info/CV/blog/fr/tags/sentry/"},{"name":"SentrySDK","permalink":"https://antoineboucher.info/CV/blog/fr/tags/sentrysdk/"}],"tags":["Serilog","Sentry","SentrySDK"],"tags_text":"Serilog Sentry SentrySDK","thumb":"/CV/blog/images/post-kind-project.png","title":"Serilog.Sinks.SentrySDK"},{"content":"WordUnveil Variante multilingue du jeu Wordle pour découvrir du vocabulaire tout en jouant. Stack : RedwoodJS, GraphQL, Prisma et MySQL.\nDémarrage En développement\ncd WordUnveil yarn install cp .env.default .env yarn rw prisma migrate dev yarn rw exec seed yarn rw dev Docker Compose\ndocker-compose up -d PostgreSQL seul\ndocker run --name=db -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin -p \u0026#39;5432:5432\u0026#39; -d postgres Déploiement yarn rw deploy baremetal production --first-run ","date":"2021-09-06","date_unix":1630939343,"id":"https://antoineboucher.info/CV/blog/fr/projects/wordunveil/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/wordunveil/","post_kind":"","section":"projects","summary":"Jeu multilingue façon Wordle avec RedwoodJS, GraphQL et Prisma.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"WordUnveil"},{"content":"Bibliothèque Python MarketWatch MarketWatch · Documentation\nClient Python pour le jeu boursier MarketWatch (connexion, cours, portefeuille, ordres, etc.). S’appuie sur des travaux antérieurs de la communauté (voir le dépôt pour les crédits).\nLes détails d’API, exemples et changelog complets sont en anglais sur GitHub.","date":"2021-09-06","date_unix":1630929600,"id":"https://antoineboucher.info/CV/blog/fr/projects/marketwatch/","permalink":"https://antoineboucher.info/CV/blog/fr/projects/marketwatch/","post_kind":"","section":"projects","summary":"Bibliothèque Python pour interagir avec le jeu boursier MarketWatch.","tag_refs":[],"tags":[],"tags_text":"","thumb":"/CV/blog/images/post-kind-project.png","title":"Bibliothèque Python MarketWatch"},{"content":"Champignons Bella Farcis aux Œufs avec Fromage de Chèvre et Épinards Catégorie Type : Végétarien Ingrédient Principal : Champignons Temps de Préparation : 1 minute Temps de Cuisson : 5 minutes Temps Total : 6 minutes Portions : 1 Ingrédients 3 gros champignons bella (prix : 1,99 $) 2 œufs (prix : 0,50 $) 100g de fromage de chèvre (prix : 1,99 $) 1 poignée d\u0026rsquo;épinards (prix : 0,50 $) Sel (prix : 0,01 $) Huile d\u0026rsquo;avocat (prix : 0,01 $) Prix Prix total : 5,00 $ Instructions Préchauffez votre poêle avec de l\u0026rsquo;huile d\u0026rsquo;avocat. Retirez les tiges des champignons et placez-les chapeau vers le bas dans la poêle. Cassez un œuf dans chaque chapeau de champignon. Assaisonnez de sel et ajoutez des épinards autour des champignons. Couvrez et faites cuire jusqu\u0026rsquo;à ce que les œufs soient pris. Émiettez le fromage de chèvre sur les champignons et les œufs. Servez chaud avec de l\u0026rsquo;avocat tranché. Avis ⭐⭐⭐⭐ - \u0026ldquo;Juteux et facile à faire. Ce plat végétarien est un mélange délicieux de textures et de saveurs. Parfait pour un repas sain et satisfaisant !\u0026rdquo;","date":"2024-01-05","date_unix":1704412800,"id":"https://antoineboucher.info/CV/blog/fr/recipes/champignon/","permalink":"https://antoineboucher.info/CV/blog/fr/recipes/champignon/","post_kind":"","section":"recipes","summary":"Un délicieux plat végétarien facile à préparer.","tag_refs":[],"tags":[],"tags_text":"","thumb":"https://antoineboucher.info/CV/blog/recipes/champignon/featured_hu_9f3ed0632dcb76cd.png","title":"Champignons Bella Farcis aux Œufs avec Fromage de Chèvre et Épinards"},{"content":"\nPhoto : Unsplash\nCatégorie Type : Végétarien Ingrédient principal : Épinards et chèvre Temps de préparation : ~25 minutes Temps de cuisson : ~35 minutes Temps total : ~60 minutes Portions : 6–8 Ingrédients Pâte\n1 disque de pâte brisée ou feuilletée au beurre (~230 g), décongelée si surgelée\n(ou assez de pâte maison pour un moule de 23–25 cm) Garniture\n400 g d’épinards frais (ou 250 g surgelés, décongelés et bien essorés) 1 c. à soupe d’huile d’olive ou de beurre 2 œufs 100 ml de crème fluide (ou crème fraîche) 150 g de fromage de chèvre frais, émietté 50 g de noix, toastées grossièrement 1 petite gousse d’ail, râpée (facultatif) Sel, poivre noir, pincée de muscade Finition\n2–3 c. à soupe de miel liquide Quelques demi-noix en décoration (facultatif) Étapes Préchauffer le four à 190 °C (375 °F). Étaler la pâte pour foncer un moule à tarte cannelé de 23–25 cm ; border et réserver au frais 15 minutes si possible. Piquer le fond, garnir de papier et de billes de cuisson, et précuire 12–15 minutes jusqu’à ce que les bords tiennent. Retirer les billes, puis poursuivre environ 5 minutes jusqu’à coloration légère. Faire tomber les épinards à la poêle avec l’huile ou le beurre. Saler légèrement, égoutter et presser pour enlever l’excès d’eau. Hacher grossièrement si les feuilles sont grandes. Fouetter les œufs avec la crème, le sel, le poivre et la muscade. Incorporer les épinards, la majeure partie du chèvre et les noix hachées. Verser dans le fond de tarte, parsemer le reste de chèvre. Cuire 25–30 minutes jusqu’à ce que l’appareil soit pris et doré par endroits. Laisser tiédir, arroser de miel et ajouter des noix si désiré. Servir tiède ou à température ambiante. Notes Le miel s’ajoute après cuisson pour garder son parfum et éviter qu’il ne brûle.\nAvis ⭐⭐⭐⭐ — Bon équilibre sucré-salé et noiseté ; idéal pour un brunch ou un dîner léger avec une salade verte.","date":"2024-01-05","date_unix":1704412800,"id":"https://antoineboucher.info/CV/blog/fr/recipes/spinach-goat-cheese-walnut-tart/","permalink":"https://antoineboucher.info/CV/blog/fr/recipes/spinach-goat-cheese-walnut-tart/","post_kind":"","section":"recipes","summary":"Tarte salée aux épinards, fromage de chèvre, noix toastées et filet de miel — fond pré-cuit et appareil simple.","tag_refs":[{"name":"Végétarien","permalink":"https://antoineboucher.info/CV/blog/fr/tags/v%C3%A9g%C3%A9tarien/"},{"name":"Pâtisserie Salée","permalink":"https://antoineboucher.info/CV/blog/fr/tags/p%C3%A2tisserie-sal%C3%A9e/"}],"tags":["Végétarien","Pâtisserie salée"],"tags_text":"Végétarien Pâtisserie salée","thumb":"https://antoineboucher.info/CV/blog/recipes/spinach-goat-cheese-walnut-tart/featured_hu_92a2e463d30dcb4c.jpg","title":"Tarte épinards, chèvre, miel et noix"}]