{"openapi":"3.1.0","info":{"title":"API de BacktestStudio","version":"1.0.0","description":"Backtests de scripts Pine Script® v6 (moteur PineTS) sur données historiques. Simulations, pas de conseil en investissement. Authentification : jeton d'API (btk_…, créé sur /account) ou jeton OAuth, en en-tête Authorization: Bearer.","license":{"name":"AGPL-3.0","url":"https://www.gnu.org/licenses/agpl-3.0.html"}},"servers":[{"url":"https://staging.backteststudio.com/api/v1"}],"security":[{"bearer":[]}],"paths":{"/me":{"get":{"summary":"Compte, offre, usage du jour","responses":{"200":{"description":"Compte","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/validate":{"post":{"summary":"Valider un script","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceRef"}}}},"responses":{"200":{"description":"Résultat de validation (ok, errors, warnings, inputs, declared…)","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/backtests":{"post":{"summary":"Lancer un backtest","description":"Attend jusqu'à `wait` secondes (défaut 30, max 55).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BacktestRequest"}}}},"responses":{"200":{"description":"Terminé dans le délai `wait` : le rapport est joint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"202":{"description":"En cours : suivre avec GET /jobs/{id}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/optimizations":{"post":{"summary":"Lancer une optimisation avec walk-forward","description":"Attend jusqu'à `wait` secondes (défaut 0, max 55).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OptimizeRequest"}}}},"responses":{"200":{"description":"Terminé dans le délai `wait` : le rapport est joint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"202":{"description":"En cours : suivre avec GET /jobs/{id}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/evaluations":{"post":{"summary":"Évaluation complète (verdict, 11 tests)","description":"Attend jusqu'à `wait` secondes (défaut 0, max 55).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvaluateRequest"}}}},"responses":{"200":{"description":"Terminé dans le délai `wait` : le rapport est joint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"202":{"description":"En cours : suivre avec GET /jobs/{id}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/jobs/{id}":{"get":{"summary":"État d'un job","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Id du job (job_…)"},{"name":"wait","in":"query","required":false,"schema":{"type":"number","minimum":0,"maximum":55},"description":"Attente max en secondes"}],"responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/jobs/{id}/cancel":{"post":{"summary":"Annuler un job","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Id du job"}],"responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reports":{"get":{"summary":"Lister mes rapports","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"strategy","in":"query","required":false,"schema":{"type":"string"}},{"name":"market","in":"query","required":false,"schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["backtest","evaluation"]}}],"responses":{"200":{"description":"Rapports","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"},"items":{"type":"array","items":{"type":"object"}}}}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reports/{id}":{"get":{"summary":"Un rapport (sans positions ni courbe d'équité)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Id du rapport, ou \"last\""}],"responses":{"200":{"description":"Rapport","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reports/{id}/trades":{"get":{"summary":"Positions d'un rapport (50 max par page)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Id du rapport"},{"name":"side","in":"query","required":false,"schema":{"type":"string","enum":["long","short"]}},{"name":"outcome","in":"query","required":false,"schema":{"type":"string","enum":["win","loss"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","closed"]}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["time","pnl","-pnl","duration","mae"]}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50}}],"responses":{"200":{"description":"Page de positions","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"},"offset":{"type":"integer"},"limit":{"type":"integer"},"nextOffset":{"type":["integer","null"]},"trades":{"type":"array","items":{"type":"object"}}}}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reports/{id}/charts/{type}":{"get":{"summary":"Graphique PNG","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Id du rapport"},{"name":"type","in":"path","required":true,"schema":{"type":"string","enum":["price_trades","trade","equity","drawdown","monthly","trade_dist","mfe_mae","heatmap","walkforward","monte_carlo","markets","costs"]}},{"name":"tradeIndex","in":"query","required":false,"schema":{"type":"integer","minimum":1},"description":"Pour type=trade"},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"log","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Image","content":{"image/png":{"schema":{"type":"string","contentMediaType":"image/png"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/strategies":{"get":{"summary":"Lister mes stratégies sauvegardées","responses":{"200":{"description":"Stratégies","content":{"application/json":{"schema":{"type":"object","properties":{"strategies":{"type":"array","items":{"type":"object"}}}}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Sauvegarder une version de script","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["source"],"additionalProperties":false,"properties":{"name":{"type":"string","maxLength":120},"source":{"type":"string","maxLength":200000}}}}}},"responses":{"200":{"description":"Version déjà connue","content":{"application/json":{"schema":{"type":"object"}}}},"201":{"description":"Nouvelle version","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets":{"get":{"summary":"Chercher un marché","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"Texte cherché (BTC, SOL…)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50}}],"responses":{"200":{"description":"Marchés","content":{"application/json":{"schema":{"type":"object","properties":{"markets":{"type":"array","items":{"type":"object"}}}}}}},"400":{"description":"Requête invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Jeton absent ou invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable (ou appartenant à un autre compte)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Quota ou limite de débit atteints","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"btk_… (jeton d'API) ou jeton d'accès OAuth"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"SourceRef":{"type":"object","additionalProperties":false,"description":"Une seule forme : `source` ou `strategy`. `path` est refusé en ligne.","properties":{"source":{"type":"string","maxLength":200000,"description":"Texte complet du script Pine v6"},"strategy":{"type":"string","maxLength":200,"description":"Nom ou id (sv_…) d'une stratégie sauvegardée"}}},"BacktestRequest":{"type":"object","additionalProperties":false,"required":["market","timeframe"],"properties":{"source":{"type":"string","maxLength":200000,"description":"Texte complet du script Pine v6 (ou `strategy`)"},"strategy":{"type":"string","maxLength":200,"description":"Nom ou id (sv_…) d'une stratégie sauvegardée"},"market":{"type":"string","description":"BTC, ETHUSDT, BYBIT:SOLUSDT.P…"},"timeframe":{"type":"string","description":"1m 3m 5m 15m 30m 1h 2h 4h 6h 12h 1d 1w"},"from":{"type":"string","description":"AAAA-MM-JJ (UTC), défaut : il y a 3 ans"},"to":{"type":"string","description":"AAAA-MM-JJ (UTC), défaut : maintenant"},"inputs":{"type":"object","additionalProperties":{"type":["number","boolean","string"]},"description":"Clés = noms de variables Pine (voir /validate)"},"costs":{"type":"object","additionalProperties":false,"properties":{"commissionPercent":{"type":"number","minimum":0,"maximum":5},"slippageTicks":{"type":"integer","minimum":0,"maximum":1000}}},"idempotencyKey":{"type":"string","maxLength":200},"wait":{"type":"number","minimum":0,"maximum":55,"description":"Attente max du résultat, en secondes"}}},"OptimizeRequest":{"type":"object","additionalProperties":false,"required":["market","timeframe"],"properties":{"source":{"type":"string","maxLength":200000,"description":"Texte complet du script Pine v6 (ou `strategy`)"},"strategy":{"type":"string","maxLength":200,"description":"Nom ou id (sv_…) d'une stratégie sauvegardée"},"market":{"type":"string","description":"BTC, ETHUSDT, BYBIT:SOLUSDT.P…"},"timeframe":{"type":"string","description":"1m 3m 5m 15m 30m 1h 2h 4h 6h 12h 1d 1w"},"from":{"type":"string","description":"AAAA-MM-JJ (UTC), défaut : il y a 3 ans"},"to":{"type":"string","description":"AAAA-MM-JJ (UTC), défaut : maintenant"},"inputs":{"type":"object","additionalProperties":{"type":["number","boolean","string"]},"description":"Clés = noms de variables Pine (voir /validate)"},"costs":{"type":"object","additionalProperties":false,"properties":{"commissionPercent":{"type":"number","minimum":0,"maximum":5},"slippageTicks":{"type":"integer","minimum":0,"maximum":1000}}},"idempotencyKey":{"type":"string","maxLength":200},"wait":{"type":"number","minimum":0,"maximum":55,"description":"Attente max du résultat, en secondes"},"params":{"type":"array","maxItems":4,"items":{"type":"string","maxLength":300},"description":"Plages, ex. [\"fastLen=5:50:5\", \"mode=A,B\"]"},"objective":{"type":"string","enum":["calmar","return","pf"]},"minTrades":{"type":"integer","minimum":1,"maximum":10000},"walkForward":{"oneOf":[{"type":"object","additionalProperties":false,"required":["folds"],"properties":{"folds":{"type":"integer","minimum":2,"maximum":10},"trainPct":{"type":"number","minimum":40,"maximum":90}}},{"const":false}]}}},"EvaluateRequest":{"type":"object","additionalProperties":false,"required":["market","timeframe"],"properties":{"source":{"type":"string","maxLength":200000,"description":"Texte complet du script Pine v6 (ou `strategy`)"},"strategy":{"type":"string","maxLength":200,"description":"Nom ou id (sv_…) d'une stratégie sauvegardée"},"market":{"type":"string","description":"BTC, ETHUSDT, BYBIT:SOLUSDT.P…"},"timeframe":{"type":"string","description":"1m 3m 5m 15m 30m 1h 2h 4h 6h 12h 1d 1w"},"from":{"type":"string","description":"AAAA-MM-JJ (UTC), défaut : il y a 3 ans"},"to":{"type":"string","description":"AAAA-MM-JJ (UTC), défaut : maintenant"},"inputs":{"type":"object","additionalProperties":{"type":["number","boolean","string"]},"description":"Clés = noms de variables Pine (voir /validate)"},"costs":{"type":"object","additionalProperties":false,"properties":{"commissionPercent":{"type":"number","minimum":0,"maximum":5},"slippageTicks":{"type":"integer","minimum":0,"maximum":1000}}},"idempotencyKey":{"type":"string","maxLength":200},"wait":{"type":"number","minimum":0,"maximum":55,"description":"Attente max du résultat, en secondes"},"params":{"type":"array","maxItems":4,"items":{"type":"string","maxLength":300},"description":"Plages, ex. [\"fastLen=5:50:5\", \"mode=A,B\"]"},"objective":{"type":"string","enum":["calmar","return","pf"]},"minTrades":{"type":"integer","minimum":1,"maximum":10000},"walkForward":{"oneOf":[{"type":"object","additionalProperties":false,"required":["folds"],"properties":{"folds":{"type":"integer","minimum":2,"maximum":10},"trainPct":{"type":"number","minimum":40,"maximum":90}}},{"const":false}]},"basket":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":120}},"tests":{"type":"array","items":{"type":"string","enum":["markets","other_period","costs","stability","monte_carlo","concentration"]}},"thresholds":{"type":"object","additionalProperties":{"type":"number"}}}},"Job":{"type":"object","required":["jobId","status"],"properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["queued","running","done","failed","cancelled"]},"progress":{"type":["object","null"],"properties":{"stage":{"type":"string"},"done":{"type":"integer"},"total":{"type":"integer"}}},"error":{"type":["string","null"]},"reportId":{"type":["string","null"]},"report":{"type":"object","description":"Présent quand status = done : métriques, verdict, liens (links.html = rapport interactif)"}}}}}}