Skip to main content

qs_backtest_api/
client.rs

1use std::pin::Pin;
2
3use async_trait::async_trait;
4use futures::Stream;
5use qs_service::TransportFailure;
6use thiserror::Error;
7
8use crate::{
9    AddProfileRequest, AddProfileResponse, BacktestEvent, BacktestStatusResponse,
10    CancelBacktestResponse, DeleteResultArtifactRequest, DeleteResultArtifactResponse,
11    GetBacktestResultResponse, GetResultArtifactChunkRequest, GetResultArtifactChunkResponse,
12    GetSearchResultResponse, ListProfilesResponse, ListSymbolsRequest, ListSymbolsResponse,
13    PingResponse, ReloadProfilesResponse, RemoveProfileRequest, RemoveProfileResponse,
14    RunBacktestMultiRequest, RunBacktestMultiResponse, RunBacktestRequest, RunBacktestResponse,
15    RunConfiguredStrategyRequest, RunPortfolioRequest, SubmitBacktestRequest,
16    SubmitBacktestResponse, SubmitConfiguredStrategyRequest, SubmitPortfolioRequest,
17    SubmitSearchRequest,
18};
19
20pub type BacktestEventStream =
21    Pin<Box<dyn Stream<Item = Result<BacktestEvent, BacktestClientError>> + Send>>;
22
23/// Provider-neutral client port for the retained backtest-job workflow.
24#[async_trait]
25pub trait BacktestClient: Send + Sync {
26    async fn ping(&self) -> Result<PingResponse, BacktestClientError>;
27    async fn submit(
28        &self,
29        request: SubmitBacktestRequest,
30    ) -> Result<SubmitBacktestResponse, BacktestClientError>;
31    async fn status(&self, job_id: &str) -> Result<BacktestStatusResponse, BacktestClientError>;
32    async fn watch(&self, job_id: &str) -> Result<BacktestEventStream, BacktestClientError>;
33    async fn result(&self, job_id: &str) -> Result<GetBacktestResultResponse, BacktestClientError>;
34    async fn get_result_artifact_chunk(
35        &self,
36        request: GetResultArtifactChunkRequest,
37    ) -> Result<GetResultArtifactChunkResponse, BacktestClientError>;
38    async fn delete_result_artifact(
39        &self,
40        request: DeleteResultArtifactRequest,
41    ) -> Result<DeleteResultArtifactResponse, BacktestClientError>;
42    async fn cancel(&self, job_id: &str) -> Result<CancelBacktestResponse, BacktestClientError>;
43}
44
45/// Provider-neutral client port for finite synchronous backtest execution.
46#[async_trait]
47pub trait BacktestSyncClient: Send + Sync {
48    async fn run_backtest(
49        &self,
50        request: RunBacktestRequest,
51    ) -> Result<RunBacktestResponse, BacktestClientError>;
52    async fn run_backtest_multi(
53        &self,
54        request: RunBacktestMultiRequest,
55    ) -> Result<RunBacktestMultiResponse, BacktestClientError>;
56}
57
58/// Provider-neutral client port for profile and market-data discovery.
59#[async_trait]
60pub trait BacktestDiscoveryClient: Send + Sync {
61    async fn list_profiles(&self) -> Result<ListProfilesResponse, BacktestClientError>;
62    async fn list_symbols(
63        &self,
64        request: ListSymbolsRequest,
65    ) -> Result<ListSymbolsResponse, BacktestClientError>;
66}
67
68/// Provider-neutral client port for configured strategy runs, portfolio runs, and server-side parameter searches.
69///
70/// Submitted jobs share the retained-job workflow: status, watch, cancel, results, and artifacts go through [`BacktestClient`] with the returned job ID.
71#[async_trait]
72pub trait BacktestStrategyClient: Send + Sync {
73    async fn run_configured_strategy(
74        &self,
75        request: RunConfiguredStrategyRequest,
76    ) -> Result<RunBacktestResponse, BacktestClientError>;
77    async fn submit_configured_strategy(
78        &self,
79        request: SubmitConfiguredStrategyRequest,
80    ) -> Result<SubmitBacktestResponse, BacktestClientError>;
81    async fn run_portfolio(
82        &self,
83        request: RunPortfolioRequest,
84    ) -> Result<RunBacktestResponse, BacktestClientError>;
85    async fn submit_portfolio(
86        &self,
87        request: SubmitPortfolioRequest,
88    ) -> Result<SubmitBacktestResponse, BacktestClientError>;
89    async fn submit_search(
90        &self,
91        request: SubmitSearchRequest,
92    ) -> Result<SubmitBacktestResponse, BacktestClientError>;
93    async fn search_result(
94        &self,
95        job_id: &str,
96    ) -> Result<GetSearchResultResponse, BacktestClientError>;
97}
98
99/// Provider-neutral client port for runtime profile administration.
100#[async_trait]
101pub trait BacktestAdminClient: Send + Sync {
102    async fn add_profile(
103        &self,
104        request: AddProfileRequest,
105    ) -> Result<AddProfileResponse, BacktestClientError>;
106    async fn remove_profile(
107        &self,
108        request: RemoveProfileRequest,
109    ) -> Result<RemoveProfileResponse, BacktestClientError>;
110    async fn reload_profiles(&self) -> Result<ReloadProfilesResponse, BacktestClientError>;
111}
112
113#[derive(Debug, Clone, Error)]
114pub enum BacktestClientError {
115    #[error("backtest service rejected the request: {0}")]
116    Service(String),
117    #[error(transparent)]
118    Transport(#[from] TransportFailure),
119    #[error(transparent)]
120    Protocol(#[from] BacktestServiceProtocolError),
121}
122
123#[derive(Debug, Clone, Error)]
124#[error("backtest service protocol failure: {detail}")]
125pub struct BacktestServiceProtocolError {
126    pub detail: String,
127}