σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 1 · Les bases de Python pour les quants›Sujet 1.25·~40 minDébutant

Flux de recherche reproductibles

Position dans le parcoursModule 1 sur 11 · Sujet 25 sur 33
1.25 Flux de recherche reproductibles
—Score du lab— / 100XP total—Série—

Mêmes entrées, même réponse

Un résultat est reproductible quand vous, un collègue ou un relecteur pouvez relancer le même projet plus tard et obtenir les mêmes chiffres. Un backtest qui ne peut pas être reconstruit ne peut être ni audité, ni débogué, ni cru. Les résultats dérivent pour quelques raisons : nombres aléatoires, versions de bibliothèques différentes, données modifiées ou éditées à la main, réglages éparpillés dans le code et état caché, comme un notebook exécuté dans le désordre. Un flux reproductible supprime chaque source de dérive et consigne ce qui a été utilisé.

import random

# Same seed, same "random" numbers
a = random.Random(42)
b = random.Random(42)
print([round(a.random(), 3) for _ in range(3)])   # [0.639, 0.025, 0.275]
print([round(b.random(), 3) for _ in range(3)])   # [0.639, 0.025, 0.275]

# A different seed gives a different but equally repeatable sequence
print(round(random.Random(7).random(), 3))        # 0.324

Les graines, et à quel générateur elles appartiennent

Les simulations et le bootstrap utilisent des nombres aléatoires, qui sont en réalité une suite déterministe partant d'une graine (seed). Fixez la graine et l'exécution se répète à l'identique. random.seed(42) initialise le seul générateur global partagé par tout le programme, si bien que tout autre code qui tire des nombres décale votre suite. Il est plus sûr de créer votre propre générateur, rng = random.Random(42) (ou numpy.random.default_rng(42)), de le transmettre et d'y puiser. Vos résultats ne dépendent alors que de votre graine, et votre initialisation ne perturbe pas l'aléa des autres.

import random

# The global generator is shared: seeding it changes everyone's numbers
random.seed(1)
first = random.random()
random.seed(1)
print(random.random() == first)        # True: global seeding repeats...

# ...but a private generator leaves the global one untouched
random.seed(1)
before = random.getstate()
mine = random.Random(99)
mine.random()
print(random.getstate() == before)     # True: your draws did not disturb it

Empreintes pour les données et les réglages

Comment savoir que le fichier de données est bien celui d'il y a un mois ? Calculez une somme de contrôle : un hachage comme SHA-256 transforme n'importe quel fichier en une courte chaîne de longueur fixe qui change si un seul octet change. hashlib.sha256(data).hexdigest() la fournit ; lisez les gros fichiers par blocs. Les réglages méritent le même traitement : sérialisez la configuration avec des clés triées (json.dumps(config, sort_keys=True)), hachez ce texte, et l'empreinte est identique pour deux configurations équivalentes, quel que soit l'ordre des clés, et différente dès qu'une valeur change.

import hashlib
import json

# A checksum of bytes
print(hashlib.sha256(b"AAPL,184.25").hexdigest()[:12])   # 06ea4aa73ec1 (12 characters of 64)

# A fingerprint of settings, independent of key order
def fingerprint(config):
    text = json.dumps(config, sort_keys=True, separators=(",", ":"))
    return hashlib.sha256(text.encode("utf-8")).hexdigest()[:12]

one = {"fee": 0.001, "window": 20}
two = {"window": 20, "fee": 0.001}
print(fingerprint(one) == fingerprint(two))              # True
print(fingerprint(one) == fingerprint({"fee": 0.002, "window": 20}))  # False

Notez ce que vous avez exécuté

Chaque exécution devrait laisser un petit manifeste à côté de ses résultats : la graine, l'empreinte de la configuration, les versions de Python et des bibliothèques, la somme de contrôle de chaque fichier d'entrée et le commit Git du code. Avec cette trace, vous pouvez dire exactement ce qui a produit un chiffre et le reconstruire plus tard. Combinez-la avec les habitudes précédentes : un environnement figé dans requirements.txt, le code dans Git, les réglages dans un seul fichier, une commande qui exécute tout des données brutes aux tableaux finaux, et aucune modification manuelle des données entre-temps.

import json
import platform

# A manifest records what a run used
manifest = {
    "seed": 42,
    "config_fingerprint": "a1b2c3d4e5f6",
    "python": platform.python_version().split(".")[0] + ".x",
    "inputs": {"prices.csv": "9f86d081884c"},
}
print(json.dumps(manifest, indent=2, sort_keys=True))
# {
#   "config_fingerprint": "a1b2c3d4e5f6",
#   "inputs": {
#     "prices.csv": "9f86d081884c"
#   },
#   "python": "3.x",
#   "seed": 42
# }
Reproductible ne veut pas dire correctUn bogue se répète aussi parfaitement. La reproductibilité permet aux autres de vérifier votre travail, de trouver le bogue et de se fier à la correction ; elle ne remplace ni les tests, ni les contrôles de cohérence sur les données, ni un regard critique sur le résultat. Les graines cachent aussi la chance : quand un résultat dépend de la graine, lancez-le avec plusieurs et rapportez la dispersion.
  • Utilisez un random.Random(seed) privé pour que les résultats ne dépendent que de votre graine.
  • Empreinte pour les données (somme de contrôle d'un fichier) et les réglages (JSON trié, puis SHA-256).
  • Enregistrez un manifeste à chaque exécution ; environnement figé + commit Git + une commande pour reconstruire.

À suivre : classes et objets, pour regrouper des données et les fonctions qui les traitent en une unité propre.

Lab : ce qui est noté

  • Générer des rendements aléatoires répétables avec seeded_returns25 pts
  • Prendre l'empreinte d'un fichier de données avec file_checksum25 pts
  • Prendre l'empreinte d'une configuration avec config_fingerprint25 pts
  • Consigner ce qu'une exécution a utilisé avec run_manifest25 pts
Explorateur · lab-25-1
▾ lab-25-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-25-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-25-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub