qtsurfer.api.client.models

Re-export of every generated model.

All request/response dataclasses live in qtsurfer.api.client._generated.models; importing this module makes them available at the shorter qtsurfer.api.client.models path.

1"""Re-export of every generated model.
2
3All request/response dataclasses live in
4``qtsurfer.api.client._generated.models``; importing this module makes
5them available at the shorter ``qtsurfer.api.client.models`` path.
6"""
7
8from qtsurfer.api.client._generated.models import *  # noqa: F401,F403
9from qtsurfer.api.client._generated.models import __all__  # noqa: F401
class AcceptedJob:
13@_attrs_define
14class AcceptedJob:
15    """Response returned by async endpoints (`202 Accepted`). The `jobId` is deterministic for the
16    same input parameters — repeated calls with identical params return the same id.
17
18        Example:
19            {'jobId': '13RBLGQlPnfDjO6wyKSX8i'}
20
21        Attributes:
22            job_id (str): Unique job identifier; use this to poll for completion. Example: 13RBLGQlPnfDjO6wyKSX8i.
23    """
24
25    job_id: str
26    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
27
28    def to_dict(self) -> dict[str, Any]:
29        job_id = self.job_id
30
31        field_dict: dict[str, Any] = {}
32        field_dict.update(self.additional_properties)
33        field_dict.update(
34            {
35                "jobId": job_id,
36            }
37        )
38
39        return field_dict
40
41    @classmethod
42    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
43        d = dict(src_dict)
44        job_id = d.pop("jobId")
45
46        accepted_job = cls(
47            job_id=job_id,
48        )
49
50        accepted_job.additional_properties = d
51        return accepted_job
52
53    @property
54    def additional_keys(self) -> list[str]:
55        return list(self.additional_properties.keys())
56
57    def __getitem__(self, key: str) -> Any:
58        return self.additional_properties[key]
59
60    def __setitem__(self, key: str, value: Any) -> None:
61        self.additional_properties[key] = value
62
63    def __delitem__(self, key: str) -> None:
64        del self.additional_properties[key]
65
66    def __contains__(self, key: str) -> bool:
67        return key in self.additional_properties

Response returned by async endpoints (202 Accepted). The jobId is deterministic for the same input parameters — repeated calls with identical params return the same id.

Example:
    {'jobId': '13RBLGQlPnfDjO6wyKSX8i'}

Attributes:
    job_id (str): Unique job identifier; use this to poll for completion. Example: 13RBLGQlPnfDjO6wyKSX8i.
AcceptedJob(job_id: str)
24def __init__(self, job_id):
25    self.job_id = job_id
26    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class AcceptedJob.

job_id: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
28    def to_dict(self) -> dict[str, Any]:
29        job_id = self.job_id
30
31        field_dict: dict[str, Any] = {}
32        field_dict.update(self.additional_properties)
33        field_dict.update(
34            {
35                "jobId": job_id,
36            }
37        )
38
39        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
41    @classmethod
42    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
43        d = dict(src_dict)
44        job_id = d.pop("jobId")
45
46        accepted_job = cls(
47            job_id=job_id,
48        )
49
50        accepted_job.additional_properties = d
51        return accepted_job
additional_keys: list[str]
53    @property
54    def additional_keys(self) -> list[str]:
55        return list(self.additional_properties.keys())
class AuthTokenError:
15@_attrs_define
16class AuthTokenError:
17    """Error envelope returned by `POST /auth/token` when the API key is rejected.
18
19    Attributes:
20        code (AuthTokenErrorCode): Machine-readable error reason.
21        message (str): Human-readable description of the failure.
22    """
23
24    code: AuthTokenErrorCode
25    message: str
26    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
27
28    def to_dict(self) -> dict[str, Any]:
29        code = self.code.value
30
31        message = self.message
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "code": code,
38                "message": message,
39            }
40        )
41
42        return field_dict
43
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        code = AuthTokenErrorCode(d.pop("code"))
48
49        message = d.pop("message")
50
51        auth_token_error = cls(
52            code=code,
53            message=message,
54        )
55
56        auth_token_error.additional_properties = d
57        return auth_token_error
58
59    @property
60    def additional_keys(self) -> list[str]:
61        return list(self.additional_properties.keys())
62
63    def __getitem__(self, key: str) -> Any:
64        return self.additional_properties[key]
65
66    def __setitem__(self, key: str, value: Any) -> None:
67        self.additional_properties[key] = value
68
69    def __delitem__(self, key: str) -> None:
70        del self.additional_properties[key]
71
72    def __contains__(self, key: str) -> bool:
73        return key in self.additional_properties

Error envelope returned by POST /auth/token when the API key is rejected.

Attributes: code (AuthTokenErrorCode): Machine-readable error reason. message (str): Human-readable description of the failure.

AuthTokenError( code: AuthTokenErrorCode, message: str)
25def __init__(self, code, message):
26    self.code = code
27    self.message = message
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class AuthTokenError.

message: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
28    def to_dict(self) -> dict[str, Any]:
29        code = self.code.value
30
31        message = self.message
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "code": code,
38                "message": message,
39            }
40        )
41
42        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        code = AuthTokenErrorCode(d.pop("code"))
48
49        message = d.pop("message")
50
51        auth_token_error = cls(
52            code=code,
53            message=message,
54        )
55
56        auth_token_error.additional_properties = d
57        return auth_token_error
additional_keys: list[str]
59    @property
60    def additional_keys(self) -> list[str]:
61        return list(self.additional_properties.keys())
class AuthTokenErrorCode(builtins.str, enum.Enum):
 5class AuthTokenErrorCode(str, Enum):
 6    APIKEY_EXPIRED = "apikey_expired"
 7    APIKEY_REVOKED = "apikey_revoked"
 8    INVALID_APIKEY = "invalid_apikey"
 9
10    def __str__(self) -> str:
11        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

APIKEY_EXPIRED = <AuthTokenErrorCode.APIKEY_EXPIRED: 'apikey_expired'>
APIKEY_REVOKED = <AuthTokenErrorCode.APIKEY_REVOKED: 'apikey_revoked'>
INVALID_APIKEY = <AuthTokenErrorCode.INVALID_APIKEY: 'invalid_apikey'>
class AuthTokenResponse:
 17@_attrs_define
 18class AuthTokenResponse:
 19    """
 20    Attributes:
 21        access_token (str): Short-lived HS256 JWT. Send as `Authorization: Bearer <token>` on all other endpoints.
 22        token_type (AuthTokenResponseTokenType): Always `Bearer`.
 23        expires_in (int): Seconds until the JWT expires (typically 3600). Example: 3600.
 24        tier (AuthTokenResponseTier): Subscription tier this token was issued for. Drives rate limits and feature flags
 25            on downstream endpoints. Example: free.
 26        scopes (list[str] | Unset): Scopes granted to this token. Reserved for future use; currently always empty.
 27    """
 28
 29    access_token: str
 30    token_type: AuthTokenResponseTokenType
 31    expires_in: int
 32    tier: AuthTokenResponseTier
 33    scopes: list[str] | Unset = UNSET
 34    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 35
 36    def to_dict(self) -> dict[str, Any]:
 37        access_token = self.access_token
 38
 39        token_type = self.token_type.value
 40
 41        expires_in = self.expires_in
 42
 43        tier = self.tier.value
 44
 45        scopes: list[str] | Unset = UNSET
 46        if not isinstance(self.scopes, Unset):
 47            scopes = self.scopes
 48
 49        field_dict: dict[str, Any] = {}
 50        field_dict.update(self.additional_properties)
 51        field_dict.update(
 52            {
 53                "access_token": access_token,
 54                "token_type": token_type,
 55                "expires_in": expires_in,
 56                "tier": tier,
 57            }
 58        )
 59        if scopes is not UNSET:
 60            field_dict["scopes"] = scopes
 61
 62        return field_dict
 63
 64    @classmethod
 65    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 66        d = dict(src_dict)
 67        access_token = d.pop("access_token")
 68
 69        token_type = AuthTokenResponseTokenType(d.pop("token_type"))
 70
 71        expires_in = d.pop("expires_in")
 72
 73        tier = AuthTokenResponseTier(d.pop("tier"))
 74
 75        scopes = cast(list[str], d.pop("scopes", UNSET))
 76
 77        auth_token_response = cls(
 78            access_token=access_token,
 79            token_type=token_type,
 80            expires_in=expires_in,
 81            tier=tier,
 82            scopes=scopes,
 83        )
 84
 85        auth_token_response.additional_properties = d
 86        return auth_token_response
 87
 88    @property
 89    def additional_keys(self) -> list[str]:
 90        return list(self.additional_properties.keys())
 91
 92    def __getitem__(self, key: str) -> Any:
 93        return self.additional_properties[key]
 94
 95    def __setitem__(self, key: str, value: Any) -> None:
 96        self.additional_properties[key] = value
 97
 98    def __delitem__(self, key: str) -> None:
 99        del self.additional_properties[key]
100
101    def __contains__(self, key: str) -> bool:
102        return key in self.additional_properties

Attributes: access_token (str): Short-lived HS256 JWT. Send as Authorization: Bearer <token> on all other endpoints. token_type (AuthTokenResponseTokenType): Always Bearer. expires_in (int): Seconds until the JWT expires (typically 3600). Example: 3600. tier (AuthTokenResponseTier): Subscription tier this token was issued for. Drives rate limits and feature flags on downstream endpoints. Example: free. scopes (list[str] | Unset): Scopes granted to this token. Reserved for future use; currently always empty.

AuthTokenResponse( access_token: str, token_type: AuthTokenResponseTokenType, expires_in: int, tier: AuthTokenResponseTier, scopes: list[str] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
28def __init__(self, access_token, token_type, expires_in, tier, scopes=attr_dict['scopes'].default):
29    self.access_token = access_token
30    self.token_type = token_type
31    self.expires_in = expires_in
32    self.tier = tier
33    self.scopes = scopes
34    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class AuthTokenResponse.

access_token: str
expires_in: int
scopes: list[str] | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
36    def to_dict(self) -> dict[str, Any]:
37        access_token = self.access_token
38
39        token_type = self.token_type.value
40
41        expires_in = self.expires_in
42
43        tier = self.tier.value
44
45        scopes: list[str] | Unset = UNSET
46        if not isinstance(self.scopes, Unset):
47            scopes = self.scopes
48
49        field_dict: dict[str, Any] = {}
50        field_dict.update(self.additional_properties)
51        field_dict.update(
52            {
53                "access_token": access_token,
54                "token_type": token_type,
55                "expires_in": expires_in,
56                "tier": tier,
57            }
58        )
59        if scopes is not UNSET:
60            field_dict["scopes"] = scopes
61
62        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
64    @classmethod
65    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
66        d = dict(src_dict)
67        access_token = d.pop("access_token")
68
69        token_type = AuthTokenResponseTokenType(d.pop("token_type"))
70
71        expires_in = d.pop("expires_in")
72
73        tier = AuthTokenResponseTier(d.pop("tier"))
74
75        scopes = cast(list[str], d.pop("scopes", UNSET))
76
77        auth_token_response = cls(
78            access_token=access_token,
79            token_type=token_type,
80            expires_in=expires_in,
81            tier=tier,
82            scopes=scopes,
83        )
84
85        auth_token_response.additional_properties = d
86        return auth_token_response
additional_keys: list[str]
88    @property
89    def additional_keys(self) -> list[str]:
90        return list(self.additional_properties.keys())
class AuthTokenResponseTier(builtins.str, enum.Enum):
 5class AuthTokenResponseTier(str, Enum):
 6    BASIC = "basic"
 7    ELITE = "elite"
 8    FREE = "free"
 9    PRO = "pro"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

BASIC = <AuthTokenResponseTier.BASIC: 'basic'>
ELITE = <AuthTokenResponseTier.ELITE: 'elite'>
FREE = <AuthTokenResponseTier.FREE: 'free'>
PRO = <AuthTokenResponseTier.PRO: 'pro'>
class AuthTokenResponseTokenType(builtins.str, enum.Enum):
5class AuthTokenResponseTokenType(str, Enum):
6    BEARER = "Bearer"
7
8    def __str__(self) -> str:
9        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

BEARER = <AuthTokenResponseTokenType.BEARER: 'Bearer'>
class BacktestJobResult:
18@_attrs_define
19class BacktestJobResult:
20    """Backtest job result.
21
22    Attributes:
23        results (ResultMap): Execution result map. Always includes core fields (hostName, iops, strategyId, instrument).
24            Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy
25            emitted at least one trade. When signal storage is enabled, includes signal fields described below. `notices`
26            carries what the run had to say about itself, and is absent when it had nothing.
27        state (JobState): Information about a single job
28    """
29
30    results: ResultMap
31    state: JobState
32    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
33
34    def to_dict(self) -> dict[str, Any]:
35        results = self.results.to_dict()
36
37        state = self.state.to_dict()
38
39        field_dict: dict[str, Any] = {}
40        field_dict.update(self.additional_properties)
41        field_dict.update(
42            {
43                "results": results,
44                "state": state,
45            }
46        )
47
48        return field_dict
49
50    @classmethod
51    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
52        from ..models.job_state import JobState
53        from ..models.result_map import ResultMap
54
55        d = dict(src_dict)
56        results = ResultMap.from_dict(d.pop("results"))
57
58        state = JobState.from_dict(d.pop("state"))
59
60        backtest_job_result = cls(
61            results=results,
62            state=state,
63        )
64
65        backtest_job_result.additional_properties = d
66        return backtest_job_result
67
68    @property
69    def additional_keys(self) -> list[str]:
70        return list(self.additional_properties.keys())
71
72    def __getitem__(self, key: str) -> Any:
73        return self.additional_properties[key]
74
75    def __setitem__(self, key: str, value: Any) -> None:
76        self.additional_properties[key] = value
77
78    def __delitem__(self, key: str) -> None:
79        del self.additional_properties[key]
80
81    def __contains__(self, key: str) -> bool:
82        return key in self.additional_properties

Backtest job result.

Attributes: results (ResultMap): Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade. When signal storage is enabled, includes signal fields described below. notices carries what the run had to say about itself, and is absent when it had nothing. state (JobState): Information about a single job

BacktestJobResult( results: ResultMap, state: JobState)
25def __init__(self, results, state):
26    self.results = results
27    self.state = state
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class BacktestJobResult.

results: ResultMap
state: JobState
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
34    def to_dict(self) -> dict[str, Any]:
35        results = self.results.to_dict()
36
37        state = self.state.to_dict()
38
39        field_dict: dict[str, Any] = {}
40        field_dict.update(self.additional_properties)
41        field_dict.update(
42            {
43                "results": results,
44                "state": state,
45            }
46        )
47
48        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
50    @classmethod
51    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
52        from ..models.job_state import JobState
53        from ..models.result_map import ResultMap
54
55        d = dict(src_dict)
56        results = ResultMap.from_dict(d.pop("results"))
57
58        state = JobState.from_dict(d.pop("state"))
59
60        backtest_job_result = cls(
61            results=results,
62            state=state,
63        )
64
65        backtest_job_result.additional_properties = d
66        return backtest_job_result
additional_keys: list[str]
68    @property
69    def additional_keys(self) -> list[str]:
70        return list(self.additional_properties.keys())
class CancelBacktestResponse200:
16@_attrs_define
17class CancelBacktestResponse200:
18    """
19    Attributes:
20        status (CancelBacktestResponse200Status | Unset):  Example: cancelling.
21        job_id (str | Unset):  Example: 13RBLGQlPnfDjO6wyKSX8i.
22    """
23
24    status: CancelBacktestResponse200Status | Unset = UNSET
25    job_id: str | Unset = UNSET
26    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
27
28    def to_dict(self) -> dict[str, Any]:
29        status: str | Unset = UNSET
30        if not isinstance(self.status, Unset):
31            status = self.status.value
32
33        job_id = self.job_id
34
35        field_dict: dict[str, Any] = {}
36        field_dict.update(self.additional_properties)
37        field_dict.update({})
38        if status is not UNSET:
39            field_dict["status"] = status
40        if job_id is not UNSET:
41            field_dict["jobId"] = job_id
42
43        return field_dict
44
45    @classmethod
46    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
47        d = dict(src_dict)
48        _status = d.pop("status", UNSET)
49        status: CancelBacktestResponse200Status | Unset
50        if isinstance(_status, Unset):
51            status = UNSET
52        else:
53            status = CancelBacktestResponse200Status(_status)
54
55        job_id = d.pop("jobId", UNSET)
56
57        cancel_backtest_response_200 = cls(
58            status=status,
59            job_id=job_id,
60        )
61
62        cancel_backtest_response_200.additional_properties = d
63        return cancel_backtest_response_200
64
65    @property
66    def additional_keys(self) -> list[str]:
67        return list(self.additional_properties.keys())
68
69    def __getitem__(self, key: str) -> Any:
70        return self.additional_properties[key]
71
72    def __setitem__(self, key: str, value: Any) -> None:
73        self.additional_properties[key] = value
74
75    def __delitem__(self, key: str) -> None:
76        del self.additional_properties[key]
77
78    def __contains__(self, key: str) -> bool:
79        return key in self.additional_properties

Attributes: status (CancelBacktestResponse200Status | Unset): Example: cancelling. job_id (str | Unset): Example: 13RBLGQlPnfDjO6wyKSX8i.

CancelBacktestResponse200( status: CancelBacktestResponse200Status | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, job_id: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
25def __init__(self, status=attr_dict['status'].default, job_id=attr_dict['job_id'].default):
26    self.status = status
27    self.job_id = job_id
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class CancelBacktestResponse200.

status: CancelBacktestResponse200Status | qtsurfer.api.client._generated.types.Unset
job_id: str | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
28    def to_dict(self) -> dict[str, Any]:
29        status: str | Unset = UNSET
30        if not isinstance(self.status, Unset):
31            status = self.status.value
32
33        job_id = self.job_id
34
35        field_dict: dict[str, Any] = {}
36        field_dict.update(self.additional_properties)
37        field_dict.update({})
38        if status is not UNSET:
39            field_dict["status"] = status
40        if job_id is not UNSET:
41            field_dict["jobId"] = job_id
42
43        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
45    @classmethod
46    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
47        d = dict(src_dict)
48        _status = d.pop("status", UNSET)
49        status: CancelBacktestResponse200Status | Unset
50        if isinstance(_status, Unset):
51            status = UNSET
52        else:
53            status = CancelBacktestResponse200Status(_status)
54
55        job_id = d.pop("jobId", UNSET)
56
57        cancel_backtest_response_200 = cls(
58            status=status,
59            job_id=job_id,
60        )
61
62        cancel_backtest_response_200.additional_properties = d
63        return cancel_backtest_response_200
additional_keys: list[str]
65    @property
66    def additional_keys(self) -> list[str]:
67        return list(self.additional_properties.keys())
class CancelBacktestResponse200Status(builtins.str, enum.Enum):
5class CancelBacktestResponse200Status(str, Enum):
6    CANCELLING = "cancelling"
7
8    def __str__(self) -> str:
9        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

CANCELLING = <CancelBacktestResponse200Status.CANCELLING: 'cancelling'>
class CancelSweepResponse200:
15@_attrs_define
16class CancelSweepResponse200:
17    """
18    Attributes:
19        status (CancelSweepResponse200Status):
20        sweep_id (str):
21    """
22
23    status: CancelSweepResponse200Status
24    sweep_id: str
25    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
26
27    def to_dict(self) -> dict[str, Any]:
28        status = self.status.value
29
30        sweep_id = self.sweep_id
31
32        field_dict: dict[str, Any] = {}
33        field_dict.update(self.additional_properties)
34        field_dict.update(
35            {
36                "status": status,
37                "sweepId": sweep_id,
38            }
39        )
40
41        return field_dict
42
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        d = dict(src_dict)
46        status = CancelSweepResponse200Status(d.pop("status"))
47
48        sweep_id = d.pop("sweepId")
49
50        cancel_sweep_response_200 = cls(
51            status=status,
52            sweep_id=sweep_id,
53        )
54
55        cancel_sweep_response_200.additional_properties = d
56        return cancel_sweep_response_200
57
58    @property
59    def additional_keys(self) -> list[str]:
60        return list(self.additional_properties.keys())
61
62    def __getitem__(self, key: str) -> Any:
63        return self.additional_properties[key]
64
65    def __setitem__(self, key: str, value: Any) -> None:
66        self.additional_properties[key] = value
67
68    def __delitem__(self, key: str) -> None:
69        del self.additional_properties[key]
70
71    def __contains__(self, key: str) -> bool:
72        return key in self.additional_properties

Attributes: status (CancelSweepResponse200Status): sweep_id (str):

CancelSweepResponse200( status: CancelSweepResponse200Status, sweep_id: str)
25def __init__(self, status, sweep_id):
26    self.status = status
27    self.sweep_id = sweep_id
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class CancelSweepResponse200.

sweep_id: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
27    def to_dict(self) -> dict[str, Any]:
28        status = self.status.value
29
30        sweep_id = self.sweep_id
31
32        field_dict: dict[str, Any] = {}
33        field_dict.update(self.additional_properties)
34        field_dict.update(
35            {
36                "status": status,
37                "sweepId": sweep_id,
38            }
39        )
40
41        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        d = dict(src_dict)
46        status = CancelSweepResponse200Status(d.pop("status"))
47
48        sweep_id = d.pop("sweepId")
49
50        cancel_sweep_response_200 = cls(
51            status=status,
52            sweep_id=sweep_id,
53        )
54
55        cancel_sweep_response_200.additional_properties = d
56        return cancel_sweep_response_200
additional_keys: list[str]
58    @property
59    def additional_keys(self) -> list[str]:
60        return list(self.additional_properties.keys())
class CancelSweepResponse200Status(builtins.str, enum.Enum):
5class CancelSweepResponse200Status(str, Enum):
6    CANCELLING = "cancelling"
7
8    def __str__(self) -> str:
9        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

CANCELLING = <CancelSweepResponse200Status.CANCELLING: 'cancelling'>
class CompileStrategyResponse200:
19@_attrs_define
20class CompileStrategyResponse200:
21    """
22    Attributes:
23        strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
24            always yields the same id, for every caller, whatever its formatting. See
25            `POST /strategy` for exactly which rewrites preserve it and which do not.
26             Example: 6bsh31ikwkuivhtgcoa6s4.
27        declared_properties (list[DeclaredProperty] | Unset): What could be established about this strategy's sweep-key
28            vocabulary without
29            constructing it. See `DeclaredProperty` — best-effort, not exhaustive.
30    """
31
32    strategy_id: str
33    declared_properties: list[DeclaredProperty] | Unset = UNSET
34    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
35
36    def to_dict(self) -> dict[str, Any]:
37        strategy_id = self.strategy_id
38
39        declared_properties: list[dict[str, Any]] | Unset = UNSET
40        if not isinstance(self.declared_properties, Unset):
41            declared_properties = []
42            for declared_properties_item_data in self.declared_properties:
43                declared_properties_item = declared_properties_item_data.to_dict()
44                declared_properties.append(declared_properties_item)
45
46        field_dict: dict[str, Any] = {}
47        field_dict.update(self.additional_properties)
48        field_dict.update(
49            {
50                "strategyId": strategy_id,
51            }
52        )
53        if declared_properties is not UNSET:
54            field_dict["declaredProperties"] = declared_properties
55
56        return field_dict
57
58    @classmethod
59    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
60        from ..models.declared_property import DeclaredProperty
61
62        d = dict(src_dict)
63        strategy_id = d.pop("strategyId")
64
65        _declared_properties = d.pop("declaredProperties", UNSET)
66        declared_properties: list[DeclaredProperty] | Unset = UNSET
67        if _declared_properties is not UNSET:
68            declared_properties = []
69            for declared_properties_item_data in _declared_properties:
70                declared_properties_item = DeclaredProperty.from_dict(declared_properties_item_data)
71
72                declared_properties.append(declared_properties_item)
73
74        compile_strategy_response_200 = cls(
75            strategy_id=strategy_id,
76            declared_properties=declared_properties,
77        )
78
79        compile_strategy_response_200.additional_properties = d
80        return compile_strategy_response_200
81
82    @property
83    def additional_keys(self) -> list[str]:
84        return list(self.additional_properties.keys())
85
86    def __getitem__(self, key: str) -> Any:
87        return self.additional_properties[key]
88
89    def __setitem__(self, key: str, value: Any) -> None:
90        self.additional_properties[key] = value
91
92    def __delitem__(self, key: str) -> None:
93        del self.additional_properties[key]
94
95    def __contains__(self, key: str) -> bool:
96        return key in self.additional_properties

Attributes: strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code always yields the same id, for every caller, whatever its formatting. See POST /strategy for exactly which rewrites preserve it and which do not. Example: 6bsh31ikwkuivhtgcoa6s4. declared_properties (list[DeclaredProperty] | Unset): What could be established about this strategy's sweep-key vocabulary without constructing it. See DeclaredProperty — best-effort, not exhaustive.

CompileStrategyResponse200( strategy_id: str, declared_properties: list[DeclaredProperty] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
25def __init__(self, strategy_id, declared_properties=attr_dict['declared_properties'].default):
26    self.strategy_id = strategy_id
27    self.declared_properties = declared_properties
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class CompileStrategyResponse200.

strategy_id: str
declared_properties: list[DeclaredProperty] | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
36    def to_dict(self) -> dict[str, Any]:
37        strategy_id = self.strategy_id
38
39        declared_properties: list[dict[str, Any]] | Unset = UNSET
40        if not isinstance(self.declared_properties, Unset):
41            declared_properties = []
42            for declared_properties_item_data in self.declared_properties:
43                declared_properties_item = declared_properties_item_data.to_dict()
44                declared_properties.append(declared_properties_item)
45
46        field_dict: dict[str, Any] = {}
47        field_dict.update(self.additional_properties)
48        field_dict.update(
49            {
50                "strategyId": strategy_id,
51            }
52        )
53        if declared_properties is not UNSET:
54            field_dict["declaredProperties"] = declared_properties
55
56        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
58    @classmethod
59    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
60        from ..models.declared_property import DeclaredProperty
61
62        d = dict(src_dict)
63        strategy_id = d.pop("strategyId")
64
65        _declared_properties = d.pop("declaredProperties", UNSET)
66        declared_properties: list[DeclaredProperty] | Unset = UNSET
67        if _declared_properties is not UNSET:
68            declared_properties = []
69            for declared_properties_item_data in _declared_properties:
70                declared_properties_item = DeclaredProperty.from_dict(declared_properties_item_data)
71
72                declared_properties.append(declared_properties_item)
73
74        compile_strategy_response_200 = cls(
75            strategy_id=strategy_id,
76            declared_properties=declared_properties,
77        )
78
79        compile_strategy_response_200.additional_properties = d
80        return compile_strategy_response_200
additional_keys: list[str]
82    @property
83    def additional_keys(self) -> list[str]:
84        return list(self.additional_properties.keys())
class CoverageWindow:
 17@_attrs_define
 18class CoverageWindow:
 19    """The time range of available data for a single data type
 20
 21    Attributes:
 22        from_ (datetime.datetime | Unset): Earliest timestamp with data available Example: 2026-04-10T21:00:00Z.
 23        to (datetime.datetime | Unset): Latest timestamp with data available Example: 2026-07-09T20:31:08Z.
 24        inactive_since (datetime.datetime | Unset): If the instrument stopped producing this data type
 25            (delisted/inactive), the timestamp it went inactive. Optional — omitted while the instrument is active. Example:
 26            2026-06-30T12:00:00Z.
 27    """
 28
 29    from_: datetime.datetime | Unset = UNSET
 30    to: datetime.datetime | Unset = UNSET
 31    inactive_since: datetime.datetime | Unset = UNSET
 32    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 33
 34    def to_dict(self) -> dict[str, Any]:
 35        from_: str | Unset = UNSET
 36        if not isinstance(self.from_, Unset):
 37            from_ = self.from_.isoformat()
 38
 39        to: str | Unset = UNSET
 40        if not isinstance(self.to, Unset):
 41            to = self.to.isoformat()
 42
 43        inactive_since: str | Unset = UNSET
 44        if not isinstance(self.inactive_since, Unset):
 45            inactive_since = self.inactive_since.isoformat()
 46
 47        field_dict: dict[str, Any] = {}
 48        field_dict.update(self.additional_properties)
 49        field_dict.update({})
 50        if from_ is not UNSET:
 51            field_dict["from"] = from_
 52        if to is not UNSET:
 53            field_dict["to"] = to
 54        if inactive_since is not UNSET:
 55            field_dict["inactiveSince"] = inactive_since
 56
 57        return field_dict
 58
 59    @classmethod
 60    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 61        d = dict(src_dict)
 62        _from_ = d.pop("from", UNSET)
 63        from_: datetime.datetime | Unset
 64        if isinstance(_from_, Unset):
 65            from_ = UNSET
 66        else:
 67            from_ = isoparse(_from_)
 68
 69        _to = d.pop("to", UNSET)
 70        to: datetime.datetime | Unset
 71        if isinstance(_to, Unset):
 72            to = UNSET
 73        else:
 74            to = isoparse(_to)
 75
 76        _inactive_since = d.pop("inactiveSince", UNSET)
 77        inactive_since: datetime.datetime | Unset
 78        if isinstance(_inactive_since, Unset):
 79            inactive_since = UNSET
 80        else:
 81            inactive_since = isoparse(_inactive_since)
 82
 83        coverage_window = cls(
 84            from_=from_,
 85            to=to,
 86            inactive_since=inactive_since,
 87        )
 88
 89        coverage_window.additional_properties = d
 90        return coverage_window
 91
 92    @property
 93    def additional_keys(self) -> list[str]:
 94        return list(self.additional_properties.keys())
 95
 96    def __getitem__(self, key: str) -> Any:
 97        return self.additional_properties[key]
 98
 99    def __setitem__(self, key: str, value: Any) -> None:
100        self.additional_properties[key] = value
101
102    def __delitem__(self, key: str) -> None:
103        del self.additional_properties[key]
104
105    def __contains__(self, key: str) -> bool:
106        return key in self.additional_properties

The time range of available data for a single data type

Attributes: from_ (datetime.datetime | Unset): Earliest timestamp with data available Example: 2026-04-10T21:00:00Z. to (datetime.datetime | Unset): Latest timestamp with data available Example: 2026-07-09T20:31:08Z. inactive_since (datetime.datetime | Unset): If the instrument stopped producing this data type (delisted/inactive), the timestamp it went inactive. Optional — omitted while the instrument is active. Example: 2026-06-30T12:00:00Z.

CoverageWindow( from_: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, to: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, inactive_since: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
26def __init__(self, from_=attr_dict['from_'].default, to=attr_dict['to'].default, inactive_since=attr_dict['inactive_since'].default):
27    self.from_ = from_
28    self.to = to
29    self.inactive_since = inactive_since
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class CoverageWindow.

from_: datetime.datetime | qtsurfer.api.client._generated.types.Unset
to: datetime.datetime | qtsurfer.api.client._generated.types.Unset
inactive_since: datetime.datetime | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
34    def to_dict(self) -> dict[str, Any]:
35        from_: str | Unset = UNSET
36        if not isinstance(self.from_, Unset):
37            from_ = self.from_.isoformat()
38
39        to: str | Unset = UNSET
40        if not isinstance(self.to, Unset):
41            to = self.to.isoformat()
42
43        inactive_since: str | Unset = UNSET
44        if not isinstance(self.inactive_since, Unset):
45            inactive_since = self.inactive_since.isoformat()
46
47        field_dict: dict[str, Any] = {}
48        field_dict.update(self.additional_properties)
49        field_dict.update({})
50        if from_ is not UNSET:
51            field_dict["from"] = from_
52        if to is not UNSET:
53            field_dict["to"] = to
54        if inactive_since is not UNSET:
55            field_dict["inactiveSince"] = inactive_since
56
57        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
59    @classmethod
60    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
61        d = dict(src_dict)
62        _from_ = d.pop("from", UNSET)
63        from_: datetime.datetime | Unset
64        if isinstance(_from_, Unset):
65            from_ = UNSET
66        else:
67            from_ = isoparse(_from_)
68
69        _to = d.pop("to", UNSET)
70        to: datetime.datetime | Unset
71        if isinstance(_to, Unset):
72            to = UNSET
73        else:
74            to = isoparse(_to)
75
76        _inactive_since = d.pop("inactiveSince", UNSET)
77        inactive_since: datetime.datetime | Unset
78        if isinstance(_inactive_since, Unset):
79            inactive_since = UNSET
80        else:
81            inactive_since = isoparse(_inactive_since)
82
83        coverage_window = cls(
84            from_=from_,
85            to=to,
86            inactive_since=inactive_since,
87        )
88
89        coverage_window.additional_properties = d
90        return coverage_window
additional_keys: list[str]
92    @property
93    def additional_keys(self) -> list[str]:
94        return list(self.additional_properties.keys())
class CreateDatasetBody:
13@_attrs_define
14class CreateDatasetBody:
15    """
16    Attributes:
17        name (str): A name unique among your datasets. `409` if already taken. Example: My BTC ticks.
18        instrument (str): Exchange instrument identifier (e.g. a currency pair) Example: BTC/USDT.
19    """
20
21    name: str
22    instrument: str
23    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
24
25    def to_dict(self) -> dict[str, Any]:
26        name = self.name
27
28        instrument = self.instrument
29
30        field_dict: dict[str, Any] = {}
31        field_dict.update(self.additional_properties)
32        field_dict.update(
33            {
34                "name": name,
35                "instrument": instrument,
36            }
37        )
38
39        return field_dict
40
41    @classmethod
42    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
43        d = dict(src_dict)
44        name = d.pop("name")
45
46        instrument = d.pop("instrument")
47
48        create_dataset_body = cls(
49            name=name,
50            instrument=instrument,
51        )
52
53        create_dataset_body.additional_properties = d
54        return create_dataset_body
55
56    @property
57    def additional_keys(self) -> list[str]:
58        return list(self.additional_properties.keys())
59
60    def __getitem__(self, key: str) -> Any:
61        return self.additional_properties[key]
62
63    def __setitem__(self, key: str, value: Any) -> None:
64        self.additional_properties[key] = value
65
66    def __delitem__(self, key: str) -> None:
67        del self.additional_properties[key]
68
69    def __contains__(self, key: str) -> bool:
70        return key in self.additional_properties

Attributes: name (str): A name unique among your datasets. 409 if already taken. Example: My BTC ticks. instrument (str): Exchange instrument identifier (e.g. a currency pair) Example: BTC/USDT.

CreateDatasetBody(name: str, instrument: str)
25def __init__(self, name, instrument):
26    self.name = name
27    self.instrument = instrument
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class CreateDatasetBody.

name: str
instrument: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
25    def to_dict(self) -> dict[str, Any]:
26        name = self.name
27
28        instrument = self.instrument
29
30        field_dict: dict[str, Any] = {}
31        field_dict.update(self.additional_properties)
32        field_dict.update(
33            {
34                "name": name,
35                "instrument": instrument,
36            }
37        )
38
39        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
41    @classmethod
42    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
43        d = dict(src_dict)
44        name = d.pop("name")
45
46        instrument = d.pop("instrument")
47
48        create_dataset_body = cls(
49            name=name,
50            instrument=instrument,
51        )
52
53        create_dataset_body.additional_properties = d
54        return create_dataset_body
additional_keys: list[str]
56    @property
57    def additional_keys(self) -> list[str]:
58        return list(self.additional_properties.keys())
class Dataset:
 18@_attrs_define
 19class Dataset:
 20    """A dataset's own metadata — not its data. `currentVersionId` is what a prepare against
 21    `exchangeId: user` reads by default; see `DatasetVersion` for what a version carries.
 22
 23    `from`/`to`/`cadence` mirror that current version's own discovered range and cadence, so
 24    you don't need a second call to `GET /datasets/{datasetId}/uploads/{uploadId}` just to see
 25    what a dataset covers. Absent until a version exists.
 26
 27        Attributes:
 28            dataset_id (str): Opaque id, returned by `POST /datasets`. Example: ds_3f9a1c2e7b0d4a5f.
 29            name (str): Unique among your datasets. Example: My BTC ticks.
 30            type_ (DatasetType): Always `ticker` in v1. Example: ticker.
 31            instrument (str): Exchange instrument identifier (e.g. a currency pair) Example: BTC/USDT.
 32            created_at (datetime.datetime): When the dataset was created. Example: 2026-08-20T09:00:00Z.
 33            current_version_id (str | Unset): The id of the most recently finalized, successfully ingested version. Absent
 34                until at
 35                least one upload has finished ingesting.
 36                 Example: dsv_8e2b4f19c6a03d7e.
 37            updated_at (datetime.datetime | Unset): When `currentVersionId` last changed. Absent until it has a value.
 38                Example: 2026-08-20T09:04:12Z.
 39            from_ (datetime.datetime | Unset): Start of `currentVersionId`'s own data range, as discovered at ingest time.
 40                Absent
 41                until a version exists.
 42                 Example: 2026-03-01T00:00:00Z.
 43            to (datetime.datetime | Unset): End of `currentVersionId`'s own data range, as discovered at ingest time. Absent
 44                until
 45                a version exists.
 46                 Example: 2026-03-08T00:00:00Z.
 47            cadence (str | Unset): `currentVersionId`'s own discovered bar cadence (e.g. `1s`, `1m`, `1h`). Absent until a
 48                version exists.
 49                 Example: 1m.
 50    """
 51
 52    dataset_id: str
 53    name: str
 54    type_: DatasetType
 55    instrument: str
 56    created_at: datetime.datetime
 57    current_version_id: str | Unset = UNSET
 58    updated_at: datetime.datetime | Unset = UNSET
 59    from_: datetime.datetime | Unset = UNSET
 60    to: datetime.datetime | Unset = UNSET
 61    cadence: str | Unset = UNSET
 62    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 63
 64    def to_dict(self) -> dict[str, Any]:
 65        dataset_id = self.dataset_id
 66
 67        name = self.name
 68
 69        type_ = self.type_.value
 70
 71        instrument = self.instrument
 72
 73        created_at = self.created_at.isoformat()
 74
 75        current_version_id = self.current_version_id
 76
 77        updated_at: str | Unset = UNSET
 78        if not isinstance(self.updated_at, Unset):
 79            updated_at = self.updated_at.isoformat()
 80
 81        from_: str | Unset = UNSET
 82        if not isinstance(self.from_, Unset):
 83            from_ = self.from_.isoformat()
 84
 85        to: str | Unset = UNSET
 86        if not isinstance(self.to, Unset):
 87            to = self.to.isoformat()
 88
 89        cadence = self.cadence
 90
 91        field_dict: dict[str, Any] = {}
 92        field_dict.update(self.additional_properties)
 93        field_dict.update(
 94            {
 95                "datasetId": dataset_id,
 96                "name": name,
 97                "type": type_,
 98                "instrument": instrument,
 99                "createdAt": created_at,
100            }
101        )
102        if current_version_id is not UNSET:
103            field_dict["currentVersionId"] = current_version_id
104        if updated_at is not UNSET:
105            field_dict["updatedAt"] = updated_at
106        if from_ is not UNSET:
107            field_dict["from"] = from_
108        if to is not UNSET:
109            field_dict["to"] = to
110        if cadence is not UNSET:
111            field_dict["cadence"] = cadence
112
113        return field_dict
114
115    @classmethod
116    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
117        d = dict(src_dict)
118        dataset_id = d.pop("datasetId")
119
120        name = d.pop("name")
121
122        type_ = DatasetType(d.pop("type"))
123
124        instrument = d.pop("instrument")
125
126        created_at = isoparse(d.pop("createdAt"))
127
128        current_version_id = d.pop("currentVersionId", UNSET)
129
130        _updated_at = d.pop("updatedAt", UNSET)
131        updated_at: datetime.datetime | Unset
132        if isinstance(_updated_at, Unset):
133            updated_at = UNSET
134        else:
135            updated_at = isoparse(_updated_at)
136
137        _from_ = d.pop("from", UNSET)
138        from_: datetime.datetime | Unset
139        if isinstance(_from_, Unset):
140            from_ = UNSET
141        else:
142            from_ = isoparse(_from_)
143
144        _to = d.pop("to", UNSET)
145        to: datetime.datetime | Unset
146        if isinstance(_to, Unset):
147            to = UNSET
148        else:
149            to = isoparse(_to)
150
151        cadence = d.pop("cadence", UNSET)
152
153        dataset = cls(
154            dataset_id=dataset_id,
155            name=name,
156            type_=type_,
157            instrument=instrument,
158            created_at=created_at,
159            current_version_id=current_version_id,
160            updated_at=updated_at,
161            from_=from_,
162            to=to,
163            cadence=cadence,
164        )
165
166        dataset.additional_properties = d
167        return dataset
168
169    @property
170    def additional_keys(self) -> list[str]:
171        return list(self.additional_properties.keys())
172
173    def __getitem__(self, key: str) -> Any:
174        return self.additional_properties[key]
175
176    def __setitem__(self, key: str, value: Any) -> None:
177        self.additional_properties[key] = value
178
179    def __delitem__(self, key: str) -> None:
180        del self.additional_properties[key]
181
182    def __contains__(self, key: str) -> bool:
183        return key in self.additional_properties

A dataset's own metadata — not its data. currentVersionId is what a prepare against exchangeId: user reads by default; see DatasetVersion for what a version carries.

from/to/cadence mirror that current version's own discovered range and cadence, so you don't need a second call to GET /datasets/{datasetId}/uploads/{uploadId} just to see what a dataset covers. Absent until a version exists.

Attributes:
    dataset_id (str): Opaque id, returned by `POST /datasets`. Example: ds_3f9a1c2e7b0d4a5f.
    name (str): Unique among your datasets. Example: My BTC ticks.
    type_ (DatasetType): Always `ticker` in v1. Example: ticker.
    instrument (str): Exchange instrument identifier (e.g. a currency pair) Example: BTC/USDT.
    created_at (datetime.datetime): When the dataset was created. Example: 2026-08-20T09:00:00Z.
    current_version_id (str | Unset): The id of the most recently finalized, successfully ingested version. Absent
        until at
        least one upload has finished ingesting.
         Example: dsv_8e2b4f19c6a03d7e.
    updated_at (datetime.datetime | Unset): When `currentVersionId` last changed. Absent until it has a value.
        Example: 2026-08-20T09:04:12Z.
    from_ (datetime.datetime | Unset): Start of `currentVersionId`'s own data range, as discovered at ingest time.
        Absent
        until a version exists.
         Example: 2026-03-01T00:00:00Z.
    to (datetime.datetime | Unset): End of `currentVersionId`'s own data range, as discovered at ingest time. Absent
        until
        a version exists.
         Example: 2026-03-08T00:00:00Z.
    cadence (str | Unset): `currentVersionId`'s own discovered bar cadence (e.g. `1s`, `1m`, `1h`). Absent until a
        version exists.
         Example: 1m.
Dataset( dataset_id: str, name: str, type_: DatasetType, instrument: str, created_at: datetime.datetime, current_version_id: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, updated_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, from_: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, to: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, cadence: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
33def __init__(self, dataset_id, name, type_, instrument, created_at, current_version_id=attr_dict['current_version_id'].default, updated_at=attr_dict['updated_at'].default, from_=attr_dict['from_'].default, to=attr_dict['to'].default, cadence=attr_dict['cadence'].default):
34    self.dataset_id = dataset_id
35    self.name = name
36    self.type_ = type_
37    self.instrument = instrument
38    self.created_at = created_at
39    self.current_version_id = current_version_id
40    self.updated_at = updated_at
41    self.from_ = from_
42    self.to = to
43    self.cadence = cadence
44    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class Dataset.

dataset_id: str
name: str
type_: DatasetType
instrument: str
created_at: datetime.datetime
current_version_id: str | qtsurfer.api.client._generated.types.Unset
updated_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset
from_: datetime.datetime | qtsurfer.api.client._generated.types.Unset
to: datetime.datetime | qtsurfer.api.client._generated.types.Unset
cadence: str | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
 64    def to_dict(self) -> dict[str, Any]:
 65        dataset_id = self.dataset_id
 66
 67        name = self.name
 68
 69        type_ = self.type_.value
 70
 71        instrument = self.instrument
 72
 73        created_at = self.created_at.isoformat()
 74
 75        current_version_id = self.current_version_id
 76
 77        updated_at: str | Unset = UNSET
 78        if not isinstance(self.updated_at, Unset):
 79            updated_at = self.updated_at.isoformat()
 80
 81        from_: str | Unset = UNSET
 82        if not isinstance(self.from_, Unset):
 83            from_ = self.from_.isoformat()
 84
 85        to: str | Unset = UNSET
 86        if not isinstance(self.to, Unset):
 87            to = self.to.isoformat()
 88
 89        cadence = self.cadence
 90
 91        field_dict: dict[str, Any] = {}
 92        field_dict.update(self.additional_properties)
 93        field_dict.update(
 94            {
 95                "datasetId": dataset_id,
 96                "name": name,
 97                "type": type_,
 98                "instrument": instrument,
 99                "createdAt": created_at,
100            }
101        )
102        if current_version_id is not UNSET:
103            field_dict["currentVersionId"] = current_version_id
104        if updated_at is not UNSET:
105            field_dict["updatedAt"] = updated_at
106        if from_ is not UNSET:
107            field_dict["from"] = from_
108        if to is not UNSET:
109            field_dict["to"] = to
110        if cadence is not UNSET:
111            field_dict["cadence"] = cadence
112
113        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
115    @classmethod
116    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
117        d = dict(src_dict)
118        dataset_id = d.pop("datasetId")
119
120        name = d.pop("name")
121
122        type_ = DatasetType(d.pop("type"))
123
124        instrument = d.pop("instrument")
125
126        created_at = isoparse(d.pop("createdAt"))
127
128        current_version_id = d.pop("currentVersionId", UNSET)
129
130        _updated_at = d.pop("updatedAt", UNSET)
131        updated_at: datetime.datetime | Unset
132        if isinstance(_updated_at, Unset):
133            updated_at = UNSET
134        else:
135            updated_at = isoparse(_updated_at)
136
137        _from_ = d.pop("from", UNSET)
138        from_: datetime.datetime | Unset
139        if isinstance(_from_, Unset):
140            from_ = UNSET
141        else:
142            from_ = isoparse(_from_)
143
144        _to = d.pop("to", UNSET)
145        to: datetime.datetime | Unset
146        if isinstance(_to, Unset):
147            to = UNSET
148        else:
149            to = isoparse(_to)
150
151        cadence = d.pop("cadence", UNSET)
152
153        dataset = cls(
154            dataset_id=dataset_id,
155            name=name,
156            type_=type_,
157            instrument=instrument,
158            created_at=created_at,
159            current_version_id=current_version_id,
160            updated_at=updated_at,
161            from_=from_,
162            to=to,
163            cadence=cadence,
164        )
165
166        dataset.additional_properties = d
167        return dataset
additional_keys: list[str]
169    @property
170    def additional_keys(self) -> list[str]:
171        return list(self.additional_properties.keys())
class DatasetCreated:
 19@_attrs_define
 20class DatasetCreated:
 21    """The metadata available immediately after creating a dataset, plus its first upload
 22    session — the presigned URL to PUT the file to. Version-derived fields such as
 23    `createdAt`, `currentVersionId`, range, and cadence are available from `GET /datasets/{datasetId}`
 24    after the relevant lifecycle stages, not in this creation response.
 25
 26        Attributes:
 27            upload_id (str): Identifies this upload session. Pass to
 28                `POST /datasets/{datasetId}/uploads/{uploadId}/finalize` once the PUT completes.
 29                 Example: up_1a2b3c4d5e6f7a8b.
 30            upload (DatasetUploadTarget): A presigned destination for uploading a raw dataset file directly to storage.
 31            dataset_id (str): Opaque id of the newly created dataset. Example: ds_3f9a1c2e7b0d4a5f.
 32            name (str): Unique name of the newly created dataset. Example: My BTC ticks.
 33            type_ (DatasetCreatedType): Always `ticker` in v1. Example: ticker.
 34            instrument (str): Exchange instrument identifier (e.g. a currency pair) Example: BTC/USDT.
 35    """
 36
 37    upload_id: str
 38    upload: DatasetUploadTarget
 39    dataset_id: str
 40    name: str
 41    type_: DatasetCreatedType
 42    instrument: str
 43    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 44
 45    def to_dict(self) -> dict[str, Any]:
 46        upload_id = self.upload_id
 47
 48        upload = self.upload.to_dict()
 49
 50        dataset_id = self.dataset_id
 51
 52        name = self.name
 53
 54        type_ = self.type_.value
 55
 56        instrument = self.instrument
 57
 58        field_dict: dict[str, Any] = {}
 59        field_dict.update(self.additional_properties)
 60        field_dict.update(
 61            {
 62                "uploadId": upload_id,
 63                "upload": upload,
 64                "datasetId": dataset_id,
 65                "name": name,
 66                "type": type_,
 67                "instrument": instrument,
 68            }
 69        )
 70
 71        return field_dict
 72
 73    @classmethod
 74    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 75        from ..models.dataset_upload_target import DatasetUploadTarget
 76
 77        d = dict(src_dict)
 78        upload_id = d.pop("uploadId")
 79
 80        upload = DatasetUploadTarget.from_dict(d.pop("upload"))
 81
 82        dataset_id = d.pop("datasetId")
 83
 84        name = d.pop("name")
 85
 86        type_ = DatasetCreatedType(d.pop("type"))
 87
 88        instrument = d.pop("instrument")
 89
 90        dataset_created = cls(
 91            upload_id=upload_id,
 92            upload=upload,
 93            dataset_id=dataset_id,
 94            name=name,
 95            type_=type_,
 96            instrument=instrument,
 97        )
 98
 99        dataset_created.additional_properties = d
100        return dataset_created
101
102    @property
103    def additional_keys(self) -> list[str]:
104        return list(self.additional_properties.keys())
105
106    def __getitem__(self, key: str) -> Any:
107        return self.additional_properties[key]
108
109    def __setitem__(self, key: str, value: Any) -> None:
110        self.additional_properties[key] = value
111
112    def __delitem__(self, key: str) -> None:
113        del self.additional_properties[key]
114
115    def __contains__(self, key: str) -> bool:
116        return key in self.additional_properties

The metadata available immediately after creating a dataset, plus its first upload session — the presigned URL to PUT the file to. Version-derived fields such as createdAt, currentVersionId, range, and cadence are available from GET /datasets/{datasetId} after the relevant lifecycle stages, not in this creation response.

Attributes:
    upload_id (str): Identifies this upload session. Pass to
        `POST /datasets/{datasetId}/uploads/{uploadId}/finalize` once the PUT completes.
         Example: up_1a2b3c4d5e6f7a8b.
    upload (DatasetUploadTarget): A presigned destination for uploading a raw dataset file directly to storage.
    dataset_id (str): Opaque id of the newly created dataset. Example: ds_3f9a1c2e7b0d4a5f.
    name (str): Unique name of the newly created dataset. Example: My BTC ticks.
    type_ (DatasetCreatedType): Always `ticker` in v1. Example: ticker.
    instrument (str): Exchange instrument identifier (e.g. a currency pair) Example: BTC/USDT.
DatasetCreated( upload_id: str, upload: DatasetUploadTarget, dataset_id: str, name: str, type_: DatasetCreatedType, instrument: str)
29def __init__(self, upload_id, upload, dataset_id, name, type_, instrument):
30    self.upload_id = upload_id
31    self.upload = upload
32    self.dataset_id = dataset_id
33    self.name = name
34    self.type_ = type_
35    self.instrument = instrument
36    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DatasetCreated.

upload_id: str
dataset_id: str
name: str
instrument: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
45    def to_dict(self) -> dict[str, Any]:
46        upload_id = self.upload_id
47
48        upload = self.upload.to_dict()
49
50        dataset_id = self.dataset_id
51
52        name = self.name
53
54        type_ = self.type_.value
55
56        instrument = self.instrument
57
58        field_dict: dict[str, Any] = {}
59        field_dict.update(self.additional_properties)
60        field_dict.update(
61            {
62                "uploadId": upload_id,
63                "upload": upload,
64                "datasetId": dataset_id,
65                "name": name,
66                "type": type_,
67                "instrument": instrument,
68            }
69        )
70
71        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 73    @classmethod
 74    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 75        from ..models.dataset_upload_target import DatasetUploadTarget
 76
 77        d = dict(src_dict)
 78        upload_id = d.pop("uploadId")
 79
 80        upload = DatasetUploadTarget.from_dict(d.pop("upload"))
 81
 82        dataset_id = d.pop("datasetId")
 83
 84        name = d.pop("name")
 85
 86        type_ = DatasetCreatedType(d.pop("type"))
 87
 88        instrument = d.pop("instrument")
 89
 90        dataset_created = cls(
 91            upload_id=upload_id,
 92            upload=upload,
 93            dataset_id=dataset_id,
 94            name=name,
 95            type_=type_,
 96            instrument=instrument,
 97        )
 98
 99        dataset_created.additional_properties = d
100        return dataset_created
additional_keys: list[str]
102    @property
103    def additional_keys(self) -> list[str]:
104        return list(self.additional_properties.keys())
class DatasetCreatedType(builtins.str, enum.Enum):
5class DatasetCreatedType(str, Enum):
6    TICKER = "ticker"
7
8    def __str__(self) -> str:
9        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

TICKER = <DatasetCreatedType.TICKER: 'ticker'>
class DatasetType(builtins.str, enum.Enum):
5class DatasetType(str, Enum):
6    TICKER = "ticker"
7
8    def __str__(self) -> str:
9        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

TICKER = <DatasetType.TICKER: 'ticker'>
class DatasetUploadSession:
17@_attrs_define
18class DatasetUploadSession:
19    """An upload session — an id plus the presigned URL to PUT the raw file to. Returned both by
20    `POST /datasets` (as part of the new dataset) and by `POST /datasets/{datasetId}/uploads`
21    (on its own, for an existing one).
22
23        Attributes:
24            upload_id (str): Identifies this upload session. Pass to
25                `POST /datasets/{datasetId}/uploads/{uploadId}/finalize` once the PUT completes.
26                 Example: up_1a2b3c4d5e6f7a8b.
27            upload (DatasetUploadTarget): A presigned destination for uploading a raw dataset file directly to storage.
28    """
29
30    upload_id: str
31    upload: DatasetUploadTarget
32    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
33
34    def to_dict(self) -> dict[str, Any]:
35        upload_id = self.upload_id
36
37        upload = self.upload.to_dict()
38
39        field_dict: dict[str, Any] = {}
40        field_dict.update(self.additional_properties)
41        field_dict.update(
42            {
43                "uploadId": upload_id,
44                "upload": upload,
45            }
46        )
47
48        return field_dict
49
50    @classmethod
51    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
52        from ..models.dataset_upload_target import DatasetUploadTarget
53
54        d = dict(src_dict)
55        upload_id = d.pop("uploadId")
56
57        upload = DatasetUploadTarget.from_dict(d.pop("upload"))
58
59        dataset_upload_session = cls(
60            upload_id=upload_id,
61            upload=upload,
62        )
63
64        dataset_upload_session.additional_properties = d
65        return dataset_upload_session
66
67    @property
68    def additional_keys(self) -> list[str]:
69        return list(self.additional_properties.keys())
70
71    def __getitem__(self, key: str) -> Any:
72        return self.additional_properties[key]
73
74    def __setitem__(self, key: str, value: Any) -> None:
75        self.additional_properties[key] = value
76
77    def __delitem__(self, key: str) -> None:
78        del self.additional_properties[key]
79
80    def __contains__(self, key: str) -> bool:
81        return key in self.additional_properties

An upload session — an id plus the presigned URL to PUT the raw file to. Returned both by POST /datasets (as part of the new dataset) and by POST /datasets/{datasetId}/uploads (on its own, for an existing one).

Attributes:
    upload_id (str): Identifies this upload session. Pass to
        `POST /datasets/{datasetId}/uploads/{uploadId}/finalize` once the PUT completes.
         Example: up_1a2b3c4d5e6f7a8b.
    upload (DatasetUploadTarget): A presigned destination for uploading a raw dataset file directly to storage.
DatasetUploadSession( upload_id: str, upload: DatasetUploadTarget)
25def __init__(self, upload_id, upload):
26    self.upload_id = upload_id
27    self.upload = upload
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DatasetUploadSession.

upload_id: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
34    def to_dict(self) -> dict[str, Any]:
35        upload_id = self.upload_id
36
37        upload = self.upload.to_dict()
38
39        field_dict: dict[str, Any] = {}
40        field_dict.update(self.additional_properties)
41        field_dict.update(
42            {
43                "uploadId": upload_id,
44                "upload": upload,
45            }
46        )
47
48        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
50    @classmethod
51    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
52        from ..models.dataset_upload_target import DatasetUploadTarget
53
54        d = dict(src_dict)
55        upload_id = d.pop("uploadId")
56
57        upload = DatasetUploadTarget.from_dict(d.pop("upload"))
58
59        dataset_upload_session = cls(
60            upload_id=upload_id,
61            upload=upload,
62        )
63
64        dataset_upload_session.additional_properties = d
65        return dataset_upload_session
additional_keys: list[str]
67    @property
68    def additional_keys(self) -> list[str]:
69        return list(self.additional_properties.keys())
class DatasetUploadState:
 20@_attrs_define
 21class DatasetUploadState:
 22    """Progress of one upload, from staged through ingest. Postgres-backed once a version exists,
 23    so `ready`/`failed` are permanent answers; `uploading`/`ingesting` reflect in-flight state
 24    that can itself age out — see the `404` case on `GET .../uploads/{uploadId}`.
 25
 26        Attributes:
 27            upload_id (str):  Example: up_1a2b3c4d5e6f7a8b.
 28            status (DatasetUploadStateStatus): * `uploading` — the file was PUT to the presigned URL, but `finalize` has not
 29                been
 30                  called yet.
 31                * `ingesting` — `finalize` was called; the worker is parsing and validating the file.
 32                * `ready` — ingested successfully. `version` carries the result.
 33                * `failed` — ingest rejected the file (e.g. bad CSV contract, mixed timestamp units).
 34                 Example: ready.
 35            job_id (str | Unset): The ingest job id, while `status` is `ingesting`.
 36            version (DatasetVersion | Unset): One successfully ingested upload. Cadence and timestamp unit are discovered
 37                from the file,
 38                not declared by the caller.
 39    """
 40
 41    upload_id: str
 42    status: DatasetUploadStateStatus
 43    job_id: str | Unset = UNSET
 44    version: DatasetVersion | Unset = UNSET
 45    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 46
 47    def to_dict(self) -> dict[str, Any]:
 48        upload_id = self.upload_id
 49
 50        status = self.status.value
 51
 52        job_id = self.job_id
 53
 54        version: dict[str, Any] | Unset = UNSET
 55        if not isinstance(self.version, Unset):
 56            version = self.version.to_dict()
 57
 58        field_dict: dict[str, Any] = {}
 59        field_dict.update(self.additional_properties)
 60        field_dict.update(
 61            {
 62                "uploadId": upload_id,
 63                "status": status,
 64            }
 65        )
 66        if job_id is not UNSET:
 67            field_dict["jobId"] = job_id
 68        if version is not UNSET:
 69            field_dict["version"] = version
 70
 71        return field_dict
 72
 73    @classmethod
 74    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 75        from ..models.dataset_version import DatasetVersion
 76
 77        d = dict(src_dict)
 78        upload_id = d.pop("uploadId")
 79
 80        status = DatasetUploadStateStatus(d.pop("status"))
 81
 82        job_id = d.pop("jobId", UNSET)
 83
 84        _version = d.pop("version", UNSET)
 85        version: DatasetVersion | Unset
 86        if isinstance(_version, Unset):
 87            version = UNSET
 88        else:
 89            version = DatasetVersion.from_dict(_version)
 90
 91        dataset_upload_state = cls(
 92            upload_id=upload_id,
 93            status=status,
 94            job_id=job_id,
 95            version=version,
 96        )
 97
 98        dataset_upload_state.additional_properties = d
 99        return dataset_upload_state
100
101    @property
102    def additional_keys(self) -> list[str]:
103        return list(self.additional_properties.keys())
104
105    def __getitem__(self, key: str) -> Any:
106        return self.additional_properties[key]
107
108    def __setitem__(self, key: str, value: Any) -> None:
109        self.additional_properties[key] = value
110
111    def __delitem__(self, key: str) -> None:
112        del self.additional_properties[key]
113
114    def __contains__(self, key: str) -> bool:
115        return key in self.additional_properties

Progress of one upload, from staged through ingest. Postgres-backed once a version exists, so ready/failed are permanent answers; uploading/ingesting reflect in-flight state that can itself age out — see the 404 case on GET .../uploads/{uploadId}.

Attributes:
    upload_id (str):  Example: up_1a2b3c4d5e6f7a8b.
    status (DatasetUploadStateStatus): * `uploading` — the file was PUT to the presigned URL, but `finalize` has not
        been
          called yet.
        * `ingesting` — `finalize` was called; the worker is parsing and validating the file.
        * `ready` — ingested successfully. `version` carries the result.
        * `failed` — ingest rejected the file (e.g. bad CSV contract, mixed timestamp units).
         Example: ready.
    job_id (str | Unset): The ingest job id, while `status` is `ingesting`.
    version (DatasetVersion | Unset): One successfully ingested upload. Cadence and timestamp unit are discovered
        from the file,
        not declared by the caller.
DatasetUploadState( upload_id: str, status: DatasetUploadStateStatus, job_id: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, version: DatasetVersion | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
27def __init__(self, upload_id, status, job_id=attr_dict['job_id'].default, version=attr_dict['version'].default):
28    self.upload_id = upload_id
29    self.status = status
30    self.job_id = job_id
31    self.version = version
32    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DatasetUploadState.

upload_id: str
job_id: str | qtsurfer.api.client._generated.types.Unset
version: DatasetVersion | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
47    def to_dict(self) -> dict[str, Any]:
48        upload_id = self.upload_id
49
50        status = self.status.value
51
52        job_id = self.job_id
53
54        version: dict[str, Any] | Unset = UNSET
55        if not isinstance(self.version, Unset):
56            version = self.version.to_dict()
57
58        field_dict: dict[str, Any] = {}
59        field_dict.update(self.additional_properties)
60        field_dict.update(
61            {
62                "uploadId": upload_id,
63                "status": status,
64            }
65        )
66        if job_id is not UNSET:
67            field_dict["jobId"] = job_id
68        if version is not UNSET:
69            field_dict["version"] = version
70
71        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
73    @classmethod
74    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
75        from ..models.dataset_version import DatasetVersion
76
77        d = dict(src_dict)
78        upload_id = d.pop("uploadId")
79
80        status = DatasetUploadStateStatus(d.pop("status"))
81
82        job_id = d.pop("jobId", UNSET)
83
84        _version = d.pop("version", UNSET)
85        version: DatasetVersion | Unset
86        if isinstance(_version, Unset):
87            version = UNSET
88        else:
89            version = DatasetVersion.from_dict(_version)
90
91        dataset_upload_state = cls(
92            upload_id=upload_id,
93            status=status,
94            job_id=job_id,
95            version=version,
96        )
97
98        dataset_upload_state.additional_properties = d
99        return dataset_upload_state
additional_keys: list[str]
101    @property
102    def additional_keys(self) -> list[str]:
103        return list(self.additional_properties.keys())
class DatasetUploadStateStatus(builtins.str, enum.Enum):
 5class DatasetUploadStateStatus(str, Enum):
 6    FAILED = "failed"
 7    INGESTING = "ingesting"
 8    READY = "ready"
 9    UPLOADING = "uploading"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

FAILED = <DatasetUploadStateStatus.FAILED: 'failed'>
INGESTING = <DatasetUploadStateStatus.INGESTING: 'ingesting'>
READY = <DatasetUploadStateStatus.READY: 'ready'>
UPLOADING = <DatasetUploadStateStatus.UPLOADING: 'uploading'>
class DatasetUploadTarget:
13@_attrs_define
14class DatasetUploadTarget:
15    """A presigned destination for uploading a raw dataset file directly to storage.
16
17    Attributes:
18        url (str): Presigned URL. `PUT` the raw CSV file here directly — no `Authorization` header,
19            no other API credentials.
20             Example: https://storage.qtsurfer.com/uploads/00000000-.../up_1a2b3c4d5e6f7a8b/raw.csv?X-Amz-....
21        expires_in_minutes (int): How long `url` stays valid. Example: 15.
22    """
23
24    url: str
25    expires_in_minutes: int
26    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
27
28    def to_dict(self) -> dict[str, Any]:
29        url = self.url
30
31        expires_in_minutes = self.expires_in_minutes
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "url": url,
38                "expiresInMinutes": expires_in_minutes,
39            }
40        )
41
42        return field_dict
43
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        url = d.pop("url")
48
49        expires_in_minutes = d.pop("expiresInMinutes")
50
51        dataset_upload_target = cls(
52            url=url,
53            expires_in_minutes=expires_in_minutes,
54        )
55
56        dataset_upload_target.additional_properties = d
57        return dataset_upload_target
58
59    @property
60    def additional_keys(self) -> list[str]:
61        return list(self.additional_properties.keys())
62
63    def __getitem__(self, key: str) -> Any:
64        return self.additional_properties[key]
65
66    def __setitem__(self, key: str, value: Any) -> None:
67        self.additional_properties[key] = value
68
69    def __delitem__(self, key: str) -> None:
70        del self.additional_properties[key]
71
72    def __contains__(self, key: str) -> bool:
73        return key in self.additional_properties

A presigned destination for uploading a raw dataset file directly to storage.

Attributes: url (str): Presigned URL. PUT the raw CSV file here directly — no Authorization header, no other API credentials. Example: https://storage.qtsurfer.com/uploads/00000000-.../up_1a2b3c4d5e6f7a8b/raw.csv?X-Amz-.... expires_in_minutes (int): How long url stays valid. Example: 15.

DatasetUploadTarget(url: str, expires_in_minutes: int)
25def __init__(self, url, expires_in_minutes):
26    self.url = url
27    self.expires_in_minutes = expires_in_minutes
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DatasetUploadTarget.

url: str
expires_in_minutes: int
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
28    def to_dict(self) -> dict[str, Any]:
29        url = self.url
30
31        expires_in_minutes = self.expires_in_minutes
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "url": url,
38                "expiresInMinutes": expires_in_minutes,
39            }
40        )
41
42        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        url = d.pop("url")
48
49        expires_in_minutes = d.pop("expiresInMinutes")
50
51        dataset_upload_target = cls(
52            url=url,
53            expires_in_minutes=expires_in_minutes,
54        )
55
56        dataset_upload_target.additional_properties = d
57        return dataset_upload_target
additional_keys: list[str]
59    @property
60    def additional_keys(self) -> list[str]:
61        return list(self.additional_properties.keys())
class DatasetVersion:
 16@_attrs_define
 17class DatasetVersion:
 18    """One successfully ingested upload. Cadence and timestamp unit are discovered from the file,
 19    not declared by the caller.
 20
 21        Attributes:
 22            dataset_id (str):  Example: ds_3f9a1c2e7b0d4a5f.
 23            id (str | Unset): The version id. Pass as `datasetVersionId` on `POST .../prepare` to pin it. Example:
 24                dsv_8e2b4f19c6a03d7e.
 25            bytes_ (int | Unset): Size of the uploaded file. Example: 4831022.
 26            rows (int | Unset): Number of data rows. Example: 86400.
 27            cadence (str | Unset): The discovered bar cadence (e.g. `1s`, `1m`, `1h`). Example: 1s.
 28            timestamp_unit (DatasetVersionTimestampUnit | Unset): The unit the `timestamp` column was uploaded in —
 29                ISO-8601, or the epoch band its
 30                numeric values fell in (seconds, millis, or micros).
 31                 Example: iso.
 32            gaps (int | Unset): Number of gaps at the discovered cadence.
 33            largest_gap_steps (int | Unset): The largest gap, in units of the discovered cadence step.
 34    """
 35
 36    dataset_id: str
 37    id: str | Unset = UNSET
 38    bytes_: int | Unset = UNSET
 39    rows: int | Unset = UNSET
 40    cadence: str | Unset = UNSET
 41    timestamp_unit: DatasetVersionTimestampUnit | Unset = UNSET
 42    gaps: int | Unset = UNSET
 43    largest_gap_steps: int | Unset = UNSET
 44    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 45
 46    def to_dict(self) -> dict[str, Any]:
 47        dataset_id = self.dataset_id
 48
 49        id = self.id
 50
 51        bytes_ = self.bytes_
 52
 53        rows = self.rows
 54
 55        cadence = self.cadence
 56
 57        timestamp_unit: str | Unset = UNSET
 58        if not isinstance(self.timestamp_unit, Unset):
 59            timestamp_unit = self.timestamp_unit.value
 60
 61        gaps = self.gaps
 62
 63        largest_gap_steps = self.largest_gap_steps
 64
 65        field_dict: dict[str, Any] = {}
 66        field_dict.update(self.additional_properties)
 67        field_dict.update(
 68            {
 69                "datasetId": dataset_id,
 70            }
 71        )
 72        if id is not UNSET:
 73            field_dict["id"] = id
 74        if bytes_ is not UNSET:
 75            field_dict["bytes"] = bytes_
 76        if rows is not UNSET:
 77            field_dict["rows"] = rows
 78        if cadence is not UNSET:
 79            field_dict["cadence"] = cadence
 80        if timestamp_unit is not UNSET:
 81            field_dict["timestampUnit"] = timestamp_unit
 82        if gaps is not UNSET:
 83            field_dict["gaps"] = gaps
 84        if largest_gap_steps is not UNSET:
 85            field_dict["largestGapSteps"] = largest_gap_steps
 86
 87        return field_dict
 88
 89    @classmethod
 90    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 91        d = dict(src_dict)
 92        dataset_id = d.pop("datasetId")
 93
 94        id = d.pop("id", UNSET)
 95
 96        bytes_ = d.pop("bytes", UNSET)
 97
 98        rows = d.pop("rows", UNSET)
 99
100        cadence = d.pop("cadence", UNSET)
101
102        _timestamp_unit = d.pop("timestampUnit", UNSET)
103        timestamp_unit: DatasetVersionTimestampUnit | Unset
104        if isinstance(_timestamp_unit, Unset):
105            timestamp_unit = UNSET
106        else:
107            timestamp_unit = DatasetVersionTimestampUnit(_timestamp_unit)
108
109        gaps = d.pop("gaps", UNSET)
110
111        largest_gap_steps = d.pop("largestGapSteps", UNSET)
112
113        dataset_version = cls(
114            dataset_id=dataset_id,
115            id=id,
116            bytes_=bytes_,
117            rows=rows,
118            cadence=cadence,
119            timestamp_unit=timestamp_unit,
120            gaps=gaps,
121            largest_gap_steps=largest_gap_steps,
122        )
123
124        dataset_version.additional_properties = d
125        return dataset_version
126
127    @property
128    def additional_keys(self) -> list[str]:
129        return list(self.additional_properties.keys())
130
131    def __getitem__(self, key: str) -> Any:
132        return self.additional_properties[key]
133
134    def __setitem__(self, key: str, value: Any) -> None:
135        self.additional_properties[key] = value
136
137    def __delitem__(self, key: str) -> None:
138        del self.additional_properties[key]
139
140    def __contains__(self, key: str) -> bool:
141        return key in self.additional_properties

One successfully ingested upload. Cadence and timestamp unit are discovered from the file, not declared by the caller.

Attributes:
    dataset_id (str):  Example: ds_3f9a1c2e7b0d4a5f.
    id (str | Unset): The version id. Pass as `datasetVersionId` on `POST .../prepare` to pin it. Example:
        dsv_8e2b4f19c6a03d7e.
    bytes_ (int | Unset): Size of the uploaded file. Example: 4831022.
    rows (int | Unset): Number of data rows. Example: 86400.
    cadence (str | Unset): The discovered bar cadence (e.g. `1s`, `1m`, `1h`). Example: 1s.
    timestamp_unit (DatasetVersionTimestampUnit | Unset): The unit the `timestamp` column was uploaded in —
        ISO-8601, or the epoch band its
        numeric values fell in (seconds, millis, or micros).
         Example: iso.
    gaps (int | Unset): Number of gaps at the discovered cadence.
    largest_gap_steps (int | Unset): The largest gap, in units of the discovered cadence step.
DatasetVersion( dataset_id: str, id: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, bytes_: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, rows: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, cadence: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, timestamp_unit: DatasetVersionTimestampUnit | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, gaps: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, largest_gap_steps: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
31def __init__(self, dataset_id, id=attr_dict['id'].default, bytes_=attr_dict['bytes_'].default, rows=attr_dict['rows'].default, cadence=attr_dict['cadence'].default, timestamp_unit=attr_dict['timestamp_unit'].default, gaps=attr_dict['gaps'].default, largest_gap_steps=attr_dict['largest_gap_steps'].default):
32    self.dataset_id = dataset_id
33    self.id = id
34    self.bytes_ = bytes_
35    self.rows = rows
36    self.cadence = cadence
37    self.timestamp_unit = timestamp_unit
38    self.gaps = gaps
39    self.largest_gap_steps = largest_gap_steps
40    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DatasetVersion.

dataset_id: str
id: str | qtsurfer.api.client._generated.types.Unset
bytes_: int | qtsurfer.api.client._generated.types.Unset
rows: int | qtsurfer.api.client._generated.types.Unset
cadence: str | qtsurfer.api.client._generated.types.Unset
timestamp_unit: DatasetVersionTimestampUnit | qtsurfer.api.client._generated.types.Unset
gaps: int | qtsurfer.api.client._generated.types.Unset
largest_gap_steps: int | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
46    def to_dict(self) -> dict[str, Any]:
47        dataset_id = self.dataset_id
48
49        id = self.id
50
51        bytes_ = self.bytes_
52
53        rows = self.rows
54
55        cadence = self.cadence
56
57        timestamp_unit: str | Unset = UNSET
58        if not isinstance(self.timestamp_unit, Unset):
59            timestamp_unit = self.timestamp_unit.value
60
61        gaps = self.gaps
62
63        largest_gap_steps = self.largest_gap_steps
64
65        field_dict: dict[str, Any] = {}
66        field_dict.update(self.additional_properties)
67        field_dict.update(
68            {
69                "datasetId": dataset_id,
70            }
71        )
72        if id is not UNSET:
73            field_dict["id"] = id
74        if bytes_ is not UNSET:
75            field_dict["bytes"] = bytes_
76        if rows is not UNSET:
77            field_dict["rows"] = rows
78        if cadence is not UNSET:
79            field_dict["cadence"] = cadence
80        if timestamp_unit is not UNSET:
81            field_dict["timestampUnit"] = timestamp_unit
82        if gaps is not UNSET:
83            field_dict["gaps"] = gaps
84        if largest_gap_steps is not UNSET:
85            field_dict["largestGapSteps"] = largest_gap_steps
86
87        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 89    @classmethod
 90    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 91        d = dict(src_dict)
 92        dataset_id = d.pop("datasetId")
 93
 94        id = d.pop("id", UNSET)
 95
 96        bytes_ = d.pop("bytes", UNSET)
 97
 98        rows = d.pop("rows", UNSET)
 99
100        cadence = d.pop("cadence", UNSET)
101
102        _timestamp_unit = d.pop("timestampUnit", UNSET)
103        timestamp_unit: DatasetVersionTimestampUnit | Unset
104        if isinstance(_timestamp_unit, Unset):
105            timestamp_unit = UNSET
106        else:
107            timestamp_unit = DatasetVersionTimestampUnit(_timestamp_unit)
108
109        gaps = d.pop("gaps", UNSET)
110
111        largest_gap_steps = d.pop("largestGapSteps", UNSET)
112
113        dataset_version = cls(
114            dataset_id=dataset_id,
115            id=id,
116            bytes_=bytes_,
117            rows=rows,
118            cadence=cadence,
119            timestamp_unit=timestamp_unit,
120            gaps=gaps,
121            largest_gap_steps=largest_gap_steps,
122        )
123
124        dataset_version.additional_properties = d
125        return dataset_version
additional_keys: list[str]
127    @property
128    def additional_keys(self) -> list[str]:
129        return list(self.additional_properties.keys())
class DatasetVersionTimestampUnit(builtins.str, enum.Enum):
 5class DatasetVersionTimestampUnit(str, Enum):
 6    ISO = "iso"
 7    MS = "ms"
 8    S = "s"
 9    US = "us"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

class DatasetWithLinksLinksSelf:
15@_attrs_define
16class DatasetWithLinksLinksSelf:
17    """
18    Attributes:
19        href (str | Unset):  Example: /v1/datasets/ds_3f9a1c2e7b0d4a5f.
20    """
21
22    href: str | Unset = UNSET
23    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
24
25    def to_dict(self) -> dict[str, Any]:
26        href = self.href
27
28        field_dict: dict[str, Any] = {}
29        field_dict.update(self.additional_properties)
30        field_dict.update({})
31        if href is not UNSET:
32            field_dict["href"] = href
33
34        return field_dict
35
36    @classmethod
37    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
38        d = dict(src_dict)
39        href = d.pop("href", UNSET)
40
41        dataset_with_links_links_self = cls(
42            href=href,
43        )
44
45        dataset_with_links_links_self.additional_properties = d
46        return dataset_with_links_links_self
47
48    @property
49    def additional_keys(self) -> list[str]:
50        return list(self.additional_properties.keys())
51
52    def __getitem__(self, key: str) -> Any:
53        return self.additional_properties[key]
54
55    def __setitem__(self, key: str, value: Any) -> None:
56        self.additional_properties[key] = value
57
58    def __delitem__(self, key: str) -> None:
59        del self.additional_properties[key]
60
61    def __contains__(self, key: str) -> bool:
62        return key in self.additional_properties

Attributes: href (str | Unset): Example: /v1/datasets/ds_3f9a1c2e7b0d4a5f.

DatasetWithLinksLinksSelf( href: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
24def __init__(self, href=attr_dict['href'].default):
25    self.href = href
26    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DatasetWithLinksLinksSelf.

href: str | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
25    def to_dict(self) -> dict[str, Any]:
26        href = self.href
27
28        field_dict: dict[str, Any] = {}
29        field_dict.update(self.additional_properties)
30        field_dict.update({})
31        if href is not UNSET:
32            field_dict["href"] = href
33
34        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
36    @classmethod
37    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
38        d = dict(src_dict)
39        href = d.pop("href", UNSET)
40
41        dataset_with_links_links_self = cls(
42            href=href,
43        )
44
45        dataset_with_links_links_self.additional_properties = d
46        return dataset_with_links_links_self
additional_keys: list[str]
48    @property
49    def additional_keys(self) -> list[str]:
50        return list(self.additional_properties.keys())
class DataSourceType(builtins.str, enum.Enum):
5class DataSourceType(str, Enum):
6    TICKER = "ticker"
7
8    def __str__(self) -> str:
9        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

TICKER = <DataSourceType.TICKER: 'ticker'>
class DeclaredProperty:
 15@_attrs_define
 16class DeclaredProperty:
 17    """One property name `POST /strategy` could establish without constructing the strategy —
 18    either declared with `@StrategyProperty` on the compiled source, or one of the small set of
 19    base properties every strategy carries (`amnt`, `enabled`, `multiEntry`, ...).
 20
 21    **Best-effort, not exhaustive.** A property registered through an attached risk/backtest
 22    config needs a live instance to discover and is not listed here. Use this to catch a typo'd
 23    sweep key before submitting, not as the definitive list of what a sweep will accept — a
 24    name absent from this list may still be valid.
 25
 26        Attributes:
 27            name (str): The key a sweep or execute param map uses for this property. Example: rsi.period.
 28            description (str | Unset): Human-readable label, as declared. Example: RSI period.
 29            default_value (str | Unset): The declared default, as a string, if one was given. Absent, not null, when none
 30                was
 31                declared.
 32                 Example: 14.
 33            reflected (bool | Unset): Whether a value for this key is injected into the strategy's field (`true`) or only
 34                available through the property map (`false`).
 35                 Example: True.
 36            min_ (float | Unset): Suggested sweep/range minimum, if declared. Advisory only, never validated. Example: 2.
 37            max_ (float | Unset): Suggested sweep/range maximum, if declared. Advisory only, never validated. Example: 50.
 38            step (float | Unset): Suggested sweep/range step, if declared. Advisory only, never validated. Example: 1.
 39    """
 40
 41    name: str
 42    description: str | Unset = UNSET
 43    default_value: str | Unset = UNSET
 44    reflected: bool | Unset = UNSET
 45    min_: float | Unset = UNSET
 46    max_: float | Unset = UNSET
 47    step: float | Unset = UNSET
 48    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 49
 50    def to_dict(self) -> dict[str, Any]:
 51        name = self.name
 52
 53        description = self.description
 54
 55        default_value = self.default_value
 56
 57        reflected = self.reflected
 58
 59        min_ = self.min_
 60
 61        max_ = self.max_
 62
 63        step = self.step
 64
 65        field_dict: dict[str, Any] = {}
 66        field_dict.update(self.additional_properties)
 67        field_dict.update(
 68            {
 69                "name": name,
 70            }
 71        )
 72        if description is not UNSET:
 73            field_dict["description"] = description
 74        if default_value is not UNSET:
 75            field_dict["defaultValue"] = default_value
 76        if reflected is not UNSET:
 77            field_dict["reflected"] = reflected
 78        if min_ is not UNSET:
 79            field_dict["min"] = min_
 80        if max_ is not UNSET:
 81            field_dict["max"] = max_
 82        if step is not UNSET:
 83            field_dict["step"] = step
 84
 85        return field_dict
 86
 87    @classmethod
 88    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 89        d = dict(src_dict)
 90        name = d.pop("name")
 91
 92        description = d.pop("description", UNSET)
 93
 94        default_value = d.pop("defaultValue", UNSET)
 95
 96        reflected = d.pop("reflected", UNSET)
 97
 98        min_ = d.pop("min", UNSET)
 99
100        max_ = d.pop("max", UNSET)
101
102        step = d.pop("step", UNSET)
103
104        declared_property = cls(
105            name=name,
106            description=description,
107            default_value=default_value,
108            reflected=reflected,
109            min_=min_,
110            max_=max_,
111            step=step,
112        )
113
114        declared_property.additional_properties = d
115        return declared_property
116
117    @property
118    def additional_keys(self) -> list[str]:
119        return list(self.additional_properties.keys())
120
121    def __getitem__(self, key: str) -> Any:
122        return self.additional_properties[key]
123
124    def __setitem__(self, key: str, value: Any) -> None:
125        self.additional_properties[key] = value
126
127    def __delitem__(self, key: str) -> None:
128        del self.additional_properties[key]
129
130    def __contains__(self, key: str) -> bool:
131        return key in self.additional_properties

One property name POST /strategy could establish without constructing the strategy — either declared with @StrategyProperty on the compiled source, or one of the small set of base properties every strategy carries (amnt, enabled, multiEntry, ...).

Best-effort, not exhaustive. A property registered through an attached risk/backtest config needs a live instance to discover and is not listed here. Use this to catch a typo'd sweep key before submitting, not as the definitive list of what a sweep will accept — a name absent from this list may still be valid.

Attributes:
    name (str): The key a sweep or execute param map uses for this property. Example: rsi.period.
    description (str | Unset): Human-readable label, as declared. Example: RSI period.
    default_value (str | Unset): The declared default, as a string, if one was given. Absent, not null, when none
        was
        declared.
         Example: 14.
    reflected (bool | Unset): Whether a value for this key is injected into the strategy's field (`true`) or only
        available through the property map (`false`).
         Example: True.
    min_ (float | Unset): Suggested sweep/range minimum, if declared. Advisory only, never validated. Example: 2.
    max_ (float | Unset): Suggested sweep/range maximum, if declared. Advisory only, never validated. Example: 50.
    step (float | Unset): Suggested sweep/range step, if declared. Advisory only, never validated. Example: 1.
DeclaredProperty( name: str, description: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, default_value: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, reflected: bool | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, min_: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, max_: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, step: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
30def __init__(self, name, description=attr_dict['description'].default, default_value=attr_dict['default_value'].default, reflected=attr_dict['reflected'].default, min_=attr_dict['min_'].default, max_=attr_dict['max_'].default, step=attr_dict['step'].default):
31    self.name = name
32    self.description = description
33    self.default_value = default_value
34    self.reflected = reflected
35    self.min_ = min_
36    self.max_ = max_
37    self.step = step
38    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DeclaredProperty.

name: str
description: str | qtsurfer.api.client._generated.types.Unset
default_value: str | qtsurfer.api.client._generated.types.Unset
reflected: bool | qtsurfer.api.client._generated.types.Unset
min_: float | qtsurfer.api.client._generated.types.Unset
max_: float | qtsurfer.api.client._generated.types.Unset
step: float | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
50    def to_dict(self) -> dict[str, Any]:
51        name = self.name
52
53        description = self.description
54
55        default_value = self.default_value
56
57        reflected = self.reflected
58
59        min_ = self.min_
60
61        max_ = self.max_
62
63        step = self.step
64
65        field_dict: dict[str, Any] = {}
66        field_dict.update(self.additional_properties)
67        field_dict.update(
68            {
69                "name": name,
70            }
71        )
72        if description is not UNSET:
73            field_dict["description"] = description
74        if default_value is not UNSET:
75            field_dict["defaultValue"] = default_value
76        if reflected is not UNSET:
77            field_dict["reflected"] = reflected
78        if min_ is not UNSET:
79            field_dict["min"] = min_
80        if max_ is not UNSET:
81            field_dict["max"] = max_
82        if step is not UNSET:
83            field_dict["step"] = step
84
85        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 87    @classmethod
 88    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 89        d = dict(src_dict)
 90        name = d.pop("name")
 91
 92        description = d.pop("description", UNSET)
 93
 94        default_value = d.pop("defaultValue", UNSET)
 95
 96        reflected = d.pop("reflected", UNSET)
 97
 98        min_ = d.pop("min", UNSET)
 99
100        max_ = d.pop("max", UNSET)
101
102        step = d.pop("step", UNSET)
103
104        declared_property = cls(
105            name=name,
106            description=description,
107            default_value=default_value,
108            reflected=reflected,
109            min_=min_,
110            max_=max_,
111            step=step,
112        )
113
114        declared_property.additional_properties = d
115        return declared_property
additional_keys: list[str]
117    @property
118    def additional_keys(self) -> list[str]:
119        return list(self.additional_properties.keys())
class DeleteDatasetResponse200:
13@_attrs_define
14class DeleteDatasetResponse200:
15    """
16    Attributes:
17        dataset_id (str):
18        deleted (bool):
19    """
20
21    dataset_id: str
22    deleted: bool
23    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
24
25    def to_dict(self) -> dict[str, Any]:
26        dataset_id = self.dataset_id
27
28        deleted = self.deleted
29
30        field_dict: dict[str, Any] = {}
31        field_dict.update(self.additional_properties)
32        field_dict.update(
33            {
34                "datasetId": dataset_id,
35                "deleted": deleted,
36            }
37        )
38
39        return field_dict
40
41    @classmethod
42    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
43        d = dict(src_dict)
44        dataset_id = d.pop("datasetId")
45
46        deleted = d.pop("deleted")
47
48        delete_dataset_response_200 = cls(
49            dataset_id=dataset_id,
50            deleted=deleted,
51        )
52
53        delete_dataset_response_200.additional_properties = d
54        return delete_dataset_response_200
55
56    @property
57    def additional_keys(self) -> list[str]:
58        return list(self.additional_properties.keys())
59
60    def __getitem__(self, key: str) -> Any:
61        return self.additional_properties[key]
62
63    def __setitem__(self, key: str, value: Any) -> None:
64        self.additional_properties[key] = value
65
66    def __delitem__(self, key: str) -> None:
67        del self.additional_properties[key]
68
69    def __contains__(self, key: str) -> bool:
70        return key in self.additional_properties

Attributes: dataset_id (str): deleted (bool):

DeleteDatasetResponse200(dataset_id: str, deleted: bool)
25def __init__(self, dataset_id, deleted):
26    self.dataset_id = dataset_id
27    self.deleted = deleted
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DeleteDatasetResponse200.

dataset_id: str
deleted: bool
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
25    def to_dict(self) -> dict[str, Any]:
26        dataset_id = self.dataset_id
27
28        deleted = self.deleted
29
30        field_dict: dict[str, Any] = {}
31        field_dict.update(self.additional_properties)
32        field_dict.update(
33            {
34                "datasetId": dataset_id,
35                "deleted": deleted,
36            }
37        )
38
39        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
41    @classmethod
42    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
43        d = dict(src_dict)
44        dataset_id = d.pop("datasetId")
45
46        deleted = d.pop("deleted")
47
48        delete_dataset_response_200 = cls(
49            dataset_id=dataset_id,
50            deleted=deleted,
51        )
52
53        delete_dataset_response_200.additional_properties = d
54        return delete_dataset_response_200
additional_keys: list[str]
56    @property
57    def additional_keys(self) -> list[str]:
58        return list(self.additional_properties.keys())
class DeleteStrategyResponse200:
13@_attrs_define
14class DeleteStrategyResponse200:
15    """
16    Attributes:
17        strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
18            always yields the same id, for every caller, whatever its formatting. See
19            `POST /strategy` for exactly which rewrites preserve it and which do not.
20             Example: 6bsh31ikwkuivhtgcoa6s4.
21        deleted (bool):
22    """
23
24    strategy_id: str
25    deleted: bool
26    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
27
28    def to_dict(self) -> dict[str, Any]:
29        strategy_id = self.strategy_id
30
31        deleted = self.deleted
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "strategyId": strategy_id,
38                "deleted": deleted,
39            }
40        )
41
42        return field_dict
43
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        strategy_id = d.pop("strategyId")
48
49        deleted = d.pop("deleted")
50
51        delete_strategy_response_200 = cls(
52            strategy_id=strategy_id,
53            deleted=deleted,
54        )
55
56        delete_strategy_response_200.additional_properties = d
57        return delete_strategy_response_200
58
59    @property
60    def additional_keys(self) -> list[str]:
61        return list(self.additional_properties.keys())
62
63    def __getitem__(self, key: str) -> Any:
64        return self.additional_properties[key]
65
66    def __setitem__(self, key: str, value: Any) -> None:
67        self.additional_properties[key] = value
68
69    def __delitem__(self, key: str) -> None:
70        del self.additional_properties[key]
71
72    def __contains__(self, key: str) -> bool:
73        return key in self.additional_properties

Attributes: strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code always yields the same id, for every caller, whatever its formatting. See POST /strategy for exactly which rewrites preserve it and which do not. Example: 6bsh31ikwkuivhtgcoa6s4. deleted (bool):

DeleteStrategyResponse200(strategy_id: str, deleted: bool)
25def __init__(self, strategy_id, deleted):
26    self.strategy_id = strategy_id
27    self.deleted = deleted
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class DeleteStrategyResponse200.

strategy_id: str
deleted: bool
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
28    def to_dict(self) -> dict[str, Any]:
29        strategy_id = self.strategy_id
30
31        deleted = self.deleted
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "strategyId": strategy_id,
38                "deleted": deleted,
39            }
40        )
41
42        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        strategy_id = d.pop("strategyId")
48
49        deleted = d.pop("deleted")
50
51        delete_strategy_response_200 = cls(
52            strategy_id=strategy_id,
53            deleted=deleted,
54        )
55
56        delete_strategy_response_200.additional_properties = d
57        return delete_strategy_response_200
additional_keys: list[str]
59    @property
60    def additional_keys(self) -> list[str]:
61        return list(self.additional_properties.keys())
class DownloadKlinesFormat(builtins.str, enum.Enum):
 5class DownloadKlinesFormat(str, Enum):
 6    LASTRA = "lastra"
 7    PARQUET = "parquet"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

LASTRA = <DownloadKlinesFormat.LASTRA: 'lastra'>
PARQUET = <DownloadKlinesFormat.PARQUET: 'parquet'>
class DownloadTickersFormat(builtins.str, enum.Enum):
 5class DownloadTickersFormat(str, Enum):
 6    LASTRA = "lastra"
 7    PARQUET = "parquet"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

LASTRA = <DownloadTickersFormat.LASTRA: 'lastra'>
PARQUET = <DownloadTickersFormat.PARQUET: 'parquet'>
class EquityCurveMeta:
 15@_attrs_define
 16class EquityCurveMeta:
 17    """What the transform pipeline actually did, computed from the observed outcome — never a copy of what was requested.
 18    Lets a caller detect a forced or no-op transform (e.g. a `resample` ceiling already above the curve's size is a
 19    legal no-op, reported honestly as `resampled: false`).
 20
 21        Attributes:
 22            input_point_count (int): Size of the curve the transform pipeline received. Example: 100000.
 23            output_point_count (int): Size after the full pipeline (resample, then differential, then outMode). Example:
 24                100.
 25            resampled (bool): True only if the resample stage actually changed the point count.
 26            differential (bool): True only if delta-encoding actually ran. Requesting it on a curve of 0 or 1 points has
 27                nothing to encode, so it does not run even if asked.
 28            out_mode (EquityCurveOutMode): JSON shape for an equity curve's points. `ARRAY` is `[{timestamp, equity}, ...]`;
 29                `SHORT` is `{timestamps: [...], equities: [...]}` (parallel arrays, no repeated key text). The one schema shared
 30                by every place `outMode` appears, request or response, so the two cannot drift to different value sets.
 31    """
 32
 33    input_point_count: int
 34    output_point_count: int
 35    resampled: bool
 36    differential: bool
 37    out_mode: EquityCurveOutMode
 38    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 39
 40    def to_dict(self) -> dict[str, Any]:
 41        input_point_count = self.input_point_count
 42
 43        output_point_count = self.output_point_count
 44
 45        resampled = self.resampled
 46
 47        differential = self.differential
 48
 49        out_mode = self.out_mode.value
 50
 51        field_dict: dict[str, Any] = {}
 52        field_dict.update(self.additional_properties)
 53        field_dict.update(
 54            {
 55                "inputPointCount": input_point_count,
 56                "outputPointCount": output_point_count,
 57                "resampled": resampled,
 58                "differential": differential,
 59                "outMode": out_mode,
 60            }
 61        )
 62
 63        return field_dict
 64
 65    @classmethod
 66    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 67        d = dict(src_dict)
 68        input_point_count = d.pop("inputPointCount")
 69
 70        output_point_count = d.pop("outputPointCount")
 71
 72        resampled = d.pop("resampled")
 73
 74        differential = d.pop("differential")
 75
 76        out_mode = EquityCurveOutMode(d.pop("outMode"))
 77
 78        equity_curve_meta = cls(
 79            input_point_count=input_point_count,
 80            output_point_count=output_point_count,
 81            resampled=resampled,
 82            differential=differential,
 83            out_mode=out_mode,
 84        )
 85
 86        equity_curve_meta.additional_properties = d
 87        return equity_curve_meta
 88
 89    @property
 90    def additional_keys(self) -> list[str]:
 91        return list(self.additional_properties.keys())
 92
 93    def __getitem__(self, key: str) -> Any:
 94        return self.additional_properties[key]
 95
 96    def __setitem__(self, key: str, value: Any) -> None:
 97        self.additional_properties[key] = value
 98
 99    def __delitem__(self, key: str) -> None:
100        del self.additional_properties[key]
101
102    def __contains__(self, key: str) -> bool:
103        return key in self.additional_properties

What the transform pipeline actually did, computed from the observed outcome — never a copy of what was requested. Lets a caller detect a forced or no-op transform (e.g. a resample ceiling already above the curve's size is a legal no-op, reported honestly as resampled: false).

Attributes:
    input_point_count (int): Size of the curve the transform pipeline received. Example: 100000.
    output_point_count (int): Size after the full pipeline (resample, then differential, then outMode). Example:
        100.
    resampled (bool): True only if the resample stage actually changed the point count.
    differential (bool): True only if delta-encoding actually ran. Requesting it on a curve of 0 or 1 points has
        nothing to encode, so it does not run even if asked.
    out_mode (EquityCurveOutMode): JSON shape for an equity curve's points. `ARRAY` is `[{timestamp, equity}, ...]`;
        `SHORT` is `{timestamps: [...], equities: [...]}` (parallel arrays, no repeated key text). The one schema shared
        by every place `outMode` appears, request or response, so the two cannot drift to different value sets.
EquityCurveMeta( input_point_count: int, output_point_count: int, resampled: bool, differential: bool, out_mode: EquityCurveOutMode)
28def __init__(self, input_point_count, output_point_count, resampled, differential, out_mode):
29    self.input_point_count = input_point_count
30    self.output_point_count = output_point_count
31    self.resampled = resampled
32    self.differential = differential
33    self.out_mode = out_mode
34    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class EquityCurveMeta.

input_point_count: int
output_point_count: int
resampled: bool
differential: bool
out_mode: EquityCurveOutMode
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
40    def to_dict(self) -> dict[str, Any]:
41        input_point_count = self.input_point_count
42
43        output_point_count = self.output_point_count
44
45        resampled = self.resampled
46
47        differential = self.differential
48
49        out_mode = self.out_mode.value
50
51        field_dict: dict[str, Any] = {}
52        field_dict.update(self.additional_properties)
53        field_dict.update(
54            {
55                "inputPointCount": input_point_count,
56                "outputPointCount": output_point_count,
57                "resampled": resampled,
58                "differential": differential,
59                "outMode": out_mode,
60            }
61        )
62
63        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
65    @classmethod
66    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
67        d = dict(src_dict)
68        input_point_count = d.pop("inputPointCount")
69
70        output_point_count = d.pop("outputPointCount")
71
72        resampled = d.pop("resampled")
73
74        differential = d.pop("differential")
75
76        out_mode = EquityCurveOutMode(d.pop("outMode"))
77
78        equity_curve_meta = cls(
79            input_point_count=input_point_count,
80            output_point_count=output_point_count,
81            resampled=resampled,
82            differential=differential,
83            out_mode=out_mode,
84        )
85
86        equity_curve_meta.additional_properties = d
87        return equity_curve_meta
additional_keys: list[str]
89    @property
90    def additional_keys(self) -> list[str]:
91        return list(self.additional_properties.keys())
class EquityCurveOptions:
16@_attrs_define
17class EquityCurveOptions:
18    """Requested equity-curve transform, applied server-side in a fixed pipeline order: `resample` (point count) then
19    `differential` (encoding) then `outMode` (JSON shape) — each stage assumes the previous one already ran. A server-
20    side size guard can still force a smaller/deflated shape above its thresholds regardless of what is requested here —
21    see `EquityCurveMeta` for what actually happened.
22
23        Attributes:
24            resample (int | Unset): Downsample to at most this many points (extrema-preserving — the global max/min and the
25                exact first/last point are always kept). Omit for no downsampling.
26            differential (bool | Unset): Delta-encode both fields from the second (post-resample) point onward. Default:
27                False.
28            out_mode (EquityCurveOutMode | Unset): JSON shape for an equity curve's points. `ARRAY` is `[{timestamp,
29                equity}, ...]`; `SHORT` is `{timestamps: [...], equities: [...]}` (parallel arrays, no repeated key text). The
30                one schema shared by every place `outMode` appears, request or response, so the two cannot drift to different
31                value sets. Default: EquityCurveOutMode.ARRAY.
32    """
33
34    resample: int | Unset = UNSET
35    differential: bool | Unset = False
36    out_mode: EquityCurveOutMode | Unset = EquityCurveOutMode.ARRAY
37    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
38
39    def to_dict(self) -> dict[str, Any]:
40        resample = self.resample
41
42        differential = self.differential
43
44        out_mode: str | Unset = UNSET
45        if not isinstance(self.out_mode, Unset):
46            out_mode = self.out_mode.value
47
48        field_dict: dict[str, Any] = {}
49        field_dict.update(self.additional_properties)
50        field_dict.update({})
51        if resample is not UNSET:
52            field_dict["resample"] = resample
53        if differential is not UNSET:
54            field_dict["differential"] = differential
55        if out_mode is not UNSET:
56            field_dict["outMode"] = out_mode
57
58        return field_dict
59
60    @classmethod
61    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
62        d = dict(src_dict)
63        resample = d.pop("resample", UNSET)
64
65        differential = d.pop("differential", UNSET)
66
67        _out_mode = d.pop("outMode", UNSET)
68        out_mode: EquityCurveOutMode | Unset
69        if isinstance(_out_mode, Unset):
70            out_mode = UNSET
71        else:
72            out_mode = EquityCurveOutMode(_out_mode)
73
74        equity_curve_options = cls(
75            resample=resample,
76            differential=differential,
77            out_mode=out_mode,
78        )
79
80        equity_curve_options.additional_properties = d
81        return equity_curve_options
82
83    @property
84    def additional_keys(self) -> list[str]:
85        return list(self.additional_properties.keys())
86
87    def __getitem__(self, key: str) -> Any:
88        return self.additional_properties[key]
89
90    def __setitem__(self, key: str, value: Any) -> None:
91        self.additional_properties[key] = value
92
93    def __delitem__(self, key: str) -> None:
94        del self.additional_properties[key]
95
96    def __contains__(self, key: str) -> bool:
97        return key in self.additional_properties

Requested equity-curve transform, applied server-side in a fixed pipeline order: resample (point count) then differential (encoding) then outMode (JSON shape) — each stage assumes the previous one already ran. A server- side size guard can still force a smaller/deflated shape above its thresholds regardless of what is requested here — see EquityCurveMeta for what actually happened.

Attributes:
    resample (int | Unset): Downsample to at most this many points (extrema-preserving — the global max/min and the
        exact first/last point are always kept). Omit for no downsampling.
    differential (bool | Unset): Delta-encode both fields from the second (post-resample) point onward. Default:
        False.
    out_mode (EquityCurveOutMode | Unset): JSON shape for an equity curve's points. `ARRAY` is `[{timestamp,
        equity}, ...]`; `SHORT` is `{timestamps: [...], equities: [...]}` (parallel arrays, no repeated key text). The
        one schema shared by every place `outMode` appears, request or response, so the two cannot drift to different
        value sets. Default: EquityCurveOutMode.ARRAY.
EquityCurveOptions( resample: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, differential: bool | qtsurfer.api.client._generated.types.Unset = False, out_mode: EquityCurveOutMode | qtsurfer.api.client._generated.types.Unset = <EquityCurveOutMode.ARRAY: 'ARRAY'>)
26def __init__(self, resample=attr_dict['resample'].default, differential=attr_dict['differential'].default, out_mode=attr_dict['out_mode'].default):
27    self.resample = resample
28    self.differential = differential
29    self.out_mode = out_mode
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class EquityCurveOptions.

resample: int | qtsurfer.api.client._generated.types.Unset
differential: bool | qtsurfer.api.client._generated.types.Unset
out_mode: EquityCurveOutMode | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
39    def to_dict(self) -> dict[str, Any]:
40        resample = self.resample
41
42        differential = self.differential
43
44        out_mode: str | Unset = UNSET
45        if not isinstance(self.out_mode, Unset):
46            out_mode = self.out_mode.value
47
48        field_dict: dict[str, Any] = {}
49        field_dict.update(self.additional_properties)
50        field_dict.update({})
51        if resample is not UNSET:
52            field_dict["resample"] = resample
53        if differential is not UNSET:
54            field_dict["differential"] = differential
55        if out_mode is not UNSET:
56            field_dict["outMode"] = out_mode
57
58        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
60    @classmethod
61    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
62        d = dict(src_dict)
63        resample = d.pop("resample", UNSET)
64
65        differential = d.pop("differential", UNSET)
66
67        _out_mode = d.pop("outMode", UNSET)
68        out_mode: EquityCurveOutMode | Unset
69        if isinstance(_out_mode, Unset):
70            out_mode = UNSET
71        else:
72            out_mode = EquityCurveOutMode(_out_mode)
73
74        equity_curve_options = cls(
75            resample=resample,
76            differential=differential,
77            out_mode=out_mode,
78        )
79
80        equity_curve_options.additional_properties = d
81        return equity_curve_options
additional_keys: list[str]
83    @property
84    def additional_keys(self) -> list[str]:
85        return list(self.additional_properties.keys())
class EquityCurveOutMode(builtins.str, enum.Enum):
 5class EquityCurveOutMode(str, Enum):
 6    ARRAY = "ARRAY"
 7    SHORT = "SHORT"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

ARRAY = <EquityCurveOutMode.ARRAY: 'ARRAY'>
SHORT = <EquityCurveOutMode.SHORT: 'SHORT'>
class EquityCurveRequest:
 17@_attrs_define
 18class EquityCurveRequest:
 19    """Selection (`mode`/`n`/`maxPct`) plus the transform preference (`resample`/`differential`/`outMode`) applied by `GET
 20    .../equityCurve` whenever ITS OWN query params are absent, for a curve this sweep retained. The transform half never
 21    affects retention or `sweepId` — a caller can always override it per-request at read time regardless of what was
 22    submitted here.
 23
 24        Attributes:
 25            resample (int | Unset): Downsample to at most this many points (extrema-preserving — the global max/min and the
 26                exact first/last point are always kept). Omit for no downsampling.
 27            differential (bool | Unset): Delta-encode both fields from the second (post-resample) point onward. Default:
 28                False.
 29            out_mode (EquityCurveOutMode | Unset): JSON shape for an equity curve's points. `ARRAY` is `[{timestamp,
 30                equity}, ...]`; `SHORT` is `{timestamps: [...], equities: [...]}` (parallel arrays, no repeated key text). The
 31                one schema shared by every place `outMode` appears, request or response, so the two cannot drift to different
 32                value sets. Default: EquityCurveOutMode.ARRAY.
 33            mode (EquityCurveRequestMode | Unset): Which trials keep their per-point equity curve. `auto` retains curves
 34                only while the accumulated size stays within server limits; `topN`/`topPct` retain curves for the best-ranked
 35                trials explicitly; `none` retains no curves. Default: EquityCurveRequestMode.AUTO.
 36            n (int | Unset): Trial count to retain when mode is topN.
 37            max_pct (float | Unset): Top percentage of trials to retain when mode is topPct.
 38    """
 39
 40    resample: int | Unset = UNSET
 41    differential: bool | Unset = False
 42    out_mode: EquityCurveOutMode | Unset = EquityCurveOutMode.ARRAY
 43    mode: EquityCurveRequestMode | Unset = EquityCurveRequestMode.AUTO
 44    n: int | Unset = UNSET
 45    max_pct: float | Unset = UNSET
 46    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 47
 48    def to_dict(self) -> dict[str, Any]:
 49        resample = self.resample
 50
 51        differential = self.differential
 52
 53        out_mode: str | Unset = UNSET
 54        if not isinstance(self.out_mode, Unset):
 55            out_mode = self.out_mode.value
 56
 57        mode: str | Unset = UNSET
 58        if not isinstance(self.mode, Unset):
 59            mode = self.mode.value
 60
 61        n = self.n
 62
 63        max_pct = self.max_pct
 64
 65        field_dict: dict[str, Any] = {}
 66        field_dict.update(self.additional_properties)
 67        field_dict.update({})
 68        if resample is not UNSET:
 69            field_dict["resample"] = resample
 70        if differential is not UNSET:
 71            field_dict["differential"] = differential
 72        if out_mode is not UNSET:
 73            field_dict["outMode"] = out_mode
 74        if mode is not UNSET:
 75            field_dict["mode"] = mode
 76        if n is not UNSET:
 77            field_dict["n"] = n
 78        if max_pct is not UNSET:
 79            field_dict["maxPct"] = max_pct
 80
 81        return field_dict
 82
 83    @classmethod
 84    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 85        d = dict(src_dict)
 86        resample = d.pop("resample", UNSET)
 87
 88        differential = d.pop("differential", UNSET)
 89
 90        _out_mode = d.pop("outMode", UNSET)
 91        out_mode: EquityCurveOutMode | Unset
 92        if isinstance(_out_mode, Unset):
 93            out_mode = UNSET
 94        else:
 95            out_mode = EquityCurveOutMode(_out_mode)
 96
 97        _mode = d.pop("mode", UNSET)
 98        mode: EquityCurveRequestMode | Unset
 99        if isinstance(_mode, Unset):
100            mode = UNSET
101        else:
102            mode = EquityCurveRequestMode(_mode)
103
104        n = d.pop("n", UNSET)
105
106        max_pct = d.pop("maxPct", UNSET)
107
108        equity_curve_request = cls(
109            resample=resample,
110            differential=differential,
111            out_mode=out_mode,
112            mode=mode,
113            n=n,
114            max_pct=max_pct,
115        )
116
117        equity_curve_request.additional_properties = d
118        return equity_curve_request
119
120    @property
121    def additional_keys(self) -> list[str]:
122        return list(self.additional_properties.keys())
123
124    def __getitem__(self, key: str) -> Any:
125        return self.additional_properties[key]
126
127    def __setitem__(self, key: str, value: Any) -> None:
128        self.additional_properties[key] = value
129
130    def __delitem__(self, key: str) -> None:
131        del self.additional_properties[key]
132
133    def __contains__(self, key: str) -> bool:
134        return key in self.additional_properties

Selection (mode/n/maxPct) plus the transform preference (resample/differential/outMode) applied by GET .../equityCurve whenever ITS OWN query params are absent, for a curve this sweep retained. The transform half never affects retention or sweepId — a caller can always override it per-request at read time regardless of what was submitted here.

Attributes:
    resample (int | Unset): Downsample to at most this many points (extrema-preserving — the global max/min and the
        exact first/last point are always kept). Omit for no downsampling.
    differential (bool | Unset): Delta-encode both fields from the second (post-resample) point onward. Default:
        False.
    out_mode (EquityCurveOutMode | Unset): JSON shape for an equity curve's points. `ARRAY` is `[{timestamp,
        equity}, ...]`; `SHORT` is `{timestamps: [...], equities: [...]}` (parallel arrays, no repeated key text). The
        one schema shared by every place `outMode` appears, request or response, so the two cannot drift to different
        value sets. Default: EquityCurveOutMode.ARRAY.
    mode (EquityCurveRequestMode | Unset): Which trials keep their per-point equity curve. `auto` retains curves
        only while the accumulated size stays within server limits; `topN`/`topPct` retain curves for the best-ranked
        trials explicitly; `none` retains no curves. Default: EquityCurveRequestMode.AUTO.
    n (int | Unset): Trial count to retain when mode is topN.
    max_pct (float | Unset): Top percentage of trials to retain when mode is topPct.
EquityCurveRequest( resample: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, differential: bool | qtsurfer.api.client._generated.types.Unset = False, out_mode: EquityCurveOutMode | qtsurfer.api.client._generated.types.Unset = <EquityCurveOutMode.ARRAY: 'ARRAY'>, mode: EquityCurveRequestMode | qtsurfer.api.client._generated.types.Unset = <EquityCurveRequestMode.AUTO: 'auto'>, n: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, max_pct: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
29def __init__(self, resample=attr_dict['resample'].default, differential=attr_dict['differential'].default, out_mode=attr_dict['out_mode'].default, mode=attr_dict['mode'].default, n=attr_dict['n'].default, max_pct=attr_dict['max_pct'].default):
30    self.resample = resample
31    self.differential = differential
32    self.out_mode = out_mode
33    self.mode = mode
34    self.n = n
35    self.max_pct = max_pct
36    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class EquityCurveRequest.

resample: int | qtsurfer.api.client._generated.types.Unset
differential: bool | qtsurfer.api.client._generated.types.Unset
out_mode: EquityCurveOutMode | qtsurfer.api.client._generated.types.Unset
mode: EquityCurveRequestMode | qtsurfer.api.client._generated.types.Unset
n: int | qtsurfer.api.client._generated.types.Unset
max_pct: float | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
48    def to_dict(self) -> dict[str, Any]:
49        resample = self.resample
50
51        differential = self.differential
52
53        out_mode: str | Unset = UNSET
54        if not isinstance(self.out_mode, Unset):
55            out_mode = self.out_mode.value
56
57        mode: str | Unset = UNSET
58        if not isinstance(self.mode, Unset):
59            mode = self.mode.value
60
61        n = self.n
62
63        max_pct = self.max_pct
64
65        field_dict: dict[str, Any] = {}
66        field_dict.update(self.additional_properties)
67        field_dict.update({})
68        if resample is not UNSET:
69            field_dict["resample"] = resample
70        if differential is not UNSET:
71            field_dict["differential"] = differential
72        if out_mode is not UNSET:
73            field_dict["outMode"] = out_mode
74        if mode is not UNSET:
75            field_dict["mode"] = mode
76        if n is not UNSET:
77            field_dict["n"] = n
78        if max_pct is not UNSET:
79            field_dict["maxPct"] = max_pct
80
81        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 83    @classmethod
 84    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 85        d = dict(src_dict)
 86        resample = d.pop("resample", UNSET)
 87
 88        differential = d.pop("differential", UNSET)
 89
 90        _out_mode = d.pop("outMode", UNSET)
 91        out_mode: EquityCurveOutMode | Unset
 92        if isinstance(_out_mode, Unset):
 93            out_mode = UNSET
 94        else:
 95            out_mode = EquityCurveOutMode(_out_mode)
 96
 97        _mode = d.pop("mode", UNSET)
 98        mode: EquityCurveRequestMode | Unset
 99        if isinstance(_mode, Unset):
100            mode = UNSET
101        else:
102            mode = EquityCurveRequestMode(_mode)
103
104        n = d.pop("n", UNSET)
105
106        max_pct = d.pop("maxPct", UNSET)
107
108        equity_curve_request = cls(
109            resample=resample,
110            differential=differential,
111            out_mode=out_mode,
112            mode=mode,
113            n=n,
114            max_pct=max_pct,
115        )
116
117        equity_curve_request.additional_properties = d
118        return equity_curve_request
additional_keys: list[str]
120    @property
121    def additional_keys(self) -> list[str]:
122        return list(self.additional_properties.keys())
class EquityCurveRequestMode(builtins.str, enum.Enum):
 5class EquityCurveRequestMode(str, Enum):
 6    AUTO = "auto"
 7    NONE = "none"
 8    TOPN = "topN"
 9    TOPPCT = "topPct"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

AUTO = <EquityCurveRequestMode.AUTO: 'auto'>
NONE = <EquityCurveRequestMode.NONE: 'none'>
TOPN = <EquityCurveRequestMode.TOPN: 'topN'>
TOPPCT = <EquityCurveRequestMode.TOPPCT: 'topPct'>
class EquityCurveResult:
 20@_attrs_define
 21class EquityCurveResult:
 22    """An equity curve, shaped per `meta.outMode`: `points` when `ARRAY`, `timestamps` + `equities` (parallel arrays) when
 23    `SHORT`. Used identically wherever a curve is returned — a plain backtest's inline `equityCurve` and a sweep row's
 24    `equityCurve` are the same type. `url` is present *instead of* any points when the curve is served by pointer rather
 25    than inline (a sweep row's top-N winners only): `GET` it separately to fetch this exact same shape with the points
 26    populated.
 27
 28        Attributes:
 29            meta (EquityCurveMeta): What the transform pipeline actually did, computed from the observed outcome — never a
 30                copy of what was requested. Lets a caller detect a forced or no-op transform (e.g. a `resample` ceiling already
 31                above the curve's size is a legal no-op, reported honestly as `resampled: false`).
 32            points (list[EquityPoint] | Unset): Present when `meta.outMode` is `ARRAY` and the curve is inline (not a
 33                pointer).
 34            timestamps (list[int] | Unset): Present when `meta.outMode` is `SHORT` and the curve is inline (not a pointer).
 35            equities (list[float] | Unset): Present when `meta.outMode` is `SHORT` and the curve is inline (not a pointer),
 36                parallel to `timestamps` (same index, same point).
 37            url (str | Unset): Present only for a sweep row's pointer curve. `GET` this to fetch the curve itself, in this
 38                exact `{points|timestamps+equities, meta}` shape — `meta` there is the real, possibly size-guarded outcome; this
 39                outer `meta` is a raw, untransformed preview from the moment the sweep selected this trial's curve, and the two
 40                can legitimately differ. Example: /v1/backtest/binance/ticker/executeSweep/req-1/swp_test/runs/3/equityCurve.
 41    """
 42
 43    meta: EquityCurveMeta
 44    points: list[EquityPoint] | Unset = UNSET
 45    timestamps: list[int] | Unset = UNSET
 46    equities: list[float] | Unset = UNSET
 47    url: str | Unset = UNSET
 48    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 49
 50    def to_dict(self) -> dict[str, Any]:
 51        meta = self.meta.to_dict()
 52
 53        points: list[dict[str, Any]] | Unset = UNSET
 54        if not isinstance(self.points, Unset):
 55            points = []
 56            for points_item_data in self.points:
 57                points_item = points_item_data.to_dict()
 58                points.append(points_item)
 59
 60        timestamps: list[int] | Unset = UNSET
 61        if not isinstance(self.timestamps, Unset):
 62            timestamps = self.timestamps
 63
 64        equities: list[float] | Unset = UNSET
 65        if not isinstance(self.equities, Unset):
 66            equities = self.equities
 67
 68        url = self.url
 69
 70        field_dict: dict[str, Any] = {}
 71        field_dict.update(self.additional_properties)
 72        field_dict.update(
 73            {
 74                "meta": meta,
 75            }
 76        )
 77        if points is not UNSET:
 78            field_dict["points"] = points
 79        if timestamps is not UNSET:
 80            field_dict["timestamps"] = timestamps
 81        if equities is not UNSET:
 82            field_dict["equities"] = equities
 83        if url is not UNSET:
 84            field_dict["url"] = url
 85
 86        return field_dict
 87
 88    @classmethod
 89    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 90        from ..models.equity_curve_meta import EquityCurveMeta
 91        from ..models.equity_point import EquityPoint
 92
 93        d = dict(src_dict)
 94        meta = EquityCurveMeta.from_dict(d.pop("meta"))
 95
 96        _points = d.pop("points", UNSET)
 97        points: list[EquityPoint] | Unset = UNSET
 98        if _points is not UNSET:
 99            points = []
100            for points_item_data in _points:
101                points_item = EquityPoint.from_dict(points_item_data)
102
103                points.append(points_item)
104
105        timestamps = cast(list[int], d.pop("timestamps", UNSET))
106
107        equities = cast(list[float], d.pop("equities", UNSET))
108
109        url = d.pop("url", UNSET)
110
111        equity_curve_result = cls(
112            meta=meta,
113            points=points,
114            timestamps=timestamps,
115            equities=equities,
116            url=url,
117        )
118
119        equity_curve_result.additional_properties = d
120        return equity_curve_result
121
122    @property
123    def additional_keys(self) -> list[str]:
124        return list(self.additional_properties.keys())
125
126    def __getitem__(self, key: str) -> Any:
127        return self.additional_properties[key]
128
129    def __setitem__(self, key: str, value: Any) -> None:
130        self.additional_properties[key] = value
131
132    def __delitem__(self, key: str) -> None:
133        del self.additional_properties[key]
134
135    def __contains__(self, key: str) -> bool:
136        return key in self.additional_properties

An equity curve, shaped per meta.outMode: points when ARRAY, timestamps + equities (parallel arrays) when SHORT. Used identically wherever a curve is returned — a plain backtest's inline equityCurve and a sweep row's equityCurve are the same type. url is present instead of any points when the curve is served by pointer rather than inline (a sweep row's top-N winners only): GET it separately to fetch this exact same shape with the points populated.

Attributes:
    meta (EquityCurveMeta): What the transform pipeline actually did, computed from the observed outcome — never a
        copy of what was requested. Lets a caller detect a forced or no-op transform (e.g. a `resample` ceiling already
        above the curve's size is a legal no-op, reported honestly as `resampled: false`).
    points (list[EquityPoint] | Unset): Present when `meta.outMode` is `ARRAY` and the curve is inline (not a
        pointer).
    timestamps (list[int] | Unset): Present when `meta.outMode` is `SHORT` and the curve is inline (not a pointer).
    equities (list[float] | Unset): Present when `meta.outMode` is `SHORT` and the curve is inline (not a pointer),
        parallel to `timestamps` (same index, same point).
    url (str | Unset): Present only for a sweep row's pointer curve. `GET` this to fetch the curve itself, in this
        exact `{points|timestamps+equities, meta}` shape — `meta` there is the real, possibly size-guarded outcome; this
        outer `meta` is a raw, untransformed preview from the moment the sweep selected this trial's curve, and the two
        can legitimately differ. Example: /v1/backtest/binance/ticker/executeSweep/req-1/swp_test/runs/3/equityCurve.
EquityCurveResult( meta: EquityCurveMeta, points: list[EquityPoint] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, timestamps: list[int] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, equities: list[float] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, url: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
28def __init__(self, meta, points=attr_dict['points'].default, timestamps=attr_dict['timestamps'].default, equities=attr_dict['equities'].default, url=attr_dict['url'].default):
29    self.meta = meta
30    self.points = points
31    self.timestamps = timestamps
32    self.equities = equities
33    self.url = url
34    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class EquityCurveResult.

points: list[EquityPoint] | qtsurfer.api.client._generated.types.Unset
timestamps: list[int] | qtsurfer.api.client._generated.types.Unset
equities: list[float] | qtsurfer.api.client._generated.types.Unset
url: str | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
50    def to_dict(self) -> dict[str, Any]:
51        meta = self.meta.to_dict()
52
53        points: list[dict[str, Any]] | Unset = UNSET
54        if not isinstance(self.points, Unset):
55            points = []
56            for points_item_data in self.points:
57                points_item = points_item_data.to_dict()
58                points.append(points_item)
59
60        timestamps: list[int] | Unset = UNSET
61        if not isinstance(self.timestamps, Unset):
62            timestamps = self.timestamps
63
64        equities: list[float] | Unset = UNSET
65        if not isinstance(self.equities, Unset):
66            equities = self.equities
67
68        url = self.url
69
70        field_dict: dict[str, Any] = {}
71        field_dict.update(self.additional_properties)
72        field_dict.update(
73            {
74                "meta": meta,
75            }
76        )
77        if points is not UNSET:
78            field_dict["points"] = points
79        if timestamps is not UNSET:
80            field_dict["timestamps"] = timestamps
81        if equities is not UNSET:
82            field_dict["equities"] = equities
83        if url is not UNSET:
84            field_dict["url"] = url
85
86        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 88    @classmethod
 89    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 90        from ..models.equity_curve_meta import EquityCurveMeta
 91        from ..models.equity_point import EquityPoint
 92
 93        d = dict(src_dict)
 94        meta = EquityCurveMeta.from_dict(d.pop("meta"))
 95
 96        _points = d.pop("points", UNSET)
 97        points: list[EquityPoint] | Unset = UNSET
 98        if _points is not UNSET:
 99            points = []
100            for points_item_data in _points:
101                points_item = EquityPoint.from_dict(points_item_data)
102
103                points.append(points_item)
104
105        timestamps = cast(list[int], d.pop("timestamps", UNSET))
106
107        equities = cast(list[float], d.pop("equities", UNSET))
108
109        url = d.pop("url", UNSET)
110
111        equity_curve_result = cls(
112            meta=meta,
113            points=points,
114            timestamps=timestamps,
115            equities=equities,
116            url=url,
117        )
118
119        equity_curve_result.additional_properties = d
120        return equity_curve_result
additional_keys: list[str]
122    @property
123    def additional_keys(self) -> list[str]:
124        return list(self.additional_properties.keys())
class EquityPoint:
13@_attrs_define
14class EquityPoint:
15    """Single sample of the running equity at a yield event.
16
17    Attributes:
18        timestamp (int): Epoch milliseconds. The first point in an equity curve is anchored at the backtest `from`;
19            subsequent points carry the timestamp of each emitted yield. Example: 1700000000000.
20        equity (float): Running equity at this point (`initialCapital + cumulativePnl`). Example: 110.5.
21    """
22
23    timestamp: int
24    equity: float
25    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
26
27    def to_dict(self) -> dict[str, Any]:
28        timestamp = self.timestamp
29
30        equity = self.equity
31
32        field_dict: dict[str, Any] = {}
33        field_dict.update(self.additional_properties)
34        field_dict.update(
35            {
36                "timestamp": timestamp,
37                "equity": equity,
38            }
39        )
40
41        return field_dict
42
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        d = dict(src_dict)
46        timestamp = d.pop("timestamp")
47
48        equity = d.pop("equity")
49
50        equity_point = cls(
51            timestamp=timestamp,
52            equity=equity,
53        )
54
55        equity_point.additional_properties = d
56        return equity_point
57
58    @property
59    def additional_keys(self) -> list[str]:
60        return list(self.additional_properties.keys())
61
62    def __getitem__(self, key: str) -> Any:
63        return self.additional_properties[key]
64
65    def __setitem__(self, key: str, value: Any) -> None:
66        self.additional_properties[key] = value
67
68    def __delitem__(self, key: str) -> None:
69        del self.additional_properties[key]
70
71    def __contains__(self, key: str) -> bool:
72        return key in self.additional_properties

Single sample of the running equity at a yield event.

Attributes: timestamp (int): Epoch milliseconds. The first point in an equity curve is anchored at the backtest from; subsequent points carry the timestamp of each emitted yield. Example: 1700000000000. equity (float): Running equity at this point (initialCapital + cumulativePnl). Example: 110.5.

EquityPoint(timestamp: int, equity: float)
25def __init__(self, timestamp, equity):
26    self.timestamp = timestamp
27    self.equity = equity
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class EquityPoint.

timestamp: int
equity: float
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
27    def to_dict(self) -> dict[str, Any]:
28        timestamp = self.timestamp
29
30        equity = self.equity
31
32        field_dict: dict[str, Any] = {}
33        field_dict.update(self.additional_properties)
34        field_dict.update(
35            {
36                "timestamp": timestamp,
37                "equity": equity,
38            }
39        )
40
41        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        d = dict(src_dict)
46        timestamp = d.pop("timestamp")
47
48        equity = d.pop("equity")
49
50        equity_point = cls(
51            timestamp=timestamp,
52            equity=equity,
53        )
54
55        equity_point.additional_properties = d
56        return equity_point
additional_keys: list[str]
58    @property
59    def additional_keys(self) -> list[str]:
60        return list(self.additional_properties.keys())
class Exchange:
15@_attrs_define
16class Exchange:
17    """Exchange service provider
18
19    Attributes:
20        id (str): Unique identifier for the exchange Example: binance.
21        name (str): Name of the exchange Example: Binance.
22        description (str | Unset): Description of the exchange Example: Binance cryptocurrency exchange.
23    """
24
25    id: str
26    name: str
27    description: str | Unset = UNSET
28    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
29
30    def to_dict(self) -> dict[str, Any]:
31        id = self.id
32
33        name = self.name
34
35        description = self.description
36
37        field_dict: dict[str, Any] = {}
38        field_dict.update(self.additional_properties)
39        field_dict.update(
40            {
41                "id": id,
42                "name": name,
43            }
44        )
45        if description is not UNSET:
46            field_dict["description"] = description
47
48        return field_dict
49
50    @classmethod
51    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
52        d = dict(src_dict)
53        id = d.pop("id")
54
55        name = d.pop("name")
56
57        description = d.pop("description", UNSET)
58
59        exchange = cls(
60            id=id,
61            name=name,
62            description=description,
63        )
64
65        exchange.additional_properties = d
66        return exchange
67
68    @property
69    def additional_keys(self) -> list[str]:
70        return list(self.additional_properties.keys())
71
72    def __getitem__(self, key: str) -> Any:
73        return self.additional_properties[key]
74
75    def __setitem__(self, key: str, value: Any) -> None:
76        self.additional_properties[key] = value
77
78    def __delitem__(self, key: str) -> None:
79        del self.additional_properties[key]
80
81    def __contains__(self, key: str) -> bool:
82        return key in self.additional_properties

Exchange service provider

Attributes: id (str): Unique identifier for the exchange Example: binance. name (str): Name of the exchange Example: Binance. description (str | Unset): Description of the exchange Example: Binance cryptocurrency exchange.

Exchange( id: str, name: str, description: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
26def __init__(self, id, name, description=attr_dict['description'].default):
27    self.id = id
28    self.name = name
29    self.description = description
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class Exchange.

id: str
name: str
description: str | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
30    def to_dict(self) -> dict[str, Any]:
31        id = self.id
32
33        name = self.name
34
35        description = self.description
36
37        field_dict: dict[str, Any] = {}
38        field_dict.update(self.additional_properties)
39        field_dict.update(
40            {
41                "id": id,
42                "name": name,
43            }
44        )
45        if description is not UNSET:
46            field_dict["description"] = description
47
48        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
50    @classmethod
51    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
52        d = dict(src_dict)
53        id = d.pop("id")
54
55        name = d.pop("name")
56
57        description = d.pop("description", UNSET)
58
59        exchange = cls(
60            id=id,
61            name=name,
62            description=description,
63        )
64
65        exchange.additional_properties = d
66        return exchange
additional_keys: list[str]
68    @property
69    def additional_keys(self) -> list[str]:
70        return list(self.additional_properties.keys())
class ExecuteBacktestBody:
 19@_attrs_define
 20class ExecuteBacktestBody:
 21    """
 22    Attributes:
 23        prepare_job_id (str): Job ID returned by `POST /prepare` (must be in `Completed` state) Example:
 24            13RBLGQlPnfDjO6wyKSX8i.
 25        strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
 26            always yields the same id, for every caller, whatever its formatting. See
 27            `POST /strategy` for exactly which rewrites preserve it and which do not.
 28             Example: 6bsh31ikwkuivhtgcoa6s4.
 29        store_signals (bool | Unset): When true, the worker uploads emitted signals to object storage and the
 30            response includes `signalsUrl` / `signalsId` fields. Defaults to false.
 31             Default: False.
 32        equity_curve (EquityCurveOptions | Unset): Requested equity-curve transform, applied server-side in a fixed
 33            pipeline order: `resample` (point count) then `differential` (encoding) then `outMode` (JSON shape) — each stage
 34            assumes the previous one already ran. A server-side size guard can still force a smaller/deflated shape above
 35            its thresholds regardless of what is requested here — see `EquityCurveMeta` for what actually happened.
 36    """
 37
 38    prepare_job_id: str
 39    strategy_id: str
 40    store_signals: bool | Unset = False
 41    equity_curve: EquityCurveOptions | Unset = UNSET
 42    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 43
 44    def to_dict(self) -> dict[str, Any]:
 45        prepare_job_id = self.prepare_job_id
 46
 47        strategy_id = self.strategy_id
 48
 49        store_signals = self.store_signals
 50
 51        equity_curve: dict[str, Any] | Unset = UNSET
 52        if not isinstance(self.equity_curve, Unset):
 53            equity_curve = self.equity_curve.to_dict()
 54
 55        field_dict: dict[str, Any] = {}
 56        field_dict.update(self.additional_properties)
 57        field_dict.update(
 58            {
 59                "prepareJobId": prepare_job_id,
 60                "strategyId": strategy_id,
 61            }
 62        )
 63        if store_signals is not UNSET:
 64            field_dict["storeSignals"] = store_signals
 65        if equity_curve is not UNSET:
 66            field_dict["equityCurve"] = equity_curve
 67
 68        return field_dict
 69
 70    @classmethod
 71    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 72        from ..models.equity_curve_options import EquityCurveOptions
 73
 74        d = dict(src_dict)
 75        prepare_job_id = d.pop("prepareJobId")
 76
 77        strategy_id = d.pop("strategyId")
 78
 79        store_signals = d.pop("storeSignals", UNSET)
 80
 81        _equity_curve = d.pop("equityCurve", UNSET)
 82        equity_curve: EquityCurveOptions | Unset
 83        if isinstance(_equity_curve, Unset):
 84            equity_curve = UNSET
 85        else:
 86            equity_curve = EquityCurveOptions.from_dict(_equity_curve)
 87
 88        execute_backtest_body = cls(
 89            prepare_job_id=prepare_job_id,
 90            strategy_id=strategy_id,
 91            store_signals=store_signals,
 92            equity_curve=equity_curve,
 93        )
 94
 95        execute_backtest_body.additional_properties = d
 96        return execute_backtest_body
 97
 98    @property
 99    def additional_keys(self) -> list[str]:
100        return list(self.additional_properties.keys())
101
102    def __getitem__(self, key: str) -> Any:
103        return self.additional_properties[key]
104
105    def __setitem__(self, key: str, value: Any) -> None:
106        self.additional_properties[key] = value
107
108    def __delitem__(self, key: str) -> None:
109        del self.additional_properties[key]
110
111    def __contains__(self, key: str) -> bool:
112        return key in self.additional_properties

Attributes: prepare_job_id (str): Job ID returned by POST /prepare (must be in Completed state) Example: 13RBLGQlPnfDjO6wyKSX8i. strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code always yields the same id, for every caller, whatever its formatting. See POST /strategy for exactly which rewrites preserve it and which do not. Example: 6bsh31ikwkuivhtgcoa6s4. store_signals (bool | Unset): When true, the worker uploads emitted signals to object storage and the response includes signalsUrl / signalsId fields. Defaults to false. Default: False. equity_curve (EquityCurveOptions | Unset): Requested equity-curve transform, applied server-side in a fixed pipeline order: resample (point count) then differential (encoding) then outMode (JSON shape) — each stage assumes the previous one already ran. A server-side size guard can still force a smaller/deflated shape above its thresholds regardless of what is requested here — see EquityCurveMeta for what actually happened.

ExecuteBacktestBody( prepare_job_id: str, strategy_id: str, store_signals: bool | qtsurfer.api.client._generated.types.Unset = False, equity_curve: EquityCurveOptions | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
27def __init__(self, prepare_job_id, strategy_id, store_signals=attr_dict['store_signals'].default, equity_curve=attr_dict['equity_curve'].default):
28    self.prepare_job_id = prepare_job_id
29    self.strategy_id = strategy_id
30    self.store_signals = store_signals
31    self.equity_curve = equity_curve
32    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ExecuteBacktestBody.

prepare_job_id: str
strategy_id: str
store_signals: bool | qtsurfer.api.client._generated.types.Unset
equity_curve: EquityCurveOptions | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
44    def to_dict(self) -> dict[str, Any]:
45        prepare_job_id = self.prepare_job_id
46
47        strategy_id = self.strategy_id
48
49        store_signals = self.store_signals
50
51        equity_curve: dict[str, Any] | Unset = UNSET
52        if not isinstance(self.equity_curve, Unset):
53            equity_curve = self.equity_curve.to_dict()
54
55        field_dict: dict[str, Any] = {}
56        field_dict.update(self.additional_properties)
57        field_dict.update(
58            {
59                "prepareJobId": prepare_job_id,
60                "strategyId": strategy_id,
61            }
62        )
63        if store_signals is not UNSET:
64            field_dict["storeSignals"] = store_signals
65        if equity_curve is not UNSET:
66            field_dict["equityCurve"] = equity_curve
67
68        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
70    @classmethod
71    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
72        from ..models.equity_curve_options import EquityCurveOptions
73
74        d = dict(src_dict)
75        prepare_job_id = d.pop("prepareJobId")
76
77        strategy_id = d.pop("strategyId")
78
79        store_signals = d.pop("storeSignals", UNSET)
80
81        _equity_curve = d.pop("equityCurve", UNSET)
82        equity_curve: EquityCurveOptions | Unset
83        if isinstance(_equity_curve, Unset):
84            equity_curve = UNSET
85        else:
86            equity_curve = EquityCurveOptions.from_dict(_equity_curve)
87
88        execute_backtest_body = cls(
89            prepare_job_id=prepare_job_id,
90            strategy_id=strategy_id,
91            store_signals=store_signals,
92            equity_curve=equity_curve,
93        )
94
95        execute_backtest_body.additional_properties = d
96        return execute_backtest_body
additional_keys: list[str]
 98    @property
 99    def additional_keys(self) -> list[str]:
100        return list(self.additional_properties.keys())
class ExecuteSweepAccepted:
 19@_attrs_define
 20class ExecuteSweepAccepted:
 21    """
 22    Attributes:
 23        sweep_id (str):  Example: swp_95e47a7f0966ce11.
 24        request_id (str):
 25        total_runs (int):
 26        shards (int):
 27        seed (int): Effective seed used to expand the sweep.
 28        queued (bool): False when an identical sweep already exists and was not enqueued again.
 29        walk_forward (WalkForwardAccepted | Unset): Echo of the accepted walk-forward configuration, present only when
 30            the submit carried one. `inSamplePct` is the resolved value, so a request that omitted it can see what it got.
 31    """
 32
 33    sweep_id: str
 34    request_id: str
 35    total_runs: int
 36    shards: int
 37    seed: int
 38    queued: bool
 39    walk_forward: WalkForwardAccepted | Unset = UNSET
 40    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 41
 42    def to_dict(self) -> dict[str, Any]:
 43        sweep_id = self.sweep_id
 44
 45        request_id = self.request_id
 46
 47        total_runs = self.total_runs
 48
 49        shards = self.shards
 50
 51        seed = self.seed
 52
 53        queued = self.queued
 54
 55        walk_forward: dict[str, Any] | Unset = UNSET
 56        if not isinstance(self.walk_forward, Unset):
 57            walk_forward = self.walk_forward.to_dict()
 58
 59        field_dict: dict[str, Any] = {}
 60        field_dict.update(self.additional_properties)
 61        field_dict.update(
 62            {
 63                "sweepId": sweep_id,
 64                "requestId": request_id,
 65                "totalRuns": total_runs,
 66                "shards": shards,
 67                "seed": seed,
 68                "queued": queued,
 69            }
 70        )
 71        if walk_forward is not UNSET:
 72            field_dict["walkForward"] = walk_forward
 73
 74        return field_dict
 75
 76    @classmethod
 77    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 78        from ..models.walk_forward_accepted import WalkForwardAccepted
 79
 80        d = dict(src_dict)
 81        sweep_id = d.pop("sweepId")
 82
 83        request_id = d.pop("requestId")
 84
 85        total_runs = d.pop("totalRuns")
 86
 87        shards = d.pop("shards")
 88
 89        seed = d.pop("seed")
 90
 91        queued = d.pop("queued")
 92
 93        _walk_forward = d.pop("walkForward", UNSET)
 94        walk_forward: WalkForwardAccepted | Unset
 95        if isinstance(_walk_forward, Unset):
 96            walk_forward = UNSET
 97        else:
 98            walk_forward = WalkForwardAccepted.from_dict(_walk_forward)
 99
100        execute_sweep_accepted = cls(
101            sweep_id=sweep_id,
102            request_id=request_id,
103            total_runs=total_runs,
104            shards=shards,
105            seed=seed,
106            queued=queued,
107            walk_forward=walk_forward,
108        )
109
110        execute_sweep_accepted.additional_properties = d
111        return execute_sweep_accepted
112
113    @property
114    def additional_keys(self) -> list[str]:
115        return list(self.additional_properties.keys())
116
117    def __getitem__(self, key: str) -> Any:
118        return self.additional_properties[key]
119
120    def __setitem__(self, key: str, value: Any) -> None:
121        self.additional_properties[key] = value
122
123    def __delitem__(self, key: str) -> None:
124        del self.additional_properties[key]
125
126    def __contains__(self, key: str) -> bool:
127        return key in self.additional_properties

Attributes: sweep_id (str): Example: swp_95e47a7f0966ce11. request_id (str): total_runs (int): shards (int): seed (int): Effective seed used to expand the sweep. queued (bool): False when an identical sweep already exists and was not enqueued again. walk_forward (WalkForwardAccepted | Unset): Echo of the accepted walk-forward configuration, present only when the submit carried one. inSamplePct is the resolved value, so a request that omitted it can see what it got.

ExecuteSweepAccepted( sweep_id: str, request_id: str, total_runs: int, shards: int, seed: int, queued: bool, walk_forward: WalkForwardAccepted | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
30def __init__(self, sweep_id, request_id, total_runs, shards, seed, queued, walk_forward=attr_dict['walk_forward'].default):
31    self.sweep_id = sweep_id
32    self.request_id = request_id
33    self.total_runs = total_runs
34    self.shards = shards
35    self.seed = seed
36    self.queued = queued
37    self.walk_forward = walk_forward
38    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ExecuteSweepAccepted.

sweep_id: str
request_id: str
total_runs: int
shards: int
seed: int
queued: bool
walk_forward: WalkForwardAccepted | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
42    def to_dict(self) -> dict[str, Any]:
43        sweep_id = self.sweep_id
44
45        request_id = self.request_id
46
47        total_runs = self.total_runs
48
49        shards = self.shards
50
51        seed = self.seed
52
53        queued = self.queued
54
55        walk_forward: dict[str, Any] | Unset = UNSET
56        if not isinstance(self.walk_forward, Unset):
57            walk_forward = self.walk_forward.to_dict()
58
59        field_dict: dict[str, Any] = {}
60        field_dict.update(self.additional_properties)
61        field_dict.update(
62            {
63                "sweepId": sweep_id,
64                "requestId": request_id,
65                "totalRuns": total_runs,
66                "shards": shards,
67                "seed": seed,
68                "queued": queued,
69            }
70        )
71        if walk_forward is not UNSET:
72            field_dict["walkForward"] = walk_forward
73
74        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 76    @classmethod
 77    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 78        from ..models.walk_forward_accepted import WalkForwardAccepted
 79
 80        d = dict(src_dict)
 81        sweep_id = d.pop("sweepId")
 82
 83        request_id = d.pop("requestId")
 84
 85        total_runs = d.pop("totalRuns")
 86
 87        shards = d.pop("shards")
 88
 89        seed = d.pop("seed")
 90
 91        queued = d.pop("queued")
 92
 93        _walk_forward = d.pop("walkForward", UNSET)
 94        walk_forward: WalkForwardAccepted | Unset
 95        if isinstance(_walk_forward, Unset):
 96            walk_forward = UNSET
 97        else:
 98            walk_forward = WalkForwardAccepted.from_dict(_walk_forward)
 99
100        execute_sweep_accepted = cls(
101            sweep_id=sweep_id,
102            request_id=request_id,
103            total_runs=total_runs,
104            shards=shards,
105            seed=seed,
106            queued=queued,
107            walk_forward=walk_forward,
108        )
109
110        execute_sweep_accepted.additional_properties = d
111        return execute_sweep_accepted
additional_keys: list[str]
113    @property
114    def additional_keys(self) -> list[str]:
115        return list(self.additional_properties.keys())
class ExecuteSweepRequest:
 22@_attrs_define
 23class ExecuteSweepRequest:
 24    """
 25    Attributes:
 26        strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
 27            always yields the same id, for every caller, whatever its formatting. See
 28            `POST /strategy` for exactly which rewrites preserve it and which do not.
 29             Example: 6bsh31ikwkuivhtgcoa6s4.
 30        sweep (SweepSpecRequest):  Example: {'sampler': 'lhs', 'seed': 487221, 'samples': 100, 'objective': 'sharpe',
 31            'params': {'rsiPeriod': {'from': 7, 'to': 28, 'step': 1}, 'useTrendFilter': {'values': [True, False]}}}.
 32        base_config (SweepBaseConfig | Unset):
 33        store_signals (bool | Unset): Store signals for every trial. Keep false for normal sweeps. Default: False.
 34        shards (int | Unset): Requested horizontal shard count; 0 or omitted selects automatically. Default: 0.
 35        min_trade_floor (int | Unset): Trials below this trade count are flagged but remain in the results. Default: 30.
 36        walk_forward (WalkForwardRequest | Unset): Opt in to walk-forward validation. Present, the sweep runs as F
 37            sequential folds and the result gains a `walkForward` section; absent, nothing about the sweep changes. Two
 38            requests that differ only in this block are two different sweeps and do not deduplicate against each other.
 39        equity_curve (EquityCurveRequest | Unset): Selection (`mode`/`n`/`maxPct`) plus the transform preference
 40            (`resample`/`differential`/`outMode`) applied by `GET .../equityCurve` whenever ITS OWN query params are absent,
 41            for a curve this sweep retained. The transform half never affects retention or `sweepId` — a caller can always
 42            override it per-request at read time regardless of what was submitted here.
 43    """
 44
 45    strategy_id: str
 46    sweep: SweepSpecRequest
 47    base_config: SweepBaseConfig | Unset = UNSET
 48    store_signals: bool | Unset = False
 49    shards: int | Unset = 0
 50    min_trade_floor: int | Unset = 30
 51    walk_forward: WalkForwardRequest | Unset = UNSET
 52    equity_curve: EquityCurveRequest | Unset = UNSET
 53    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 54
 55    def to_dict(self) -> dict[str, Any]:
 56        strategy_id = self.strategy_id
 57
 58        sweep = self.sweep.to_dict()
 59
 60        base_config: dict[str, Any] | Unset = UNSET
 61        if not isinstance(self.base_config, Unset):
 62            base_config = self.base_config.to_dict()
 63
 64        store_signals = self.store_signals
 65
 66        shards = self.shards
 67
 68        min_trade_floor = self.min_trade_floor
 69
 70        walk_forward: dict[str, Any] | Unset = UNSET
 71        if not isinstance(self.walk_forward, Unset):
 72            walk_forward = self.walk_forward.to_dict()
 73
 74        equity_curve: dict[str, Any] | Unset = UNSET
 75        if not isinstance(self.equity_curve, Unset):
 76            equity_curve = self.equity_curve.to_dict()
 77
 78        field_dict: dict[str, Any] = {}
 79        field_dict.update(self.additional_properties)
 80        field_dict.update(
 81            {
 82                "strategyId": strategy_id,
 83                "sweep": sweep,
 84            }
 85        )
 86        if base_config is not UNSET:
 87            field_dict["baseConfig"] = base_config
 88        if store_signals is not UNSET:
 89            field_dict["storeSignals"] = store_signals
 90        if shards is not UNSET:
 91            field_dict["shards"] = shards
 92        if min_trade_floor is not UNSET:
 93            field_dict["minTradeFloor"] = min_trade_floor
 94        if walk_forward is not UNSET:
 95            field_dict["walkForward"] = walk_forward
 96        if equity_curve is not UNSET:
 97            field_dict["equityCurve"] = equity_curve
 98
 99        return field_dict
100
101    @classmethod
102    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
103        from ..models.equity_curve_request import EquityCurveRequest
104        from ..models.sweep_base_config import SweepBaseConfig
105        from ..models.sweep_spec_request import SweepSpecRequest
106        from ..models.walk_forward_request import WalkForwardRequest
107
108        d = dict(src_dict)
109        strategy_id = d.pop("strategyId")
110
111        sweep = SweepSpecRequest.from_dict(d.pop("sweep"))
112
113        _base_config = d.pop("baseConfig", UNSET)
114        base_config: SweepBaseConfig | Unset
115        if isinstance(_base_config, Unset):
116            base_config = UNSET
117        else:
118            base_config = SweepBaseConfig.from_dict(_base_config)
119
120        store_signals = d.pop("storeSignals", UNSET)
121
122        shards = d.pop("shards", UNSET)
123
124        min_trade_floor = d.pop("minTradeFloor", UNSET)
125
126        _walk_forward = d.pop("walkForward", UNSET)
127        walk_forward: WalkForwardRequest | Unset
128        if isinstance(_walk_forward, Unset):
129            walk_forward = UNSET
130        else:
131            walk_forward = WalkForwardRequest.from_dict(_walk_forward)
132
133        _equity_curve = d.pop("equityCurve", UNSET)
134        equity_curve: EquityCurveRequest | Unset
135        if isinstance(_equity_curve, Unset):
136            equity_curve = UNSET
137        else:
138            equity_curve = EquityCurveRequest.from_dict(_equity_curve)
139
140        execute_sweep_request = cls(
141            strategy_id=strategy_id,
142            sweep=sweep,
143            base_config=base_config,
144            store_signals=store_signals,
145            shards=shards,
146            min_trade_floor=min_trade_floor,
147            walk_forward=walk_forward,
148            equity_curve=equity_curve,
149        )
150
151        execute_sweep_request.additional_properties = d
152        return execute_sweep_request
153
154    @property
155    def additional_keys(self) -> list[str]:
156        return list(self.additional_properties.keys())
157
158    def __getitem__(self, key: str) -> Any:
159        return self.additional_properties[key]
160
161    def __setitem__(self, key: str, value: Any) -> None:
162        self.additional_properties[key] = value
163
164    def __delitem__(self, key: str) -> None:
165        del self.additional_properties[key]
166
167    def __contains__(self, key: str) -> bool:
168        return key in self.additional_properties

Attributes: strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code always yields the same id, for every caller, whatever its formatting. See POST /strategy for exactly which rewrites preserve it and which do not. Example: 6bsh31ikwkuivhtgcoa6s4. sweep (SweepSpecRequest): Example: {'sampler': 'lhs', 'seed': 487221, 'samples': 100, 'objective': 'sharpe', 'params': {'rsiPeriod': {'from': 7, 'to': 28, 'step': 1}, 'useTrendFilter': {'values': [True, False]}}}. base_config (SweepBaseConfig | Unset): store_signals (bool | Unset): Store signals for every trial. Keep false for normal sweeps. Default: False. shards (int | Unset): Requested horizontal shard count; 0 or omitted selects automatically. Default: 0. min_trade_floor (int | Unset): Trials below this trade count are flagged but remain in the results. Default: 30. walk_forward (WalkForwardRequest | Unset): Opt in to walk-forward validation. Present, the sweep runs as F sequential folds and the result gains a walkForward section; absent, nothing about the sweep changes. Two requests that differ only in this block are two different sweeps and do not deduplicate against each other. equity_curve (EquityCurveRequest | Unset): Selection (mode/n/maxPct) plus the transform preference (resample/differential/outMode) applied by GET .../equityCurve whenever ITS OWN query params are absent, for a curve this sweep retained. The transform half never affects retention or sweepId — a caller can always override it per-request at read time regardless of what was submitted here.

ExecuteSweepRequest( strategy_id: str, sweep: SweepSpecRequest, base_config: SweepBaseConfig | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, store_signals: bool | qtsurfer.api.client._generated.types.Unset = False, shards: int | qtsurfer.api.client._generated.types.Unset = 0, min_trade_floor: int | qtsurfer.api.client._generated.types.Unset = 30, walk_forward: WalkForwardRequest | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, equity_curve: EquityCurveRequest | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
31def __init__(self, strategy_id, sweep, base_config=attr_dict['base_config'].default, store_signals=attr_dict['store_signals'].default, shards=attr_dict['shards'].default, min_trade_floor=attr_dict['min_trade_floor'].default, walk_forward=attr_dict['walk_forward'].default, equity_curve=attr_dict['equity_curve'].default):
32    self.strategy_id = strategy_id
33    self.sweep = sweep
34    self.base_config = base_config
35    self.store_signals = store_signals
36    self.shards = shards
37    self.min_trade_floor = min_trade_floor
38    self.walk_forward = walk_forward
39    self.equity_curve = equity_curve
40    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ExecuteSweepRequest.

strategy_id: str
base_config: SweepBaseConfig | qtsurfer.api.client._generated.types.Unset
store_signals: bool | qtsurfer.api.client._generated.types.Unset
shards: int | qtsurfer.api.client._generated.types.Unset
min_trade_floor: int | qtsurfer.api.client._generated.types.Unset
walk_forward: WalkForwardRequest | qtsurfer.api.client._generated.types.Unset
equity_curve: EquityCurveRequest | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
55    def to_dict(self) -> dict[str, Any]:
56        strategy_id = self.strategy_id
57
58        sweep = self.sweep.to_dict()
59
60        base_config: dict[str, Any] | Unset = UNSET
61        if not isinstance(self.base_config, Unset):
62            base_config = self.base_config.to_dict()
63
64        store_signals = self.store_signals
65
66        shards = self.shards
67
68        min_trade_floor = self.min_trade_floor
69
70        walk_forward: dict[str, Any] | Unset = UNSET
71        if not isinstance(self.walk_forward, Unset):
72            walk_forward = self.walk_forward.to_dict()
73
74        equity_curve: dict[str, Any] | Unset = UNSET
75        if not isinstance(self.equity_curve, Unset):
76            equity_curve = self.equity_curve.to_dict()
77
78        field_dict: dict[str, Any] = {}
79        field_dict.update(self.additional_properties)
80        field_dict.update(
81            {
82                "strategyId": strategy_id,
83                "sweep": sweep,
84            }
85        )
86        if base_config is not UNSET:
87            field_dict["baseConfig"] = base_config
88        if store_signals is not UNSET:
89            field_dict["storeSignals"] = store_signals
90        if shards is not UNSET:
91            field_dict["shards"] = shards
92        if min_trade_floor is not UNSET:
93            field_dict["minTradeFloor"] = min_trade_floor
94        if walk_forward is not UNSET:
95            field_dict["walkForward"] = walk_forward
96        if equity_curve is not UNSET:
97            field_dict["equityCurve"] = equity_curve
98
99        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
101    @classmethod
102    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
103        from ..models.equity_curve_request import EquityCurveRequest
104        from ..models.sweep_base_config import SweepBaseConfig
105        from ..models.sweep_spec_request import SweepSpecRequest
106        from ..models.walk_forward_request import WalkForwardRequest
107
108        d = dict(src_dict)
109        strategy_id = d.pop("strategyId")
110
111        sweep = SweepSpecRequest.from_dict(d.pop("sweep"))
112
113        _base_config = d.pop("baseConfig", UNSET)
114        base_config: SweepBaseConfig | Unset
115        if isinstance(_base_config, Unset):
116            base_config = UNSET
117        else:
118            base_config = SweepBaseConfig.from_dict(_base_config)
119
120        store_signals = d.pop("storeSignals", UNSET)
121
122        shards = d.pop("shards", UNSET)
123
124        min_trade_floor = d.pop("minTradeFloor", UNSET)
125
126        _walk_forward = d.pop("walkForward", UNSET)
127        walk_forward: WalkForwardRequest | Unset
128        if isinstance(_walk_forward, Unset):
129            walk_forward = UNSET
130        else:
131            walk_forward = WalkForwardRequest.from_dict(_walk_forward)
132
133        _equity_curve = d.pop("equityCurve", UNSET)
134        equity_curve: EquityCurveRequest | Unset
135        if isinstance(_equity_curve, Unset):
136            equity_curve = UNSET
137        else:
138            equity_curve = EquityCurveRequest.from_dict(_equity_curve)
139
140        execute_sweep_request = cls(
141            strategy_id=strategy_id,
142            sweep=sweep,
143            base_config=base_config,
144            store_signals=store_signals,
145            shards=shards,
146            min_trade_floor=min_trade_floor,
147            walk_forward=walk_forward,
148            equity_curve=equity_curve,
149        )
150
151        execute_sweep_request.additional_properties = d
152        return execute_sweep_request
additional_keys: list[str]
154    @property
155    def additional_keys(self) -> list[str]:
156        return list(self.additional_properties.keys())
class ExecuteSweepResult:
 26@_attrs_define
 27class ExecuteSweepResult:
 28    """
 29    Attributes:
 30        sweep_id (str):
 31        status (ExecuteSweepResultStatus): The sweep's own status vocabulary — not the same set `state.status` below
 32            uses. See `state` for why.
 33        objective (ExecuteSweepResultObjective):
 34        order (ExecuteSweepResultOrder):
 35        progress (SweepProgress): How far along a sweep is, and — when the sweep is still running — enough to tell a
 36            healthy one from a stuck one. The counts partition the shards (or, for a walk-forward sweep, the folds): every
 37            unit is either finished, failed, waiting to be retried, or not yet started.
 38        leaderboard_size (int): Total result rows currently available.
 39        truncated (bool): True only when the ranked view exceeds its display limit.
 40        leaderboard (list[SweepRunRow]):
 41        state (JobState): Information about a single job
 42        ranking (ExecuteSweepResultRanking | Unset): Which ordering was actually applied, which is not always the one
 43            requested: a sweep with no stored parameter grid cannot be plateau-ranked and falls back to `raw`. Always `raw`
 44            when `order=natural`.
 45        pbo (float | Unset): Probability of backtest overfitting for the sweep as a whole, by combinatorially symmetric
 46            cross-validation: how often the configuration that won in-sample lands below median out-of-sample. Above ~0.5
 47            the sweep is selecting noise, whatever its top row says. Computed once when the last shard finishes, so it is
 48            absent while the sweep is still running and on sweeps too small for the statistic to mean anything.
 49        pbo_splits (int | Unset): How many train/test splits the `pbo` figure was averaged over.
 50        fail_reason (str | Unset): Why the sweep produced less than it should have — the cause reported by the **first**
 51            shard to fail, not a list. It is what turns an inscrutable empty leaderboard into an answer: a sweep can come
 52            back `PARTIAL` with `done: 0` because the strategy could not be loaded at all, and without this the response
 53            says only that nothing finished.
 54            First failure wins and later ones are not recorded, so on a sweep where several shards failed for different
 55            reasons this names one of them rather than all. Absent when no shard reported a cause, which is the normal case
 56            for a healthy sweep — read it together with `progress.failedShards` rather than as a count of anything. Example:
 57            Failed to load/configure strategy.
 58        walk_forward (WalkForwardResult | Unset): Present only on a sweep submitted with `walkForward`, and present from
 59            acceptance onward — its presence, not its contents, is what identifies a walk-forward sweep. `completedFolds` is
 60            0 while the first fold is still running.
 61    """
 62
 63    sweep_id: str
 64    status: ExecuteSweepResultStatus
 65    objective: ExecuteSweepResultObjective
 66    order: ExecuteSweepResultOrder
 67    progress: SweepProgress
 68    leaderboard_size: int
 69    truncated: bool
 70    leaderboard: list[SweepRunRow]
 71    state: JobState
 72    ranking: ExecuteSweepResultRanking | Unset = UNSET
 73    pbo: float | Unset = UNSET
 74    pbo_splits: int | Unset = UNSET
 75    fail_reason: str | Unset = UNSET
 76    walk_forward: WalkForwardResult | Unset = UNSET
 77    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 78
 79    def to_dict(self) -> dict[str, Any]:
 80        sweep_id = self.sweep_id
 81
 82        status = self.status.value
 83
 84        objective = self.objective.value
 85
 86        order = self.order.value
 87
 88        progress = self.progress.to_dict()
 89
 90        leaderboard_size = self.leaderboard_size
 91
 92        truncated = self.truncated
 93
 94        leaderboard = []
 95        for leaderboard_item_data in self.leaderboard:
 96            leaderboard_item = leaderboard_item_data.to_dict()
 97            leaderboard.append(leaderboard_item)
 98
 99        state = self.state.to_dict()
100
101        ranking: str | Unset = UNSET
102        if not isinstance(self.ranking, Unset):
103            ranking = self.ranking.value
104
105        pbo = self.pbo
106
107        pbo_splits = self.pbo_splits
108
109        fail_reason = self.fail_reason
110
111        walk_forward: dict[str, Any] | Unset = UNSET
112        if not isinstance(self.walk_forward, Unset):
113            walk_forward = self.walk_forward.to_dict()
114
115        field_dict: dict[str, Any] = {}
116        field_dict.update(self.additional_properties)
117        field_dict.update(
118            {
119                "sweepId": sweep_id,
120                "status": status,
121                "objective": objective,
122                "order": order,
123                "progress": progress,
124                "leaderboardSize": leaderboard_size,
125                "truncated": truncated,
126                "leaderboard": leaderboard,
127                "state": state,
128            }
129        )
130        if ranking is not UNSET:
131            field_dict["ranking"] = ranking
132        if pbo is not UNSET:
133            field_dict["pbo"] = pbo
134        if pbo_splits is not UNSET:
135            field_dict["pboSplits"] = pbo_splits
136        if fail_reason is not UNSET:
137            field_dict["failReason"] = fail_reason
138        if walk_forward is not UNSET:
139            field_dict["walkForward"] = walk_forward
140
141        return field_dict
142
143    @classmethod
144    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
145        from ..models.job_state import JobState
146        from ..models.sweep_progress import SweepProgress
147        from ..models.sweep_run_row import SweepRunRow
148        from ..models.walk_forward_result import WalkForwardResult
149
150        d = dict(src_dict)
151        sweep_id = d.pop("sweepId")
152
153        status = ExecuteSweepResultStatus(d.pop("status"))
154
155        objective = ExecuteSweepResultObjective(d.pop("objective"))
156
157        order = ExecuteSweepResultOrder(d.pop("order"))
158
159        progress = SweepProgress.from_dict(d.pop("progress"))
160
161        leaderboard_size = d.pop("leaderboardSize")
162
163        truncated = d.pop("truncated")
164
165        leaderboard = []
166        _leaderboard = d.pop("leaderboard")
167        for leaderboard_item_data in _leaderboard:
168            leaderboard_item = SweepRunRow.from_dict(leaderboard_item_data)
169
170            leaderboard.append(leaderboard_item)
171
172        state = JobState.from_dict(d.pop("state"))
173
174        _ranking = d.pop("ranking", UNSET)
175        ranking: ExecuteSweepResultRanking | Unset
176        if isinstance(_ranking, Unset):
177            ranking = UNSET
178        else:
179            ranking = ExecuteSweepResultRanking(_ranking)
180
181        pbo = d.pop("pbo", UNSET)
182
183        pbo_splits = d.pop("pboSplits", UNSET)
184
185        fail_reason = d.pop("failReason", UNSET)
186
187        _walk_forward = d.pop("walkForward", UNSET)
188        walk_forward: WalkForwardResult | Unset
189        if isinstance(_walk_forward, Unset):
190            walk_forward = UNSET
191        else:
192            walk_forward = WalkForwardResult.from_dict(_walk_forward)
193
194        execute_sweep_result = cls(
195            sweep_id=sweep_id,
196            status=status,
197            objective=objective,
198            order=order,
199            progress=progress,
200            leaderboard_size=leaderboard_size,
201            truncated=truncated,
202            leaderboard=leaderboard,
203            state=state,
204            ranking=ranking,
205            pbo=pbo,
206            pbo_splits=pbo_splits,
207            fail_reason=fail_reason,
208            walk_forward=walk_forward,
209        )
210
211        execute_sweep_result.additional_properties = d
212        return execute_sweep_result
213
214    @property
215    def additional_keys(self) -> list[str]:
216        return list(self.additional_properties.keys())
217
218    def __getitem__(self, key: str) -> Any:
219        return self.additional_properties[key]
220
221    def __setitem__(self, key: str, value: Any) -> None:
222        self.additional_properties[key] = value
223
224    def __delitem__(self, key: str) -> None:
225        del self.additional_properties[key]
226
227    def __contains__(self, key: str) -> bool:
228        return key in self.additional_properties

Attributes: sweep_id (str): status (ExecuteSweepResultStatus): The sweep's own status vocabulary — not the same set state.status below uses. See state for why. objective (ExecuteSweepResultObjective): order (ExecuteSweepResultOrder): progress (SweepProgress): How far along a sweep is, and — when the sweep is still running — enough to tell a healthy one from a stuck one. The counts partition the shards (or, for a walk-forward sweep, the folds): every unit is either finished, failed, waiting to be retried, or not yet started. leaderboard_size (int): Total result rows currently available. truncated (bool): True only when the ranked view exceeds its display limit. leaderboard (list[SweepRunRow]): state (JobState): Information about a single job ranking (ExecuteSweepResultRanking | Unset): Which ordering was actually applied, which is not always the one requested: a sweep with no stored parameter grid cannot be plateau-ranked and falls back to raw. Always raw when order=natural. pbo (float | Unset): Probability of backtest overfitting for the sweep as a whole, by combinatorially symmetric cross-validation: how often the configuration that won in-sample lands below median out-of-sample. Above ~0.5 the sweep is selecting noise, whatever its top row says. Computed once when the last shard finishes, so it is absent while the sweep is still running and on sweeps too small for the statistic to mean anything. pbo_splits (int | Unset): How many train/test splits the pbo figure was averaged over. fail_reason (str | Unset): Why the sweep produced less than it should have — the cause reported by the first shard to fail, not a list. It is what turns an inscrutable empty leaderboard into an answer: a sweep can come back PARTIAL with done: 0 because the strategy could not be loaded at all, and without this the response says only that nothing finished. First failure wins and later ones are not recorded, so on a sweep where several shards failed for different reasons this names one of them rather than all. Absent when no shard reported a cause, which is the normal case for a healthy sweep — read it together with progress.failedShards rather than as a count of anything. Example: Failed to load/configure strategy. walk_forward (WalkForwardResult | Unset): Present only on a sweep submitted with walkForward, and present from acceptance onward — its presence, not its contents, is what identifies a walk-forward sweep. completedFolds is 0 while the first fold is still running.

ExecuteSweepResult( sweep_id: str, status: ExecuteSweepResultStatus, objective: ExecuteSweepResultObjective, order: ExecuteSweepResultOrder, progress: SweepProgress, leaderboard_size: int, truncated: bool, leaderboard: list[SweepRunRow], state: JobState, ranking: ExecuteSweepResultRanking | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, pbo: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, pbo_splits: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, fail_reason: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, walk_forward: WalkForwardResult | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
37def __init__(self, sweep_id, status, objective, order, progress, leaderboard_size, truncated, leaderboard, state, ranking=attr_dict['ranking'].default, pbo=attr_dict['pbo'].default, pbo_splits=attr_dict['pbo_splits'].default, fail_reason=attr_dict['fail_reason'].default, walk_forward=attr_dict['walk_forward'].default):
38    self.sweep_id = sweep_id
39    self.status = status
40    self.objective = objective
41    self.order = order
42    self.progress = progress
43    self.leaderboard_size = leaderboard_size
44    self.truncated = truncated
45    self.leaderboard = leaderboard
46    self.state = state
47    self.ranking = ranking
48    self.pbo = pbo
49    self.pbo_splits = pbo_splits
50    self.fail_reason = fail_reason
51    self.walk_forward = walk_forward
52    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ExecuteSweepResult.

sweep_id: str
progress: SweepProgress
leaderboard_size: int
truncated: bool
leaderboard: list[SweepRunRow]
state: JobState
ranking: ExecuteSweepResultRanking | qtsurfer.api.client._generated.types.Unset
pbo: float | qtsurfer.api.client._generated.types.Unset
pbo_splits: int | qtsurfer.api.client._generated.types.Unset
fail_reason: str | qtsurfer.api.client._generated.types.Unset
walk_forward: WalkForwardResult | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
 79    def to_dict(self) -> dict[str, Any]:
 80        sweep_id = self.sweep_id
 81
 82        status = self.status.value
 83
 84        objective = self.objective.value
 85
 86        order = self.order.value
 87
 88        progress = self.progress.to_dict()
 89
 90        leaderboard_size = self.leaderboard_size
 91
 92        truncated = self.truncated
 93
 94        leaderboard = []
 95        for leaderboard_item_data in self.leaderboard:
 96            leaderboard_item = leaderboard_item_data.to_dict()
 97            leaderboard.append(leaderboard_item)
 98
 99        state = self.state.to_dict()
100
101        ranking: str | Unset = UNSET
102        if not isinstance(self.ranking, Unset):
103            ranking = self.ranking.value
104
105        pbo = self.pbo
106
107        pbo_splits = self.pbo_splits
108
109        fail_reason = self.fail_reason
110
111        walk_forward: dict[str, Any] | Unset = UNSET
112        if not isinstance(self.walk_forward, Unset):
113            walk_forward = self.walk_forward.to_dict()
114
115        field_dict: dict[str, Any] = {}
116        field_dict.update(self.additional_properties)
117        field_dict.update(
118            {
119                "sweepId": sweep_id,
120                "status": status,
121                "objective": objective,
122                "order": order,
123                "progress": progress,
124                "leaderboardSize": leaderboard_size,
125                "truncated": truncated,
126                "leaderboard": leaderboard,
127                "state": state,
128            }
129        )
130        if ranking is not UNSET:
131            field_dict["ranking"] = ranking
132        if pbo is not UNSET:
133            field_dict["pbo"] = pbo
134        if pbo_splits is not UNSET:
135            field_dict["pboSplits"] = pbo_splits
136        if fail_reason is not UNSET:
137            field_dict["failReason"] = fail_reason
138        if walk_forward is not UNSET:
139            field_dict["walkForward"] = walk_forward
140
141        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
143    @classmethod
144    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
145        from ..models.job_state import JobState
146        from ..models.sweep_progress import SweepProgress
147        from ..models.sweep_run_row import SweepRunRow
148        from ..models.walk_forward_result import WalkForwardResult
149
150        d = dict(src_dict)
151        sweep_id = d.pop("sweepId")
152
153        status = ExecuteSweepResultStatus(d.pop("status"))
154
155        objective = ExecuteSweepResultObjective(d.pop("objective"))
156
157        order = ExecuteSweepResultOrder(d.pop("order"))
158
159        progress = SweepProgress.from_dict(d.pop("progress"))
160
161        leaderboard_size = d.pop("leaderboardSize")
162
163        truncated = d.pop("truncated")
164
165        leaderboard = []
166        _leaderboard = d.pop("leaderboard")
167        for leaderboard_item_data in _leaderboard:
168            leaderboard_item = SweepRunRow.from_dict(leaderboard_item_data)
169
170            leaderboard.append(leaderboard_item)
171
172        state = JobState.from_dict(d.pop("state"))
173
174        _ranking = d.pop("ranking", UNSET)
175        ranking: ExecuteSweepResultRanking | Unset
176        if isinstance(_ranking, Unset):
177            ranking = UNSET
178        else:
179            ranking = ExecuteSweepResultRanking(_ranking)
180
181        pbo = d.pop("pbo", UNSET)
182
183        pbo_splits = d.pop("pboSplits", UNSET)
184
185        fail_reason = d.pop("failReason", UNSET)
186
187        _walk_forward = d.pop("walkForward", UNSET)
188        walk_forward: WalkForwardResult | Unset
189        if isinstance(_walk_forward, Unset):
190            walk_forward = UNSET
191        else:
192            walk_forward = WalkForwardResult.from_dict(_walk_forward)
193
194        execute_sweep_result = cls(
195            sweep_id=sweep_id,
196            status=status,
197            objective=objective,
198            order=order,
199            progress=progress,
200            leaderboard_size=leaderboard_size,
201            truncated=truncated,
202            leaderboard=leaderboard,
203            state=state,
204            ranking=ranking,
205            pbo=pbo,
206            pbo_splits=pbo_splits,
207            fail_reason=fail_reason,
208            walk_forward=walk_forward,
209        )
210
211        execute_sweep_result.additional_properties = d
212        return execute_sweep_result
additional_keys: list[str]
214    @property
215    def additional_keys(self) -> list[str]:
216        return list(self.additional_properties.keys())
class ExecuteSweepResultObjective(builtins.str, enum.Enum):
 5class ExecuteSweepResultObjective(str, Enum):
 6    MAXDD = "maxdd"
 7    PNL = "pnl"
 8    SHARPE = "sharpe"
 9    SORTINO = "sortino"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

SHARPE = <ExecuteSweepResultObjective.SHARPE: 'sharpe'>
SORTINO = <ExecuteSweepResultObjective.SORTINO: 'sortino'>
class ExecuteSweepResultOrder(builtins.str, enum.Enum):
 5class ExecuteSweepResultOrder(str, Enum):
 6    NATURAL = "natural"
 7    RANKED = "ranked"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

NATURAL = <ExecuteSweepResultOrder.NATURAL: 'natural'>
RANKED = <ExecuteSweepResultOrder.RANKED: 'ranked'>
class ExecuteSweepResultRanking(builtins.str, enum.Enum):
 5class ExecuteSweepResultRanking(str, Enum):
 6    PLATEAU = "plateau"
 7    RAW = "raw"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

PLATEAU = <ExecuteSweepResultRanking.PLATEAU: 'plateau'>
class ExecuteSweepResultStatus(builtins.str, enum.Enum):
 5class ExecuteSweepResultStatus(str, Enum):
 6    CANCELLED = "CANCELLED"
 7    COMPLETED = "COMPLETED"
 8    PARTIAL = "PARTIAL"
 9    RUNNING = "RUNNING"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

CANCELLED = <ExecuteSweepResultStatus.CANCELLED: 'CANCELLED'>
COMPLETED = <ExecuteSweepResultStatus.COMPLETED: 'COMPLETED'>
PARTIAL = <ExecuteSweepResultStatus.PARTIAL: 'PARTIAL'>
RUNNING = <ExecuteSweepResultStatus.RUNNING: 'RUNNING'>
class FinalizeDatasetUploadResponse202:
13@_attrs_define
14class FinalizeDatasetUploadResponse202:
15    """
16    Attributes:
17        job_id (str):
18    """
19
20    job_id: str
21    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
22
23    def to_dict(self) -> dict[str, Any]:
24        job_id = self.job_id
25
26        field_dict: dict[str, Any] = {}
27        field_dict.update(self.additional_properties)
28        field_dict.update(
29            {
30                "jobId": job_id,
31            }
32        )
33
34        return field_dict
35
36    @classmethod
37    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
38        d = dict(src_dict)
39        job_id = d.pop("jobId")
40
41        finalize_dataset_upload_response_202 = cls(
42            job_id=job_id,
43        )
44
45        finalize_dataset_upload_response_202.additional_properties = d
46        return finalize_dataset_upload_response_202
47
48    @property
49    def additional_keys(self) -> list[str]:
50        return list(self.additional_properties.keys())
51
52    def __getitem__(self, key: str) -> Any:
53        return self.additional_properties[key]
54
55    def __setitem__(self, key: str, value: Any) -> None:
56        self.additional_properties[key] = value
57
58    def __delitem__(self, key: str) -> None:
59        del self.additional_properties[key]
60
61    def __contains__(self, key: str) -> bool:
62        return key in self.additional_properties

Attributes: job_id (str):

FinalizeDatasetUploadResponse202(job_id: str)
24def __init__(self, job_id):
25    self.job_id = job_id
26    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class FinalizeDatasetUploadResponse202.

job_id: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
23    def to_dict(self) -> dict[str, Any]:
24        job_id = self.job_id
25
26        field_dict: dict[str, Any] = {}
27        field_dict.update(self.additional_properties)
28        field_dict.update(
29            {
30                "jobId": job_id,
31            }
32        )
33
34        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
36    @classmethod
37    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
38        d = dict(src_dict)
39        job_id = d.pop("jobId")
40
41        finalize_dataset_upload_response_202 = cls(
42            job_id=job_id,
43        )
44
45        finalize_dataset_upload_response_202.additional_properties = d
46        return finalize_dataset_upload_response_202
additional_keys: list[str]
48    @property
49    def additional_keys(self) -> list[str]:
50        return list(self.additional_properties.keys())
class GetBacktestResultResponse202:
12@_attrs_define
13class GetBacktestResultResponse202:
14    """ """
15
16    def to_dict(self) -> dict[str, Any]:
17
18        field_dict: dict[str, Any] = {}
19
20        return field_dict
21
22    @classmethod
23    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
24        get_backtest_result_response_202 = cls()
25
26        return get_backtest_result_response_202
GetBacktestResultResponse202()
21def __init__(self, ):
22    pass

Method generated by attrs for class GetBacktestResultResponse202.

def to_dict(self) -> dict[str, typing.Any]:
16    def to_dict(self) -> dict[str, Any]:
17
18        field_dict: dict[str, Any] = {}
19
20        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
22    @classmethod
23    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
24        get_backtest_result_response_202 = cls()
25
26        return get_backtest_result_response_202
class GetStrategyCodeResponse200:
13@_attrs_define
14class GetStrategyCodeResponse200:
15    """
16    Attributes:
17        strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
18            always yields the same id, for every caller, whatever its formatting. See
19            `POST /strategy` for exactly which rewrites preserve it and which do not.
20             Example: 6bsh31ikwkuivhtgcoa6s4.
21        code (str): Raw strategy Java source code, exactly as registered.
22    """
23
24    strategy_id: str
25    code: str
26    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
27
28    def to_dict(self) -> dict[str, Any]:
29        strategy_id = self.strategy_id
30
31        code = self.code
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "strategyId": strategy_id,
38                "code": code,
39            }
40        )
41
42        return field_dict
43
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        strategy_id = d.pop("strategyId")
48
49        code = d.pop("code")
50
51        get_strategy_code_response_200 = cls(
52            strategy_id=strategy_id,
53            code=code,
54        )
55
56        get_strategy_code_response_200.additional_properties = d
57        return get_strategy_code_response_200
58
59    @property
60    def additional_keys(self) -> list[str]:
61        return list(self.additional_properties.keys())
62
63    def __getitem__(self, key: str) -> Any:
64        return self.additional_properties[key]
65
66    def __setitem__(self, key: str, value: Any) -> None:
67        self.additional_properties[key] = value
68
69    def __delitem__(self, key: str) -> None:
70        del self.additional_properties[key]
71
72    def __contains__(self, key: str) -> bool:
73        return key in self.additional_properties

Attributes: strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code always yields the same id, for every caller, whatever its formatting. See POST /strategy for exactly which rewrites preserve it and which do not. Example: 6bsh31ikwkuivhtgcoa6s4. code (str): Raw strategy Java source code, exactly as registered.

GetStrategyCodeResponse200(strategy_id: str, code: str)
25def __init__(self, strategy_id, code):
26    self.strategy_id = strategy_id
27    self.code = code
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class GetStrategyCodeResponse200.

strategy_id: str
code: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
28    def to_dict(self) -> dict[str, Any]:
29        strategy_id = self.strategy_id
30
31        code = self.code
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "strategyId": strategy_id,
38                "code": code,
39            }
40        )
41
42        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        strategy_id = d.pop("strategyId")
48
49        code = d.pop("code")
50
51        get_strategy_code_response_200 = cls(
52            strategy_id=strategy_id,
53            code=code,
54        )
55
56        get_strategy_code_response_200.additional_properties = d
57        return get_strategy_code_response_200
additional_keys: list[str]
59    @property
60    def additional_keys(self) -> list[str]:
61        return list(self.additional_properties.keys())
class GetSweepResultObjective(builtins.str, enum.Enum):
 5class GetSweepResultObjective(str, Enum):
 6    MAXDD = "maxdd"
 7    PNL = "pnl"
 8    SHARPE = "sharpe"
 9    SORTINO = "sortino"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

MAXDD = <GetSweepResultObjective.MAXDD: 'maxdd'>
SHARPE = <GetSweepResultObjective.SHARPE: 'sharpe'>
SORTINO = <GetSweepResultObjective.SORTINO: 'sortino'>
class GetSweepResultOrder(builtins.str, enum.Enum):
 5class GetSweepResultOrder(str, Enum):
 6    NATURAL = "natural"
 7    RANKED = "ranked"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

NATURAL = <GetSweepResultOrder.NATURAL: 'natural'>
RANKED = <GetSweepResultOrder.RANKED: 'ranked'>
class GetSweepResultRanking(builtins.str, enum.Enum):
 5class GetSweepResultRanking(str, Enum):
 6    PLATEAU = "plateau"
 7    RAW = "raw"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

PLATEAU = <GetSweepResultRanking.PLATEAU: 'plateau'>
RAW = <GetSweepResultRanking.RAW: 'raw'>
class GetSweepSensitivityObjective(builtins.str, enum.Enum):
 5class GetSweepSensitivityObjective(str, Enum):
 6    MAXDD = "maxdd"
 7    PNL = "pnl"
 8    SHARPE = "sharpe"
 9    SORTINO = "sortino"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

SORTINO = <GetSweepSensitivityObjective.SORTINO: 'sortino'>
class InstrumentCoverage:
19@_attrs_define
20class InstrumentCoverage:
21    """Time coverage of available data for this instrument, per data type
22
23    Attributes:
24        tickers (CoverageWindow | Unset): The time range of available data for a single data type
25        klines (CoverageWindow | Unset): The time range of available data for a single data type
26    """
27
28    tickers: CoverageWindow | Unset = UNSET
29    klines: CoverageWindow | Unset = UNSET
30    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
31
32    def to_dict(self) -> dict[str, Any]:
33        tickers: dict[str, Any] | Unset = UNSET
34        if not isinstance(self.tickers, Unset):
35            tickers = self.tickers.to_dict()
36
37        klines: dict[str, Any] | Unset = UNSET
38        if not isinstance(self.klines, Unset):
39            klines = self.klines.to_dict()
40
41        field_dict: dict[str, Any] = {}
42        field_dict.update(self.additional_properties)
43        field_dict.update({})
44        if tickers is not UNSET:
45            field_dict["tickers"] = tickers
46        if klines is not UNSET:
47            field_dict["klines"] = klines
48
49        return field_dict
50
51    @classmethod
52    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
53        from ..models.coverage_window import CoverageWindow
54
55        d = dict(src_dict)
56        _tickers = d.pop("tickers", UNSET)
57        tickers: CoverageWindow | Unset
58        if isinstance(_tickers, Unset):
59            tickers = UNSET
60        else:
61            tickers = CoverageWindow.from_dict(_tickers)
62
63        _klines = d.pop("klines", UNSET)
64        klines: CoverageWindow | Unset
65        if isinstance(_klines, Unset):
66            klines = UNSET
67        else:
68            klines = CoverageWindow.from_dict(_klines)
69
70        instrument_coverage = cls(
71            tickers=tickers,
72            klines=klines,
73        )
74
75        instrument_coverage.additional_properties = d
76        return instrument_coverage
77
78    @property
79    def additional_keys(self) -> list[str]:
80        return list(self.additional_properties.keys())
81
82    def __getitem__(self, key: str) -> Any:
83        return self.additional_properties[key]
84
85    def __setitem__(self, key: str, value: Any) -> None:
86        self.additional_properties[key] = value
87
88    def __delitem__(self, key: str) -> None:
89        del self.additional_properties[key]
90
91    def __contains__(self, key: str) -> bool:
92        return key in self.additional_properties

Time coverage of available data for this instrument, per data type

Attributes: tickers (CoverageWindow | Unset): The time range of available data for a single data type klines (CoverageWindow | Unset): The time range of available data for a single data type

InstrumentCoverage( tickers: CoverageWindow | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, klines: CoverageWindow | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
25def __init__(self, tickers=attr_dict['tickers'].default, klines=attr_dict['klines'].default):
26    self.tickers = tickers
27    self.klines = klines
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class InstrumentCoverage.

tickers: CoverageWindow | qtsurfer.api.client._generated.types.Unset
klines: CoverageWindow | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
32    def to_dict(self) -> dict[str, Any]:
33        tickers: dict[str, Any] | Unset = UNSET
34        if not isinstance(self.tickers, Unset):
35            tickers = self.tickers.to_dict()
36
37        klines: dict[str, Any] | Unset = UNSET
38        if not isinstance(self.klines, Unset):
39            klines = self.klines.to_dict()
40
41        field_dict: dict[str, Any] = {}
42        field_dict.update(self.additional_properties)
43        field_dict.update({})
44        if tickers is not UNSET:
45            field_dict["tickers"] = tickers
46        if klines is not UNSET:
47            field_dict["klines"] = klines
48
49        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
51    @classmethod
52    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
53        from ..models.coverage_window import CoverageWindow
54
55        d = dict(src_dict)
56        _tickers = d.pop("tickers", UNSET)
57        tickers: CoverageWindow | Unset
58        if isinstance(_tickers, Unset):
59            tickers = UNSET
60        else:
61            tickers = CoverageWindow.from_dict(_tickers)
62
63        _klines = d.pop("klines", UNSET)
64        klines: CoverageWindow | Unset
65        if isinstance(_klines, Unset):
66            klines = UNSET
67        else:
68            klines = CoverageWindow.from_dict(_klines)
69
70        instrument_coverage = cls(
71            tickers=tickers,
72            klines=klines,
73        )
74
75        instrument_coverage.additional_properties = d
76        return instrument_coverage
additional_keys: list[str]
78    @property
79    def additional_keys(self) -> list[str]:
80        return list(self.additional_properties.keys())
class InstrumentDetail:
 19@_attrs_define
 20class InstrumentDetail:
 21    """Exchange instrument with per-data-type coverage and market info
 22
 23    Attributes:
 24        id (str): Instrument identifier (e.g. currency pair) Example: BTC/USDT.
 25        base (str): Base currency Example: BTC.
 26        quote (str): Quote currency Example: USDT.
 27        coverage (InstrumentCoverage | Unset): Time coverage of available data for this instrument, per data type
 28        last_price (float | Unset): Last traded price Example: 84250.5.
 29        volume24h (float | Unset): Trading volume in the last 24 hours (in quote currency) Example: 1234567.89.
 30    """
 31
 32    id: str
 33    base: str
 34    quote: str
 35    coverage: InstrumentCoverage | Unset = UNSET
 36    last_price: float | Unset = UNSET
 37    volume24h: float | Unset = UNSET
 38    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 39
 40    def to_dict(self) -> dict[str, Any]:
 41        id = self.id
 42
 43        base = self.base
 44
 45        quote = self.quote
 46
 47        coverage: dict[str, Any] | Unset = UNSET
 48        if not isinstance(self.coverage, Unset):
 49            coverage = self.coverage.to_dict()
 50
 51        last_price = self.last_price
 52
 53        volume24h = self.volume24h
 54
 55        field_dict: dict[str, Any] = {}
 56        field_dict.update(self.additional_properties)
 57        field_dict.update(
 58            {
 59                "id": id,
 60                "base": base,
 61                "quote": quote,
 62            }
 63        )
 64        if coverage is not UNSET:
 65            field_dict["coverage"] = coverage
 66        if last_price is not UNSET:
 67            field_dict["lastPrice"] = last_price
 68        if volume24h is not UNSET:
 69            field_dict["volume24h"] = volume24h
 70
 71        return field_dict
 72
 73    @classmethod
 74    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 75        from ..models.instrument_coverage import InstrumentCoverage
 76
 77        d = dict(src_dict)
 78        id = d.pop("id")
 79
 80        base = d.pop("base")
 81
 82        quote = d.pop("quote")
 83
 84        _coverage = d.pop("coverage", UNSET)
 85        coverage: InstrumentCoverage | Unset
 86        if isinstance(_coverage, Unset):
 87            coverage = UNSET
 88        else:
 89            coverage = InstrumentCoverage.from_dict(_coverage)
 90
 91        last_price = d.pop("lastPrice", UNSET)
 92
 93        volume24h = d.pop("volume24h", UNSET)
 94
 95        instrument_detail = cls(
 96            id=id,
 97            base=base,
 98            quote=quote,
 99            coverage=coverage,
100            last_price=last_price,
101            volume24h=volume24h,
102        )
103
104        instrument_detail.additional_properties = d
105        return instrument_detail
106
107    @property
108    def additional_keys(self) -> list[str]:
109        return list(self.additional_properties.keys())
110
111    def __getitem__(self, key: str) -> Any:
112        return self.additional_properties[key]
113
114    def __setitem__(self, key: str, value: Any) -> None:
115        self.additional_properties[key] = value
116
117    def __delitem__(self, key: str) -> None:
118        del self.additional_properties[key]
119
120    def __contains__(self, key: str) -> bool:
121        return key in self.additional_properties

Exchange instrument with per-data-type coverage and market info

Attributes: id (str): Instrument identifier (e.g. currency pair) Example: BTC/USDT. base (str): Base currency Example: BTC. quote (str): Quote currency Example: USDT. coverage (InstrumentCoverage | Unset): Time coverage of available data for this instrument, per data type last_price (float | Unset): Last traded price Example: 84250.5. volume24h (float | Unset): Trading volume in the last 24 hours (in quote currency) Example: 1234567.89.

InstrumentDetail( id: str, base: str, quote: str, coverage: InstrumentCoverage | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, last_price: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, volume24h: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
29def __init__(self, id, base, quote, coverage=attr_dict['coverage'].default, last_price=attr_dict['last_price'].default, volume24h=attr_dict['volume24h'].default):
30    self.id = id
31    self.base = base
32    self.quote = quote
33    self.coverage = coverage
34    self.last_price = last_price
35    self.volume24h = volume24h
36    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class InstrumentDetail.

id: str
base: str
quote: str
coverage: InstrumentCoverage | qtsurfer.api.client._generated.types.Unset
last_price: float | qtsurfer.api.client._generated.types.Unset
volume24h: float | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
40    def to_dict(self) -> dict[str, Any]:
41        id = self.id
42
43        base = self.base
44
45        quote = self.quote
46
47        coverage: dict[str, Any] | Unset = UNSET
48        if not isinstance(self.coverage, Unset):
49            coverage = self.coverage.to_dict()
50
51        last_price = self.last_price
52
53        volume24h = self.volume24h
54
55        field_dict: dict[str, Any] = {}
56        field_dict.update(self.additional_properties)
57        field_dict.update(
58            {
59                "id": id,
60                "base": base,
61                "quote": quote,
62            }
63        )
64        if coverage is not UNSET:
65            field_dict["coverage"] = coverage
66        if last_price is not UNSET:
67            field_dict["lastPrice"] = last_price
68        if volume24h is not UNSET:
69            field_dict["volume24h"] = volume24h
70
71        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 73    @classmethod
 74    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 75        from ..models.instrument_coverage import InstrumentCoverage
 76
 77        d = dict(src_dict)
 78        id = d.pop("id")
 79
 80        base = d.pop("base")
 81
 82        quote = d.pop("quote")
 83
 84        _coverage = d.pop("coverage", UNSET)
 85        coverage: InstrumentCoverage | Unset
 86        if isinstance(_coverage, Unset):
 87            coverage = UNSET
 88        else:
 89            coverage = InstrumentCoverage.from_dict(_coverage)
 90
 91        last_price = d.pop("lastPrice", UNSET)
 92
 93        volume24h = d.pop("volume24h", UNSET)
 94
 95        instrument_detail = cls(
 96            id=id,
 97            base=base,
 98            quote=quote,
 99            coverage=coverage,
100            last_price=last_price,
101            volume24h=volume24h,
102        )
103
104        instrument_detail.additional_properties = d
105        return instrument_detail
additional_keys: list[str]
107    @property
108    def additional_keys(self) -> list[str]:
109        return list(self.additional_properties.keys())
class InstrumentListMeta:
17@_attrs_define
18class InstrumentListMeta:
19    """Metadata describing the instruments listing
20
21    Attributes:
22        updated_at (datetime.datetime): When this listing was last refreshed Example: 2026-07-09T19:09:07Z.
23        exchange (str): The exchange the instruments belong to Example: binance.
24        segment (InstrumentListMetaSegment): The market segment served in `data` Example: spot.
25    """
26
27    updated_at: datetime.datetime
28    exchange: str
29    segment: InstrumentListMetaSegment
30    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
31
32    def to_dict(self) -> dict[str, Any]:
33        updated_at = self.updated_at.isoformat()
34
35        exchange = self.exchange
36
37        segment = self.segment.value
38
39        field_dict: dict[str, Any] = {}
40        field_dict.update(self.additional_properties)
41        field_dict.update(
42            {
43                "updatedAt": updated_at,
44                "exchange": exchange,
45                "segment": segment,
46            }
47        )
48
49        return field_dict
50
51    @classmethod
52    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
53        d = dict(src_dict)
54        updated_at = isoparse(d.pop("updatedAt"))
55
56        exchange = d.pop("exchange")
57
58        segment = InstrumentListMetaSegment(d.pop("segment"))
59
60        instrument_list_meta = cls(
61            updated_at=updated_at,
62            exchange=exchange,
63            segment=segment,
64        )
65
66        instrument_list_meta.additional_properties = d
67        return instrument_list_meta
68
69    @property
70    def additional_keys(self) -> list[str]:
71        return list(self.additional_properties.keys())
72
73    def __getitem__(self, key: str) -> Any:
74        return self.additional_properties[key]
75
76    def __setitem__(self, key: str, value: Any) -> None:
77        self.additional_properties[key] = value
78
79    def __delitem__(self, key: str) -> None:
80        del self.additional_properties[key]
81
82    def __contains__(self, key: str) -> bool:
83        return key in self.additional_properties

Metadata describing the instruments listing

Attributes: updated_at (datetime.datetime): When this listing was last refreshed Example: 2026-07-09T19:09:07Z. exchange (str): The exchange the instruments belong to Example: binance. segment (InstrumentListMetaSegment): The market segment served in data Example: spot.

InstrumentListMeta( updated_at: datetime.datetime, exchange: str, segment: InstrumentListMetaSegment)
26def __init__(self, updated_at, exchange, segment):
27    self.updated_at = updated_at
28    self.exchange = exchange
29    self.segment = segment
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class InstrumentListMeta.

updated_at: datetime.datetime
exchange: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
32    def to_dict(self) -> dict[str, Any]:
33        updated_at = self.updated_at.isoformat()
34
35        exchange = self.exchange
36
37        segment = self.segment.value
38
39        field_dict: dict[str, Any] = {}
40        field_dict.update(self.additional_properties)
41        field_dict.update(
42            {
43                "updatedAt": updated_at,
44                "exchange": exchange,
45                "segment": segment,
46            }
47        )
48
49        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
51    @classmethod
52    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
53        d = dict(src_dict)
54        updated_at = isoparse(d.pop("updatedAt"))
55
56        exchange = d.pop("exchange")
57
58        segment = InstrumentListMetaSegment(d.pop("segment"))
59
60        instrument_list_meta = cls(
61            updated_at=updated_at,
62            exchange=exchange,
63            segment=segment,
64        )
65
66        instrument_list_meta.additional_properties = d
67        return instrument_list_meta
additional_keys: list[str]
69    @property
70    def additional_keys(self) -> list[str]:
71        return list(self.additional_properties.keys())
class InstrumentListMetaSegment(builtins.str, enum.Enum):
 5class InstrumentListMetaSegment(str, Enum):
 6    FUTURES = "futures"
 7    SPOT = "spot"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

FUTURES = <InstrumentListMetaSegment.FUTURES: 'futures'>
class InstrumentListResponse:
19@_attrs_define
20class InstrumentListResponse:
21    """HAL-style response envelope for the instruments listing
22
23    Attributes:
24        data (list[InstrumentDetail]): The list of instruments for the segment
25        meta (InstrumentListMeta): Metadata describing the instruments listing
26        field_links (InstrumentLinks): HAL `_links` — segment discovery for the instruments listing
27    """
28
29    data: list[InstrumentDetail]
30    meta: InstrumentListMeta
31    field_links: InstrumentLinks
32    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
33
34    def to_dict(self) -> dict[str, Any]:
35        data = []
36        for data_item_data in self.data:
37            data_item = data_item_data.to_dict()
38            data.append(data_item)
39
40        meta = self.meta.to_dict()
41
42        field_links = self.field_links.to_dict()
43
44        field_dict: dict[str, Any] = {}
45        field_dict.update(self.additional_properties)
46        field_dict.update(
47            {
48                "data": data,
49                "meta": meta,
50                "_links": field_links,
51            }
52        )
53
54        return field_dict
55
56    @classmethod
57    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
58        from ..models.instrument_detail import InstrumentDetail
59        from ..models.instrument_links import InstrumentLinks
60        from ..models.instrument_list_meta import InstrumentListMeta
61
62        d = dict(src_dict)
63        data = []
64        _data = d.pop("data")
65        for data_item_data in _data:
66            data_item = InstrumentDetail.from_dict(data_item_data)
67
68            data.append(data_item)
69
70        meta = InstrumentListMeta.from_dict(d.pop("meta"))
71
72        field_links = InstrumentLinks.from_dict(d.pop("_links"))
73
74        instrument_list_response = cls(
75            data=data,
76            meta=meta,
77            field_links=field_links,
78        )
79
80        instrument_list_response.additional_properties = d
81        return instrument_list_response
82
83    @property
84    def additional_keys(self) -> list[str]:
85        return list(self.additional_properties.keys())
86
87    def __getitem__(self, key: str) -> Any:
88        return self.additional_properties[key]
89
90    def __setitem__(self, key: str, value: Any) -> None:
91        self.additional_properties[key] = value
92
93    def __delitem__(self, key: str) -> None:
94        del self.additional_properties[key]
95
96    def __contains__(self, key: str) -> bool:
97        return key in self.additional_properties

HAL-style response envelope for the instruments listing

Attributes: data (list[InstrumentDetail]): The list of instruments for the segment meta (InstrumentListMeta): Metadata describing the instruments listing field_links (InstrumentLinks): HAL _links — segment discovery for the instruments listing

InstrumentListResponse( data: list[InstrumentDetail], meta: InstrumentListMeta, field_links: InstrumentLinks)
26def __init__(self, data, meta, field_links):
27    self.data = data
28    self.meta = meta
29    self.field_links = field_links
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class InstrumentListResponse.

data: list[InstrumentDetail]
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
34    def to_dict(self) -> dict[str, Any]:
35        data = []
36        for data_item_data in self.data:
37            data_item = data_item_data.to_dict()
38            data.append(data_item)
39
40        meta = self.meta.to_dict()
41
42        field_links = self.field_links.to_dict()
43
44        field_dict: dict[str, Any] = {}
45        field_dict.update(self.additional_properties)
46        field_dict.update(
47            {
48                "data": data,
49                "meta": meta,
50                "_links": field_links,
51            }
52        )
53
54        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
56    @classmethod
57    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
58        from ..models.instrument_detail import InstrumentDetail
59        from ..models.instrument_links import InstrumentLinks
60        from ..models.instrument_list_meta import InstrumentListMeta
61
62        d = dict(src_dict)
63        data = []
64        _data = d.pop("data")
65        for data_item_data in _data:
66            data_item = InstrumentDetail.from_dict(data_item_data)
67
68            data.append(data_item)
69
70        meta = InstrumentListMeta.from_dict(d.pop("meta"))
71
72        field_links = InstrumentLinks.from_dict(d.pop("_links"))
73
74        instrument_list_response = cls(
75            data=data,
76            meta=meta,
77            field_links=field_links,
78        )
79
80        instrument_list_response.additional_properties = d
81        return instrument_list_response
additional_keys: list[str]
83    @property
84    def additional_keys(self) -> list[str]:
85        return list(self.additional_properties.keys())
class JobState:
 18@_attrs_define
 19class JobState:
 20    """Information about a single job
 21
 22    Attributes:
 23        context_id (str): Opaque context identifier for the job Example: ctx_2o8heaioicr0edvx5ybcap.
 24        status (JobStateStatus): Current status of the job. Treat `Completed | Aborted | Failed` as
 25            terminal; `New | Started` mean keep polling. A single-instrument prepare
 26            is always terminal (`Completed`) — decide from
 27            `PrepareJobState.coverageRatio`, not by polling.
 28             Example: Completed.
 29        size (int): Total size of the data being prepared Example: 100.
 30        completed (int): The amount of data processed so far Example: 50.
 31        status_detail (None | str | Unset): Detailed status information, if available Example: Job completed with error
 32            code 5001.
 33        start_time (datetime.datetime | None | Unset): Timestamp for when the preparation started Example:
 34            2025-01-04T14:00:00Z.
 35        end_time (datetime.datetime | None | Unset): Timestamp for when the preparation finished Example:
 36            2025-01-04T14:00:20Z.
 37    """
 38
 39    context_id: str
 40    status: JobStateStatus
 41    size: int
 42    completed: int
 43    status_detail: None | str | Unset = UNSET
 44    start_time: datetime.datetime | None | Unset = UNSET
 45    end_time: datetime.datetime | None | Unset = UNSET
 46    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 47
 48    def to_dict(self) -> dict[str, Any]:
 49        context_id = self.context_id
 50
 51        status = self.status.value
 52
 53        size = self.size
 54
 55        completed = self.completed
 56
 57        status_detail: None | str | Unset
 58        if isinstance(self.status_detail, Unset):
 59            status_detail = UNSET
 60        else:
 61            status_detail = self.status_detail
 62
 63        start_time: None | str | Unset
 64        if isinstance(self.start_time, Unset):
 65            start_time = UNSET
 66        elif isinstance(self.start_time, datetime.datetime):
 67            start_time = self.start_time.isoformat()
 68        else:
 69            start_time = self.start_time
 70
 71        end_time: None | str | Unset
 72        if isinstance(self.end_time, Unset):
 73            end_time = UNSET
 74        elif isinstance(self.end_time, datetime.datetime):
 75            end_time = self.end_time.isoformat()
 76        else:
 77            end_time = self.end_time
 78
 79        field_dict: dict[str, Any] = {}
 80        field_dict.update(self.additional_properties)
 81        field_dict.update(
 82            {
 83                "contextId": context_id,
 84                "status": status,
 85                "size": size,
 86                "completed": completed,
 87            }
 88        )
 89        if status_detail is not UNSET:
 90            field_dict["statusDetail"] = status_detail
 91        if start_time is not UNSET:
 92            field_dict["startTime"] = start_time
 93        if end_time is not UNSET:
 94            field_dict["endTime"] = end_time
 95
 96        return field_dict
 97
 98    @classmethod
 99    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
100        d = dict(src_dict)
101        context_id = d.pop("contextId")
102
103        status = JobStateStatus(d.pop("status"))
104
105        size = d.pop("size")
106
107        completed = d.pop("completed")
108
109        def _parse_status_detail(data: object) -> None | str | Unset:
110            if data is None:
111                return data
112            if isinstance(data, Unset):
113                return data
114            return cast(None | str | Unset, data)
115
116        status_detail = _parse_status_detail(d.pop("statusDetail", UNSET))
117
118        def _parse_start_time(data: object) -> datetime.datetime | None | Unset:
119            if data is None:
120                return data
121            if isinstance(data, Unset):
122                return data
123            try:
124                if not isinstance(data, str):
125                    raise TypeError()
126                start_time_type_0 = isoparse(data)
127
128                return start_time_type_0
129            except (TypeError, ValueError, AttributeError, KeyError):
130                pass
131            return cast(datetime.datetime | None | Unset, data)
132
133        start_time = _parse_start_time(d.pop("startTime", UNSET))
134
135        def _parse_end_time(data: object) -> datetime.datetime | None | Unset:
136            if data is None:
137                return data
138            if isinstance(data, Unset):
139                return data
140            try:
141                if not isinstance(data, str):
142                    raise TypeError()
143                end_time_type_0 = isoparse(data)
144
145                return end_time_type_0
146            except (TypeError, ValueError, AttributeError, KeyError):
147                pass
148            return cast(datetime.datetime | None | Unset, data)
149
150        end_time = _parse_end_time(d.pop("endTime", UNSET))
151
152        job_state = cls(
153            context_id=context_id,
154            status=status,
155            size=size,
156            completed=completed,
157            status_detail=status_detail,
158            start_time=start_time,
159            end_time=end_time,
160        )
161
162        job_state.additional_properties = d
163        return job_state
164
165    @property
166    def additional_keys(self) -> list[str]:
167        return list(self.additional_properties.keys())
168
169    def __getitem__(self, key: str) -> Any:
170        return self.additional_properties[key]
171
172    def __setitem__(self, key: str, value: Any) -> None:
173        self.additional_properties[key] = value
174
175    def __delitem__(self, key: str) -> None:
176        del self.additional_properties[key]
177
178    def __contains__(self, key: str) -> bool:
179        return key in self.additional_properties

Information about a single job

Attributes: context_id (str): Opaque context identifier for the job Example: ctx_2o8heaioicr0edvx5ybcap. status (JobStateStatus): Current status of the job. Treat Completed | Aborted | Failed as terminal; New | Started mean keep polling. A single-instrument prepare is always terminal (Completed) — decide from PrepareJobState.coverageRatio, not by polling. Example: Completed. size (int): Total size of the data being prepared Example: 100. completed (int): The amount of data processed so far Example: 50. status_detail (None | str | Unset): Detailed status information, if available Example: Job completed with error code 5001. start_time (datetime.datetime | None | Unset): Timestamp for when the preparation started Example: 2025-01-04T14:00:00Z. end_time (datetime.datetime | None | Unset): Timestamp for when the preparation finished Example: 2025-01-04T14:00:20Z.

JobState( context_id: str, status: JobStateStatus, size: int, completed: int, status_detail: None | str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, start_time: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, end_time: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
30def __init__(self, context_id, status, size, completed, status_detail=attr_dict['status_detail'].default, start_time=attr_dict['start_time'].default, end_time=attr_dict['end_time'].default):
31    self.context_id = context_id
32    self.status = status
33    self.size = size
34    self.completed = completed
35    self.status_detail = status_detail
36    self.start_time = start_time
37    self.end_time = end_time
38    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class JobState.

context_id: str
status: JobStateStatus
size: int
completed: int
status_detail: None | str | qtsurfer.api.client._generated.types.Unset
start_time: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset
end_time: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
48    def to_dict(self) -> dict[str, Any]:
49        context_id = self.context_id
50
51        status = self.status.value
52
53        size = self.size
54
55        completed = self.completed
56
57        status_detail: None | str | Unset
58        if isinstance(self.status_detail, Unset):
59            status_detail = UNSET
60        else:
61            status_detail = self.status_detail
62
63        start_time: None | str | Unset
64        if isinstance(self.start_time, Unset):
65            start_time = UNSET
66        elif isinstance(self.start_time, datetime.datetime):
67            start_time = self.start_time.isoformat()
68        else:
69            start_time = self.start_time
70
71        end_time: None | str | Unset
72        if isinstance(self.end_time, Unset):
73            end_time = UNSET
74        elif isinstance(self.end_time, datetime.datetime):
75            end_time = self.end_time.isoformat()
76        else:
77            end_time = self.end_time
78
79        field_dict: dict[str, Any] = {}
80        field_dict.update(self.additional_properties)
81        field_dict.update(
82            {
83                "contextId": context_id,
84                "status": status,
85                "size": size,
86                "completed": completed,
87            }
88        )
89        if status_detail is not UNSET:
90            field_dict["statusDetail"] = status_detail
91        if start_time is not UNSET:
92            field_dict["startTime"] = start_time
93        if end_time is not UNSET:
94            field_dict["endTime"] = end_time
95
96        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 98    @classmethod
 99    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
100        d = dict(src_dict)
101        context_id = d.pop("contextId")
102
103        status = JobStateStatus(d.pop("status"))
104
105        size = d.pop("size")
106
107        completed = d.pop("completed")
108
109        def _parse_status_detail(data: object) -> None | str | Unset:
110            if data is None:
111                return data
112            if isinstance(data, Unset):
113                return data
114            return cast(None | str | Unset, data)
115
116        status_detail = _parse_status_detail(d.pop("statusDetail", UNSET))
117
118        def _parse_start_time(data: object) -> datetime.datetime | None | Unset:
119            if data is None:
120                return data
121            if isinstance(data, Unset):
122                return data
123            try:
124                if not isinstance(data, str):
125                    raise TypeError()
126                start_time_type_0 = isoparse(data)
127
128                return start_time_type_0
129            except (TypeError, ValueError, AttributeError, KeyError):
130                pass
131            return cast(datetime.datetime | None | Unset, data)
132
133        start_time = _parse_start_time(d.pop("startTime", UNSET))
134
135        def _parse_end_time(data: object) -> datetime.datetime | None | Unset:
136            if data is None:
137                return data
138            if isinstance(data, Unset):
139                return data
140            try:
141                if not isinstance(data, str):
142                    raise TypeError()
143                end_time_type_0 = isoparse(data)
144
145                return end_time_type_0
146            except (TypeError, ValueError, AttributeError, KeyError):
147                pass
148            return cast(datetime.datetime | None | Unset, data)
149
150        end_time = _parse_end_time(d.pop("endTime", UNSET))
151
152        job_state = cls(
153            context_id=context_id,
154            status=status,
155            size=size,
156            completed=completed,
157            status_detail=status_detail,
158            start_time=start_time,
159            end_time=end_time,
160        )
161
162        job_state.additional_properties = d
163        return job_state
additional_keys: list[str]
165    @property
166    def additional_keys(self) -> list[str]:
167        return list(self.additional_properties.keys())
class JobStateStatus(builtins.str, enum.Enum):
 5class JobStateStatus(str, Enum):
 6    ABORTED = "Aborted"
 7    COMPLETED = "Completed"
 8    FAILED = "Failed"
 9    NEW = "New"
10    STARTED = "Started"
11
12    def __str__(self) -> str:
13        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

ABORTED = <JobStateStatus.ABORTED: 'Aborted'>
COMPLETED = <JobStateStatus.COMPLETED: 'Completed'>
FAILED = <JobStateStatus.FAILED: 'Failed'>
NEW = <JobStateStatus.NEW: 'New'>
STARTED = <JobStateStatus.STARTED: 'Started'>
class ListDatasetsResponse200:
17@_attrs_define
18class ListDatasetsResponse200:
19    """
20    Attributes:
21        datasets (list[Dataset]):
22    """
23
24    datasets: list[Dataset]
25    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
26
27    def to_dict(self) -> dict[str, Any]:
28        datasets = []
29        for datasets_item_data in self.datasets:
30            datasets_item = datasets_item_data.to_dict()
31            datasets.append(datasets_item)
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "datasets": datasets,
38            }
39        )
40
41        return field_dict
42
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        from ..models.dataset import Dataset
46
47        d = dict(src_dict)
48        datasets = []
49        _datasets = d.pop("datasets")
50        for datasets_item_data in _datasets:
51            datasets_item = Dataset.from_dict(datasets_item_data)
52
53            datasets.append(datasets_item)
54
55        list_datasets_response_200 = cls(
56            datasets=datasets,
57        )
58
59        list_datasets_response_200.additional_properties = d
60        return list_datasets_response_200
61
62    @property
63    def additional_keys(self) -> list[str]:
64        return list(self.additional_properties.keys())
65
66    def __getitem__(self, key: str) -> Any:
67        return self.additional_properties[key]
68
69    def __setitem__(self, key: str, value: Any) -> None:
70        self.additional_properties[key] = value
71
72    def __delitem__(self, key: str) -> None:
73        del self.additional_properties[key]
74
75    def __contains__(self, key: str) -> bool:
76        return key in self.additional_properties

Attributes: datasets (list[Dataset]):

ListDatasetsResponse200( datasets: list[Dataset])
24def __init__(self, datasets):
25    self.datasets = datasets
26    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ListDatasetsResponse200.

datasets: list[Dataset]
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
27    def to_dict(self) -> dict[str, Any]:
28        datasets = []
29        for datasets_item_data in self.datasets:
30            datasets_item = datasets_item_data.to_dict()
31            datasets.append(datasets_item)
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "datasets": datasets,
38            }
39        )
40
41        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        from ..models.dataset import Dataset
46
47        d = dict(src_dict)
48        datasets = []
49        _datasets = d.pop("datasets")
50        for datasets_item_data in _datasets:
51            datasets_item = Dataset.from_dict(datasets_item_data)
52
53            datasets.append(datasets_item)
54
55        list_datasets_response_200 = cls(
56            datasets=datasets,
57        )
58
59        list_datasets_response_200.additional_properties = d
60        return list_datasets_response_200
additional_keys: list[str]
62    @property
63    def additional_keys(self) -> list[str]:
64        return list(self.additional_properties.keys())
class ListSegmentInstrumentsSegment(builtins.str, enum.Enum):
 5class ListSegmentInstrumentsSegment(str, Enum):
 6    FUTURES = "futures"
 7    SPOT = "spot"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

FUTURES = <ListSegmentInstrumentsSegment.FUTURES: 'futures'>
class ListStrategiesResponse200:
17@_attrs_define
18class ListStrategiesResponse200:
19    """
20    Attributes:
21        strategies (list[StrategySummary]):
22    """
23
24    strategies: list[StrategySummary]
25    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
26
27    def to_dict(self) -> dict[str, Any]:
28        strategies = []
29        for strategies_item_data in self.strategies:
30            strategies_item = strategies_item_data.to_dict()
31            strategies.append(strategies_item)
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "strategies": strategies,
38            }
39        )
40
41        return field_dict
42
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        from ..models.strategy_summary import StrategySummary
46
47        d = dict(src_dict)
48        strategies = []
49        _strategies = d.pop("strategies")
50        for strategies_item_data in _strategies:
51            strategies_item = StrategySummary.from_dict(strategies_item_data)
52
53            strategies.append(strategies_item)
54
55        list_strategies_response_200 = cls(
56            strategies=strategies,
57        )
58
59        list_strategies_response_200.additional_properties = d
60        return list_strategies_response_200
61
62    @property
63    def additional_keys(self) -> list[str]:
64        return list(self.additional_properties.keys())
65
66    def __getitem__(self, key: str) -> Any:
67        return self.additional_properties[key]
68
69    def __setitem__(self, key: str, value: Any) -> None:
70        self.additional_properties[key] = value
71
72    def __delitem__(self, key: str) -> None:
73        del self.additional_properties[key]
74
75    def __contains__(self, key: str) -> bool:
76        return key in self.additional_properties

Attributes: strategies (list[StrategySummary]):

ListStrategiesResponse200( strategies: list[StrategySummary])
24def __init__(self, strategies):
25    self.strategies = strategies
26    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ListStrategiesResponse200.

strategies: list[StrategySummary]
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
27    def to_dict(self) -> dict[str, Any]:
28        strategies = []
29        for strategies_item_data in self.strategies:
30            strategies_item = strategies_item_data.to_dict()
31            strategies.append(strategies_item)
32
33        field_dict: dict[str, Any] = {}
34        field_dict.update(self.additional_properties)
35        field_dict.update(
36            {
37                "strategies": strategies,
38            }
39        )
40
41        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        from ..models.strategy_summary import StrategySummary
46
47        d = dict(src_dict)
48        strategies = []
49        _strategies = d.pop("strategies")
50        for strategies_item_data in _strategies:
51            strategies_item = StrategySummary.from_dict(strategies_item_data)
52
53            strategies.append(strategies_item)
54
55        list_strategies_response_200 = cls(
56            strategies=strategies,
57        )
58
59        list_strategies_response_200.additional_properties = d
60        return list_strategies_response_200
additional_keys: list[str]
62    @property
63    def additional_keys(self) -> list[str]:
64        return list(self.additional_properties.keys())
class Notice:
 16@_attrs_define
 17class Notice:
 18    """A diagnostic the engine raised while the strategy ran. Advisory: it describes something worth
 19    knowing about how the strategy is wired, not necessarily an error.
 20
 21        Attributes:
 22            level (str): Severity as the engine classified it. Example: WARN.
 23            code (str): Stable identifier for the kind of finding; safe to match on. Example: indicator.bar-data-on-ticker-
 24                path.
 25            message (str): Human-readable explanation. Example: Indicator requires bar data but is on the ticker path.
 26            provenance (NoticeProvenance | Unset): Where it came from, which matters because the two silences differ: an
 27                empty list from a
 28                real run (`execute`) is a clean bill of health, while an empty list from
 29                `compile-dry-run` is only a lower bound over a bounded synthetic series.
 30                 Example: compile-dry-run.
 31    """
 32
 33    level: str
 34    code: str
 35    message: str
 36    provenance: NoticeProvenance | Unset = UNSET
 37    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 38
 39    def to_dict(self) -> dict[str, Any]:
 40        level = self.level
 41
 42        code = self.code
 43
 44        message = self.message
 45
 46        provenance: str | Unset = UNSET
 47        if not isinstance(self.provenance, Unset):
 48            provenance = self.provenance.value
 49
 50        field_dict: dict[str, Any] = {}
 51        field_dict.update(self.additional_properties)
 52        field_dict.update(
 53            {
 54                "level": level,
 55                "code": code,
 56                "message": message,
 57            }
 58        )
 59        if provenance is not UNSET:
 60            field_dict["provenance"] = provenance
 61
 62        return field_dict
 63
 64    @classmethod
 65    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 66        d = dict(src_dict)
 67        level = d.pop("level")
 68
 69        code = d.pop("code")
 70
 71        message = d.pop("message")
 72
 73        _provenance = d.pop("provenance", UNSET)
 74        provenance: NoticeProvenance | Unset
 75        if isinstance(_provenance, Unset):
 76            provenance = UNSET
 77        else:
 78            provenance = NoticeProvenance(_provenance)
 79
 80        notice = cls(
 81            level=level,
 82            code=code,
 83            message=message,
 84            provenance=provenance,
 85        )
 86
 87        notice.additional_properties = d
 88        return notice
 89
 90    @property
 91    def additional_keys(self) -> list[str]:
 92        return list(self.additional_properties.keys())
 93
 94    def __getitem__(self, key: str) -> Any:
 95        return self.additional_properties[key]
 96
 97    def __setitem__(self, key: str, value: Any) -> None:
 98        self.additional_properties[key] = value
 99
100    def __delitem__(self, key: str) -> None:
101        del self.additional_properties[key]
102
103    def __contains__(self, key: str) -> bool:
104        return key in self.additional_properties

A diagnostic the engine raised while the strategy ran. Advisory: it describes something worth knowing about how the strategy is wired, not necessarily an error.

Attributes:
    level (str): Severity as the engine classified it. Example: WARN.
    code (str): Stable identifier for the kind of finding; safe to match on. Example: indicator.bar-data-on-ticker-
        path.
    message (str): Human-readable explanation. Example: Indicator requires bar data but is on the ticker path.
    provenance (NoticeProvenance | Unset): Where it came from, which matters because the two silences differ: an
        empty list from a
        real run (`execute`) is a clean bill of health, while an empty list from
        `compile-dry-run` is only a lower bound over a bounded synthetic series.
         Example: compile-dry-run.
Notice( level: str, code: str, message: str, provenance: NoticeProvenance | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
27def __init__(self, level, code, message, provenance=attr_dict['provenance'].default):
28    self.level = level
29    self.code = code
30    self.message = message
31    self.provenance = provenance
32    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class Notice.

level: str
code: str
message: str
provenance: NoticeProvenance | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
39    def to_dict(self) -> dict[str, Any]:
40        level = self.level
41
42        code = self.code
43
44        message = self.message
45
46        provenance: str | Unset = UNSET
47        if not isinstance(self.provenance, Unset):
48            provenance = self.provenance.value
49
50        field_dict: dict[str, Any] = {}
51        field_dict.update(self.additional_properties)
52        field_dict.update(
53            {
54                "level": level,
55                "code": code,
56                "message": message,
57            }
58        )
59        if provenance is not UNSET:
60            field_dict["provenance"] = provenance
61
62        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
64    @classmethod
65    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
66        d = dict(src_dict)
67        level = d.pop("level")
68
69        code = d.pop("code")
70
71        message = d.pop("message")
72
73        _provenance = d.pop("provenance", UNSET)
74        provenance: NoticeProvenance | Unset
75        if isinstance(_provenance, Unset):
76            provenance = UNSET
77        else:
78            provenance = NoticeProvenance(_provenance)
79
80        notice = cls(
81            level=level,
82            code=code,
83            message=message,
84            provenance=provenance,
85        )
86
87        notice.additional_properties = d
88        return notice
additional_keys: list[str]
90    @property
91    def additional_keys(self) -> list[str]:
92        return list(self.additional_properties.keys())
class NoticeProvenance(builtins.str, enum.Enum):
 5class NoticeProvenance(str, Enum):
 6    COMPILE_DRY_RUN = "compile-dry-run"
 7    EXECUTE = "execute"
 8
 9    def __str__(self) -> str:
10        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

COMPILE_DRY_RUN = <NoticeProvenance.COMPILE_DRY_RUN: 'compile-dry-run'>
EXECUTE = <NoticeProvenance.EXECUTE: 'execute'>
class PrepareJobState:
 22@_attrs_define
 23class PrepareJobState:
 24    """State of a single-instrument prepare job — the `JobState` shape plus a coverage summary.
 25    A single-instrument prepare is always terminal (`status: Completed`): the client decides
 26    what to do from `coverageRatio` (e.g. execute if it is at or above a chosen threshold)
 27    rather than polling for missing hours that may never arrive — a missing hour for one
 28    instrument usually means low activity, not missing data.
 29
 30    **Two coverage shapes, by exchange vs. dataset.** Against a managed exchange, coverage is
 31    walked hour by hour: `totalHours`/`hoursWithData`/`hoursWithoutData`. Against a
 32    dataset-backed prepare (`exchangeId: user`), coverage is reported on the dataset's own
 33    cadence grid instead — hour-walking a daily dataset would report `1/24` and read as
 34    broken — via `cadence`/`gaps`/`largestGapSteps`; `totalHours`/`hoursWithData`/
 35    `hoursWithoutData` are absent in that case. `dataFrom`/`dataTo`/`coverageRatio` are present
 36    either way, computed accordingly.
 37
 38        Attributes:
 39            context_id (str): Opaque context identifier for the job Example: ctx_2o8heaioicr0edvx5ybcap.
 40            status (JobStateStatus): Current status of the job. Treat `Completed | Aborted | Failed` as
 41                terminal; `New | Started` mean keep polling. A single-instrument prepare
 42                is always terminal (`Completed`) — decide from
 43                `PrepareJobState.coverageRatio`, not by polling.
 44                 Example: Completed.
 45            size (int): Total size of the data being prepared Example: 100.
 46            completed (int): The amount of data processed so far Example: 50.
 47            status_detail (None | str | Unset): Detailed status information, if available Example: Job completed with error
 48                code 5001.
 49            start_time (datetime.datetime | None | Unset): Timestamp for when the preparation started Example:
 50                2025-01-04T14:00:00Z.
 51            end_time (datetime.datetime | None | Unset): Timestamp for when the preparation finished Example:
 52                2025-01-04T14:00:20Z.
 53            data_from (datetime.datetime | None | Unset): Start of the available data range for the prepared instrument.
 54                Example: 2026-04-14T13:00:00Z.
 55            data_to (datetime.datetime | None | Unset): End of the available data range for the prepared instrument.
 56                Example: 2026-04-14T15:30:05Z.
 57            coverage_ratio (float | Unset): Against a managed exchange: `hoursWithData / totalHours` in `[0,1]` (`1.0` when
 58                `totalHours` is 0), the fraction of hours in the requested range that have served
 59                data. Against a dataset (`exchangeId: user`): `rows / expectedStepsAtCadence`
 60                over the dataset version's own range — echoing what ingest computed once, not
 61                recomputed against a narrower prepare request.
 62                 Example: 0.994.
 63            total_hours (int | Unset): Number of whole hours in the requested prepare range. Managed exchanges only —
 64                absent for a dataset-backed prepare.
 65                 Example: 168.
 66            hours_with_data (int | Unset): Number of hours in the range that have data. Managed exchanges only — absent for
 67                a dataset-backed prepare.
 68                 Example: 167.
 69            cadence (str | Unset): The dataset version's own discovered cadence (e.g. `1m`, `1h`). Only present for a
 70                dataset-backed prepare (`exchangeId: user`).
 71                 Example: 1m.
 72            gaps (int | Unset): Number of gaps in the dataset version at its own cadence, as discovered at ingest
 73                time. Only present for a dataset-backed prepare.
 74            largest_gap_steps (int | Unset): The largest gap in the dataset version, in units of its own cadence step. Only
 75                present for a dataset-backed prepare.
 76            hours_without_data (list[PrepareJobStateHoursWithoutDataItem] | Unset): One entry per hour in the range that has
 77                no data, with a rationale. Managed
 78                exchanges only — absent for a dataset-backed prepare.
 79    """
 80
 81    context_id: str
 82    status: JobStateStatus
 83    size: int
 84    completed: int
 85    status_detail: None | str | Unset = UNSET
 86    start_time: datetime.datetime | None | Unset = UNSET
 87    end_time: datetime.datetime | None | Unset = UNSET
 88    data_from: datetime.datetime | None | Unset = UNSET
 89    data_to: datetime.datetime | None | Unset = UNSET
 90    coverage_ratio: float | Unset = UNSET
 91    total_hours: int | Unset = UNSET
 92    hours_with_data: int | Unset = UNSET
 93    cadence: str | Unset = UNSET
 94    gaps: int | Unset = UNSET
 95    largest_gap_steps: int | Unset = UNSET
 96    hours_without_data: list[PrepareJobStateHoursWithoutDataItem] | Unset = UNSET
 97    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 98
 99    def to_dict(self) -> dict[str, Any]:
100        context_id = self.context_id
101
102        status = self.status.value
103
104        size = self.size
105
106        completed = self.completed
107
108        status_detail: None | str | Unset
109        if isinstance(self.status_detail, Unset):
110            status_detail = UNSET
111        else:
112            status_detail = self.status_detail
113
114        start_time: None | str | Unset
115        if isinstance(self.start_time, Unset):
116            start_time = UNSET
117        elif isinstance(self.start_time, datetime.datetime):
118            start_time = self.start_time.isoformat()
119        else:
120            start_time = self.start_time
121
122        end_time: None | str | Unset
123        if isinstance(self.end_time, Unset):
124            end_time = UNSET
125        elif isinstance(self.end_time, datetime.datetime):
126            end_time = self.end_time.isoformat()
127        else:
128            end_time = self.end_time
129
130        data_from: None | str | Unset
131        if isinstance(self.data_from, Unset):
132            data_from = UNSET
133        elif isinstance(self.data_from, datetime.datetime):
134            data_from = self.data_from.isoformat()
135        else:
136            data_from = self.data_from
137
138        data_to: None | str | Unset
139        if isinstance(self.data_to, Unset):
140            data_to = UNSET
141        elif isinstance(self.data_to, datetime.datetime):
142            data_to = self.data_to.isoformat()
143        else:
144            data_to = self.data_to
145
146        coverage_ratio = self.coverage_ratio
147
148        total_hours = self.total_hours
149
150        hours_with_data = self.hours_with_data
151
152        cadence = self.cadence
153
154        gaps = self.gaps
155
156        largest_gap_steps = self.largest_gap_steps
157
158        hours_without_data: list[dict[str, Any]] | Unset = UNSET
159        if not isinstance(self.hours_without_data, Unset):
160            hours_without_data = []
161            for hours_without_data_item_data in self.hours_without_data:
162                hours_without_data_item = hours_without_data_item_data.to_dict()
163                hours_without_data.append(hours_without_data_item)
164
165        field_dict: dict[str, Any] = {}
166        field_dict.update(self.additional_properties)
167        field_dict.update(
168            {
169                "contextId": context_id,
170                "status": status,
171                "size": size,
172                "completed": completed,
173            }
174        )
175        if status_detail is not UNSET:
176            field_dict["statusDetail"] = status_detail
177        if start_time is not UNSET:
178            field_dict["startTime"] = start_time
179        if end_time is not UNSET:
180            field_dict["endTime"] = end_time
181        if data_from is not UNSET:
182            field_dict["dataFrom"] = data_from
183        if data_to is not UNSET:
184            field_dict["dataTo"] = data_to
185        if coverage_ratio is not UNSET:
186            field_dict["coverageRatio"] = coverage_ratio
187        if total_hours is not UNSET:
188            field_dict["totalHours"] = total_hours
189        if hours_with_data is not UNSET:
190            field_dict["hoursWithData"] = hours_with_data
191        if cadence is not UNSET:
192            field_dict["cadence"] = cadence
193        if gaps is not UNSET:
194            field_dict["gaps"] = gaps
195        if largest_gap_steps is not UNSET:
196            field_dict["largestGapSteps"] = largest_gap_steps
197        if hours_without_data is not UNSET:
198            field_dict["hoursWithoutData"] = hours_without_data
199
200        return field_dict
201
202    @classmethod
203    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
204        from ..models.prepare_job_state_hours_without_data_item import PrepareJobStateHoursWithoutDataItem
205
206        d = dict(src_dict)
207        context_id = d.pop("contextId")
208
209        status = JobStateStatus(d.pop("status"))
210
211        size = d.pop("size")
212
213        completed = d.pop("completed")
214
215        def _parse_status_detail(data: object) -> None | str | Unset:
216            if data is None:
217                return data
218            if isinstance(data, Unset):
219                return data
220            return cast(None | str | Unset, data)
221
222        status_detail = _parse_status_detail(d.pop("statusDetail", UNSET))
223
224        def _parse_start_time(data: object) -> datetime.datetime | None | Unset:
225            if data is None:
226                return data
227            if isinstance(data, Unset):
228                return data
229            try:
230                if not isinstance(data, str):
231                    raise TypeError()
232                start_time_type_0 = isoparse(data)
233
234                return start_time_type_0
235            except (TypeError, ValueError, AttributeError, KeyError):
236                pass
237            return cast(datetime.datetime | None | Unset, data)
238
239        start_time = _parse_start_time(d.pop("startTime", UNSET))
240
241        def _parse_end_time(data: object) -> datetime.datetime | None | Unset:
242            if data is None:
243                return data
244            if isinstance(data, Unset):
245                return data
246            try:
247                if not isinstance(data, str):
248                    raise TypeError()
249                end_time_type_0 = isoparse(data)
250
251                return end_time_type_0
252            except (TypeError, ValueError, AttributeError, KeyError):
253                pass
254            return cast(datetime.datetime | None | Unset, data)
255
256        end_time = _parse_end_time(d.pop("endTime", UNSET))
257
258        def _parse_data_from(data: object) -> datetime.datetime | None | Unset:
259            if data is None:
260                return data
261            if isinstance(data, Unset):
262                return data
263            try:
264                if not isinstance(data, str):
265                    raise TypeError()
266                data_from_type_0 = isoparse(data)
267
268                return data_from_type_0
269            except (TypeError, ValueError, AttributeError, KeyError):
270                pass
271            return cast(datetime.datetime | None | Unset, data)
272
273        data_from = _parse_data_from(d.pop("dataFrom", UNSET))
274
275        def _parse_data_to(data: object) -> datetime.datetime | None | Unset:
276            if data is None:
277                return data
278            if isinstance(data, Unset):
279                return data
280            try:
281                if not isinstance(data, str):
282                    raise TypeError()
283                data_to_type_0 = isoparse(data)
284
285                return data_to_type_0
286            except (TypeError, ValueError, AttributeError, KeyError):
287                pass
288            return cast(datetime.datetime | None | Unset, data)
289
290        data_to = _parse_data_to(d.pop("dataTo", UNSET))
291
292        coverage_ratio = d.pop("coverageRatio", UNSET)
293
294        total_hours = d.pop("totalHours", UNSET)
295
296        hours_with_data = d.pop("hoursWithData", UNSET)
297
298        cadence = d.pop("cadence", UNSET)
299
300        gaps = d.pop("gaps", UNSET)
301
302        largest_gap_steps = d.pop("largestGapSteps", UNSET)
303
304        _hours_without_data = d.pop("hoursWithoutData", UNSET)
305        hours_without_data: list[PrepareJobStateHoursWithoutDataItem] | Unset = UNSET
306        if _hours_without_data is not UNSET:
307            hours_without_data = []
308            for hours_without_data_item_data in _hours_without_data:
309                hours_without_data_item = PrepareJobStateHoursWithoutDataItem.from_dict(hours_without_data_item_data)
310
311                hours_without_data.append(hours_without_data_item)
312
313        prepare_job_state = cls(
314            context_id=context_id,
315            status=status,
316            size=size,
317            completed=completed,
318            status_detail=status_detail,
319            start_time=start_time,
320            end_time=end_time,
321            data_from=data_from,
322            data_to=data_to,
323            coverage_ratio=coverage_ratio,
324            total_hours=total_hours,
325            hours_with_data=hours_with_data,
326            cadence=cadence,
327            gaps=gaps,
328            largest_gap_steps=largest_gap_steps,
329            hours_without_data=hours_without_data,
330        )
331
332        prepare_job_state.additional_properties = d
333        return prepare_job_state
334
335    @property
336    def additional_keys(self) -> list[str]:
337        return list(self.additional_properties.keys())
338
339    def __getitem__(self, key: str) -> Any:
340        return self.additional_properties[key]
341
342    def __setitem__(self, key: str, value: Any) -> None:
343        self.additional_properties[key] = value
344
345    def __delitem__(self, key: str) -> None:
346        del self.additional_properties[key]
347
348    def __contains__(self, key: str) -> bool:
349        return key in self.additional_properties

State of a single-instrument prepare job — the JobState shape plus a coverage summary. A single-instrument prepare is always terminal (status: Completed): the client decides what to do from coverageRatio (e.g. execute if it is at or above a chosen threshold) rather than polling for missing hours that may never arrive — a missing hour for one instrument usually means low activity, not missing data.

Two coverage shapes, by exchange vs. dataset. Against a managed exchange, coverage is walked hour by hour: totalHours/hoursWithData/hoursWithoutData. Against a dataset-backed prepare (exchangeId: user), coverage is reported on the dataset's own cadence grid instead — hour-walking a daily dataset would report 1/24 and read as broken — via cadence/gaps/largestGapSteps; totalHours/hoursWithData/ hoursWithoutData are absent in that case. dataFrom/dataTo/coverageRatio are present either way, computed accordingly.

Attributes:
    context_id (str): Opaque context identifier for the job Example: ctx_2o8heaioicr0edvx5ybcap.
    status (JobStateStatus): Current status of the job. Treat `Completed | Aborted | Failed` as
        terminal; `New | Started` mean keep polling. A single-instrument prepare
        is always terminal (`Completed`) — decide from
        `PrepareJobState.coverageRatio`, not by polling.
         Example: Completed.
    size (int): Total size of the data being prepared Example: 100.
    completed (int): The amount of data processed so far Example: 50.
    status_detail (None | str | Unset): Detailed status information, if available Example: Job completed with error
        code 5001.
    start_time (datetime.datetime | None | Unset): Timestamp for when the preparation started Example:
        2025-01-04T14:00:00Z.
    end_time (datetime.datetime | None | Unset): Timestamp for when the preparation finished Example:
        2025-01-04T14:00:20Z.
    data_from (datetime.datetime | None | Unset): Start of the available data range for the prepared instrument.
        Example: 2026-04-14T13:00:00Z.
    data_to (datetime.datetime | None | Unset): End of the available data range for the prepared instrument.
        Example: 2026-04-14T15:30:05Z.
    coverage_ratio (float | Unset): Against a managed exchange: `hoursWithData / totalHours` in `[0,1]` (`1.0` when
        `totalHours` is 0), the fraction of hours in the requested range that have served
        data. Against a dataset (`exchangeId: user`): `rows / expectedStepsAtCadence`
        over the dataset version's own range — echoing what ingest computed once, not
        recomputed against a narrower prepare request.
         Example: 0.994.
    total_hours (int | Unset): Number of whole hours in the requested prepare range. Managed exchanges only —
        absent for a dataset-backed prepare.
         Example: 168.
    hours_with_data (int | Unset): Number of hours in the range that have data. Managed exchanges only — absent for
        a dataset-backed prepare.
         Example: 167.
    cadence (str | Unset): The dataset version's own discovered cadence (e.g. `1m`, `1h`). Only present for a
        dataset-backed prepare (`exchangeId: user`).
         Example: 1m.
    gaps (int | Unset): Number of gaps in the dataset version at its own cadence, as discovered at ingest
        time. Only present for a dataset-backed prepare.
    largest_gap_steps (int | Unset): The largest gap in the dataset version, in units of its own cadence step. Only
        present for a dataset-backed prepare.
    hours_without_data (list[PrepareJobStateHoursWithoutDataItem] | Unset): One entry per hour in the range that has
        no data, with a rationale. Managed
        exchanges only — absent for a dataset-backed prepare.
PrepareJobState( context_id: str, status: JobStateStatus, size: int, completed: int, status_detail: None | str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, start_time: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, end_time: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, data_from: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, data_to: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, coverage_ratio: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, total_hours: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, hours_with_data: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, cadence: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, gaps: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, largest_gap_steps: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, hours_without_data: list[PrepareJobStateHoursWithoutDataItem] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
39def __init__(self, context_id, status, size, completed, status_detail=attr_dict['status_detail'].default, start_time=attr_dict['start_time'].default, end_time=attr_dict['end_time'].default, data_from=attr_dict['data_from'].default, data_to=attr_dict['data_to'].default, coverage_ratio=attr_dict['coverage_ratio'].default, total_hours=attr_dict['total_hours'].default, hours_with_data=attr_dict['hours_with_data'].default, cadence=attr_dict['cadence'].default, gaps=attr_dict['gaps'].default, largest_gap_steps=attr_dict['largest_gap_steps'].default, hours_without_data=attr_dict['hours_without_data'].default):
40    self.context_id = context_id
41    self.status = status
42    self.size = size
43    self.completed = completed
44    self.status_detail = status_detail
45    self.start_time = start_time
46    self.end_time = end_time
47    self.data_from = data_from
48    self.data_to = data_to
49    self.coverage_ratio = coverage_ratio
50    self.total_hours = total_hours
51    self.hours_with_data = hours_with_data
52    self.cadence = cadence
53    self.gaps = gaps
54    self.largest_gap_steps = largest_gap_steps
55    self.hours_without_data = hours_without_data
56    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class PrepareJobState.

context_id: str
status: JobStateStatus
size: int
completed: int
status_detail: None | str | qtsurfer.api.client._generated.types.Unset
start_time: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset
end_time: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset
data_from: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset
data_to: datetime.datetime | None | qtsurfer.api.client._generated.types.Unset
coverage_ratio: float | qtsurfer.api.client._generated.types.Unset
total_hours: int | qtsurfer.api.client._generated.types.Unset
hours_with_data: int | qtsurfer.api.client._generated.types.Unset
cadence: str | qtsurfer.api.client._generated.types.Unset
gaps: int | qtsurfer.api.client._generated.types.Unset
largest_gap_steps: int | qtsurfer.api.client._generated.types.Unset
hours_without_data: list[PrepareJobStateHoursWithoutDataItem] | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
 99    def to_dict(self) -> dict[str, Any]:
100        context_id = self.context_id
101
102        status = self.status.value
103
104        size = self.size
105
106        completed = self.completed
107
108        status_detail: None | str | Unset
109        if isinstance(self.status_detail, Unset):
110            status_detail = UNSET
111        else:
112            status_detail = self.status_detail
113
114        start_time: None | str | Unset
115        if isinstance(self.start_time, Unset):
116            start_time = UNSET
117        elif isinstance(self.start_time, datetime.datetime):
118            start_time = self.start_time.isoformat()
119        else:
120            start_time = self.start_time
121
122        end_time: None | str | Unset
123        if isinstance(self.end_time, Unset):
124            end_time = UNSET
125        elif isinstance(self.end_time, datetime.datetime):
126            end_time = self.end_time.isoformat()
127        else:
128            end_time = self.end_time
129
130        data_from: None | str | Unset
131        if isinstance(self.data_from, Unset):
132            data_from = UNSET
133        elif isinstance(self.data_from, datetime.datetime):
134            data_from = self.data_from.isoformat()
135        else:
136            data_from = self.data_from
137
138        data_to: None | str | Unset
139        if isinstance(self.data_to, Unset):
140            data_to = UNSET
141        elif isinstance(self.data_to, datetime.datetime):
142            data_to = self.data_to.isoformat()
143        else:
144            data_to = self.data_to
145
146        coverage_ratio = self.coverage_ratio
147
148        total_hours = self.total_hours
149
150        hours_with_data = self.hours_with_data
151
152        cadence = self.cadence
153
154        gaps = self.gaps
155
156        largest_gap_steps = self.largest_gap_steps
157
158        hours_without_data: list[dict[str, Any]] | Unset = UNSET
159        if not isinstance(self.hours_without_data, Unset):
160            hours_without_data = []
161            for hours_without_data_item_data in self.hours_without_data:
162                hours_without_data_item = hours_without_data_item_data.to_dict()
163                hours_without_data.append(hours_without_data_item)
164
165        field_dict: dict[str, Any] = {}
166        field_dict.update(self.additional_properties)
167        field_dict.update(
168            {
169                "contextId": context_id,
170                "status": status,
171                "size": size,
172                "completed": completed,
173            }
174        )
175        if status_detail is not UNSET:
176            field_dict["statusDetail"] = status_detail
177        if start_time is not UNSET:
178            field_dict["startTime"] = start_time
179        if end_time is not UNSET:
180            field_dict["endTime"] = end_time
181        if data_from is not UNSET:
182            field_dict["dataFrom"] = data_from
183        if data_to is not UNSET:
184            field_dict["dataTo"] = data_to
185        if coverage_ratio is not UNSET:
186            field_dict["coverageRatio"] = coverage_ratio
187        if total_hours is not UNSET:
188            field_dict["totalHours"] = total_hours
189        if hours_with_data is not UNSET:
190            field_dict["hoursWithData"] = hours_with_data
191        if cadence is not UNSET:
192            field_dict["cadence"] = cadence
193        if gaps is not UNSET:
194            field_dict["gaps"] = gaps
195        if largest_gap_steps is not UNSET:
196            field_dict["largestGapSteps"] = largest_gap_steps
197        if hours_without_data is not UNSET:
198            field_dict["hoursWithoutData"] = hours_without_data
199
200        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
202    @classmethod
203    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
204        from ..models.prepare_job_state_hours_without_data_item import PrepareJobStateHoursWithoutDataItem
205
206        d = dict(src_dict)
207        context_id = d.pop("contextId")
208
209        status = JobStateStatus(d.pop("status"))
210
211        size = d.pop("size")
212
213        completed = d.pop("completed")
214
215        def _parse_status_detail(data: object) -> None | str | Unset:
216            if data is None:
217                return data
218            if isinstance(data, Unset):
219                return data
220            return cast(None | str | Unset, data)
221
222        status_detail = _parse_status_detail(d.pop("statusDetail", UNSET))
223
224        def _parse_start_time(data: object) -> datetime.datetime | None | Unset:
225            if data is None:
226                return data
227            if isinstance(data, Unset):
228                return data
229            try:
230                if not isinstance(data, str):
231                    raise TypeError()
232                start_time_type_0 = isoparse(data)
233
234                return start_time_type_0
235            except (TypeError, ValueError, AttributeError, KeyError):
236                pass
237            return cast(datetime.datetime | None | Unset, data)
238
239        start_time = _parse_start_time(d.pop("startTime", UNSET))
240
241        def _parse_end_time(data: object) -> datetime.datetime | None | Unset:
242            if data is None:
243                return data
244            if isinstance(data, Unset):
245                return data
246            try:
247                if not isinstance(data, str):
248                    raise TypeError()
249                end_time_type_0 = isoparse(data)
250
251                return end_time_type_0
252            except (TypeError, ValueError, AttributeError, KeyError):
253                pass
254            return cast(datetime.datetime | None | Unset, data)
255
256        end_time = _parse_end_time(d.pop("endTime", UNSET))
257
258        def _parse_data_from(data: object) -> datetime.datetime | None | Unset:
259            if data is None:
260                return data
261            if isinstance(data, Unset):
262                return data
263            try:
264                if not isinstance(data, str):
265                    raise TypeError()
266                data_from_type_0 = isoparse(data)
267
268                return data_from_type_0
269            except (TypeError, ValueError, AttributeError, KeyError):
270                pass
271            return cast(datetime.datetime | None | Unset, data)
272
273        data_from = _parse_data_from(d.pop("dataFrom", UNSET))
274
275        def _parse_data_to(data: object) -> datetime.datetime | None | Unset:
276            if data is None:
277                return data
278            if isinstance(data, Unset):
279                return data
280            try:
281                if not isinstance(data, str):
282                    raise TypeError()
283                data_to_type_0 = isoparse(data)
284
285                return data_to_type_0
286            except (TypeError, ValueError, AttributeError, KeyError):
287                pass
288            return cast(datetime.datetime | None | Unset, data)
289
290        data_to = _parse_data_to(d.pop("dataTo", UNSET))
291
292        coverage_ratio = d.pop("coverageRatio", UNSET)
293
294        total_hours = d.pop("totalHours", UNSET)
295
296        hours_with_data = d.pop("hoursWithData", UNSET)
297
298        cadence = d.pop("cadence", UNSET)
299
300        gaps = d.pop("gaps", UNSET)
301
302        largest_gap_steps = d.pop("largestGapSteps", UNSET)
303
304        _hours_without_data = d.pop("hoursWithoutData", UNSET)
305        hours_without_data: list[PrepareJobStateHoursWithoutDataItem] | Unset = UNSET
306        if _hours_without_data is not UNSET:
307            hours_without_data = []
308            for hours_without_data_item_data in _hours_without_data:
309                hours_without_data_item = PrepareJobStateHoursWithoutDataItem.from_dict(hours_without_data_item_data)
310
311                hours_without_data.append(hours_without_data_item)
312
313        prepare_job_state = cls(
314            context_id=context_id,
315            status=status,
316            size=size,
317            completed=completed,
318            status_detail=status_detail,
319            start_time=start_time,
320            end_time=end_time,
321            data_from=data_from,
322            data_to=data_to,
323            coverage_ratio=coverage_ratio,
324            total_hours=total_hours,
325            hours_with_data=hours_with_data,
326            cadence=cadence,
327            gaps=gaps,
328            largest_gap_steps=largest_gap_steps,
329            hours_without_data=hours_without_data,
330        )
331
332        prepare_job_state.additional_properties = d
333        return prepare_job_state
additional_keys: list[str]
335    @property
336    def additional_keys(self) -> list[str]:
337        return list(self.additional_properties.keys())
class PrepareJobStateHoursWithoutDataItem:
 18@_attrs_define
 19class PrepareJobStateHoursWithoutDataItem:
 20    """
 21    Attributes:
 22        hour (datetime.datetime | Unset): The hour (UTC, hour-aligned) that has no data. Example: 2026-04-14T02:00:00Z.
 23        expected (int | Unset): Expected row count for the hour (currently always 0; reserved for
 24            future use). The rationale never depends on it.
 25        rationale (PrepareJobStateHoursWithoutDataItemRationale | Unset): Why the hour has no data.
 26            `pending_conversion`: data for this hour is
 27            still being produced — a re-poll may fill it. `low_activity`: the
 28            instrument did not trade that hour. `unknown`: no data to classify by.
 29             Example: low_activity.
 30    """
 31
 32    hour: datetime.datetime | Unset = UNSET
 33    expected: int | Unset = UNSET
 34    rationale: PrepareJobStateHoursWithoutDataItemRationale | Unset = UNSET
 35    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 36
 37    def to_dict(self) -> dict[str, Any]:
 38        hour: str | Unset = UNSET
 39        if not isinstance(self.hour, Unset):
 40            hour = self.hour.isoformat()
 41
 42        expected = self.expected
 43
 44        rationale: str | Unset = UNSET
 45        if not isinstance(self.rationale, Unset):
 46            rationale = self.rationale.value
 47
 48        field_dict: dict[str, Any] = {}
 49        field_dict.update(self.additional_properties)
 50        field_dict.update({})
 51        if hour is not UNSET:
 52            field_dict["hour"] = hour
 53        if expected is not UNSET:
 54            field_dict["expected"] = expected
 55        if rationale is not UNSET:
 56            field_dict["rationale"] = rationale
 57
 58        return field_dict
 59
 60    @classmethod
 61    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 62        d = dict(src_dict)
 63        _hour = d.pop("hour", UNSET)
 64        hour: datetime.datetime | Unset
 65        if isinstance(_hour, Unset):
 66            hour = UNSET
 67        else:
 68            hour = isoparse(_hour)
 69
 70        expected = d.pop("expected", UNSET)
 71
 72        _rationale = d.pop("rationale", UNSET)
 73        rationale: PrepareJobStateHoursWithoutDataItemRationale | Unset
 74        if isinstance(_rationale, Unset):
 75            rationale = UNSET
 76        else:
 77            rationale = PrepareJobStateHoursWithoutDataItemRationale(_rationale)
 78
 79        prepare_job_state_hours_without_data_item = cls(
 80            hour=hour,
 81            expected=expected,
 82            rationale=rationale,
 83        )
 84
 85        prepare_job_state_hours_without_data_item.additional_properties = d
 86        return prepare_job_state_hours_without_data_item
 87
 88    @property
 89    def additional_keys(self) -> list[str]:
 90        return list(self.additional_properties.keys())
 91
 92    def __getitem__(self, key: str) -> Any:
 93        return self.additional_properties[key]
 94
 95    def __setitem__(self, key: str, value: Any) -> None:
 96        self.additional_properties[key] = value
 97
 98    def __delitem__(self, key: str) -> None:
 99        del self.additional_properties[key]
100
101    def __contains__(self, key: str) -> bool:
102        return key in self.additional_properties

Attributes: hour (datetime.datetime | Unset): The hour (UTC, hour-aligned) that has no data. Example: 2026-04-14T02:00:00Z. expected (int | Unset): Expected row count for the hour (currently always 0; reserved for future use). The rationale never depends on it. rationale (PrepareJobStateHoursWithoutDataItemRationale | Unset): Why the hour has no data. pending_conversion: data for this hour is still being produced — a re-poll may fill it. low_activity: the instrument did not trade that hour. unknown: no data to classify by. Example: low_activity.

PrepareJobStateHoursWithoutDataItem( hour: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, expected: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, rationale: PrepareJobStateHoursWithoutDataItemRationale | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
26def __init__(self, hour=attr_dict['hour'].default, expected=attr_dict['expected'].default, rationale=attr_dict['rationale'].default):
27    self.hour = hour
28    self.expected = expected
29    self.rationale = rationale
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class PrepareJobStateHoursWithoutDataItem.

hour: datetime.datetime | qtsurfer.api.client._generated.types.Unset
expected: int | qtsurfer.api.client._generated.types.Unset
rationale: PrepareJobStateHoursWithoutDataItemRationale | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
37    def to_dict(self) -> dict[str, Any]:
38        hour: str | Unset = UNSET
39        if not isinstance(self.hour, Unset):
40            hour = self.hour.isoformat()
41
42        expected = self.expected
43
44        rationale: str | Unset = UNSET
45        if not isinstance(self.rationale, Unset):
46            rationale = self.rationale.value
47
48        field_dict: dict[str, Any] = {}
49        field_dict.update(self.additional_properties)
50        field_dict.update({})
51        if hour is not UNSET:
52            field_dict["hour"] = hour
53        if expected is not UNSET:
54            field_dict["expected"] = expected
55        if rationale is not UNSET:
56            field_dict["rationale"] = rationale
57
58        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
60    @classmethod
61    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
62        d = dict(src_dict)
63        _hour = d.pop("hour", UNSET)
64        hour: datetime.datetime | Unset
65        if isinstance(_hour, Unset):
66            hour = UNSET
67        else:
68            hour = isoparse(_hour)
69
70        expected = d.pop("expected", UNSET)
71
72        _rationale = d.pop("rationale", UNSET)
73        rationale: PrepareJobStateHoursWithoutDataItemRationale | Unset
74        if isinstance(_rationale, Unset):
75            rationale = UNSET
76        else:
77            rationale = PrepareJobStateHoursWithoutDataItemRationale(_rationale)
78
79        prepare_job_state_hours_without_data_item = cls(
80            hour=hour,
81            expected=expected,
82            rationale=rationale,
83        )
84
85        prepare_job_state_hours_without_data_item.additional_properties = d
86        return prepare_job_state_hours_without_data_item
additional_keys: list[str]
88    @property
89    def additional_keys(self) -> list[str]:
90        return list(self.additional_properties.keys())
class PrepareJobStateHoursWithoutDataItemRationale(builtins.str, enum.Enum):
 5class PrepareJobStateHoursWithoutDataItemRationale(str, Enum):
 6    LOW_ACTIVITY = "low_activity"
 7    PENDING_CONVERSION = "pending_conversion"
 8    UNKNOWN = "unknown"
 9
10    def __str__(self) -> str:
11        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

PENDING_CONVERSION = <PrepareJobStateHoursWithoutDataItemRationale.PENDING_CONVERSION: 'pending_conversion'>
class PrepareRequest:
 16@_attrs_define
 17class PrepareRequest:
 18    """Two shapes, chosen by the `exchangeId` path segment. Against a managed exchange,
 19    `instrument` is required and `datasetId`/`datasetVersionId` are ignored. Against the
 20    reserved `exchangeId: user`, send `datasetId` instead of `instrument` — `instrument` is
 21    ignored there, since it comes from the dataset itself.
 22
 23        Example:
 24            {'instrument': 'BTC/USDT', 'from': '2024-12-13T00:00:00Z', 'to': '2024-12-14T00:00:00Z', 'cadence': '1m'}
 25
 26        Attributes:
 27            from_ (str): Start date for the preparation process. Supports the following formats:
 28                - ISO-8601 (e.g. 2024-12-14T23:59:59Z)
 29                - ISO DATE (e.g. 2024-12-14)
 30                - BASIC ISO DATE (e.g., 20241214)
 31                 Example: 2024-12-13T00:00:00Z.
 32            to (str): End date for the preparation process. Supports the following formats:
 33                - ISO-8601 (e.g. 2024-12-14T23:59:59Z)
 34                - ISO DATE (e.g. 2024-12-14)
 35                - BASIC ISO DATE (e.g., 20241214)
 36                 Example: 2024-12-14.
 37            instrument (str | Unset): Exchange instrument identifier (e.g. a currency pair) Example: BTC/USDT.
 38            dataset_id (str | Unset): Only for `exchangeId: user`: the id of a dataset created via `POST /datasets`, in
 39                place
 40                of `instrument`. Ignored against a managed exchange.
 41                 Example: ds_3f9a1c2e7b0d4a5f.
 42            dataset_version_id (str | Unset): Only for `exchangeId: user`, and optional even then: pins a specific past
 43                version of
 44                the dataset instead of its current one. Defaults to the dataset's current version.
 45                 Example: dsv_8e2b4f19c6a03d7e.
 46            cadence (PrepareRequestCadence | Unset): Output bar cadence for the prepared range. Defaults to the publisher's
 47                native cadence (`1s`); coarser cadences are produced on demand via
 48                resampling and stored alongside the native blob in cache. Coarser-than-
 49                source values must be exact multiples of the source cadence — invalid
 50                labels return `400`.
 51                 Default: PrepareRequestCadence.VALUE_0.
 52    """
 53
 54    from_: str
 55    to: str
 56    instrument: str | Unset = UNSET
 57    dataset_id: str | Unset = UNSET
 58    dataset_version_id: str | Unset = UNSET
 59    cadence: PrepareRequestCadence | Unset = PrepareRequestCadence.VALUE_0
 60    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 61
 62    def to_dict(self) -> dict[str, Any]:
 63        from_ = self.from_
 64
 65        to = self.to
 66
 67        instrument = self.instrument
 68
 69        dataset_id = self.dataset_id
 70
 71        dataset_version_id = self.dataset_version_id
 72
 73        cadence: str | Unset = UNSET
 74        if not isinstance(self.cadence, Unset):
 75            cadence = self.cadence.value
 76
 77        field_dict: dict[str, Any] = {}
 78        field_dict.update(self.additional_properties)
 79        field_dict.update(
 80            {
 81                "from": from_,
 82                "to": to,
 83            }
 84        )
 85        if instrument is not UNSET:
 86            field_dict["instrument"] = instrument
 87        if dataset_id is not UNSET:
 88            field_dict["datasetId"] = dataset_id
 89        if dataset_version_id is not UNSET:
 90            field_dict["datasetVersionId"] = dataset_version_id
 91        if cadence is not UNSET:
 92            field_dict["cadence"] = cadence
 93
 94        return field_dict
 95
 96    @classmethod
 97    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 98        d = dict(src_dict)
 99        from_ = d.pop("from")
100
101        to = d.pop("to")
102
103        instrument = d.pop("instrument", UNSET)
104
105        dataset_id = d.pop("datasetId", UNSET)
106
107        dataset_version_id = d.pop("datasetVersionId", UNSET)
108
109        _cadence = d.pop("cadence", UNSET)
110        cadence: PrepareRequestCadence | Unset
111        if isinstance(_cadence, Unset):
112            cadence = UNSET
113        else:
114            cadence = PrepareRequestCadence(_cadence)
115
116        prepare_request = cls(
117            from_=from_,
118            to=to,
119            instrument=instrument,
120            dataset_id=dataset_id,
121            dataset_version_id=dataset_version_id,
122            cadence=cadence,
123        )
124
125        prepare_request.additional_properties = d
126        return prepare_request
127
128    @property
129    def additional_keys(self) -> list[str]:
130        return list(self.additional_properties.keys())
131
132    def __getitem__(self, key: str) -> Any:
133        return self.additional_properties[key]
134
135    def __setitem__(self, key: str, value: Any) -> None:
136        self.additional_properties[key] = value
137
138    def __delitem__(self, key: str) -> None:
139        del self.additional_properties[key]
140
141    def __contains__(self, key: str) -> bool:
142        return key in self.additional_properties

Two shapes, chosen by the exchangeId path segment. Against a managed exchange, instrument is required and datasetId/datasetVersionId are ignored. Against the reserved exchangeId: user, send datasetId instead of instrumentinstrument is ignored there, since it comes from the dataset itself.

Example:
    {'instrument': 'BTC/USDT', 'from': '2024-12-13T00:00:00Z', 'to': '2024-12-14T00:00:00Z', 'cadence': '1m'}

Attributes:
    from_ (str): Start date for the preparation process. Supports the following formats:
        - ISO-8601 (e.g. 2024-12-14T23:59:59Z)
        - ISO DATE (e.g. 2024-12-14)
        - BASIC ISO DATE (e.g., 20241214)
         Example: 2024-12-13T00:00:00Z.
    to (str): End date for the preparation process. Supports the following formats:
        - ISO-8601 (e.g. 2024-12-14T23:59:59Z)
        - ISO DATE (e.g. 2024-12-14)
        - BASIC ISO DATE (e.g., 20241214)
         Example: 2024-12-14.
    instrument (str | Unset): Exchange instrument identifier (e.g. a currency pair) Example: BTC/USDT.
    dataset_id (str | Unset): Only for `exchangeId: user`: the id of a dataset created via `POST /datasets`, in
        place
        of `instrument`. Ignored against a managed exchange.
         Example: ds_3f9a1c2e7b0d4a5f.
    dataset_version_id (str | Unset): Only for `exchangeId: user`, and optional even then: pins a specific past
        version of
        the dataset instead of its current one. Defaults to the dataset's current version.
         Example: dsv_8e2b4f19c6a03d7e.
    cadence (PrepareRequestCadence | Unset): Output bar cadence for the prepared range. Defaults to the publisher's
        native cadence (`1s`); coarser cadences are produced on demand via
        resampling and stored alongside the native blob in cache. Coarser-than-
        source values must be exact multiples of the source cadence — invalid
        labels return `400`.
         Default: PrepareRequestCadence.VALUE_0.
PrepareRequest( from_: str, to: str, instrument: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, dataset_id: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, dataset_version_id: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, cadence: PrepareRequestCadence | qtsurfer.api.client._generated.types.Unset = <PrepareRequestCadence.VALUE_0: '1s'>)
29def __init__(self, from_, to, instrument=attr_dict['instrument'].default, dataset_id=attr_dict['dataset_id'].default, dataset_version_id=attr_dict['dataset_version_id'].default, cadence=attr_dict['cadence'].default):
30    self.from_ = from_
31    self.to = to
32    self.instrument = instrument
33    self.dataset_id = dataset_id
34    self.dataset_version_id = dataset_version_id
35    self.cadence = cadence
36    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class PrepareRequest.

from_: str
to: str
instrument: str | qtsurfer.api.client._generated.types.Unset
dataset_id: str | qtsurfer.api.client._generated.types.Unset
dataset_version_id: str | qtsurfer.api.client._generated.types.Unset
cadence: PrepareRequestCadence | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
62    def to_dict(self) -> dict[str, Any]:
63        from_ = self.from_
64
65        to = self.to
66
67        instrument = self.instrument
68
69        dataset_id = self.dataset_id
70
71        dataset_version_id = self.dataset_version_id
72
73        cadence: str | Unset = UNSET
74        if not isinstance(self.cadence, Unset):
75            cadence = self.cadence.value
76
77        field_dict: dict[str, Any] = {}
78        field_dict.update(self.additional_properties)
79        field_dict.update(
80            {
81                "from": from_,
82                "to": to,
83            }
84        )
85        if instrument is not UNSET:
86            field_dict["instrument"] = instrument
87        if dataset_id is not UNSET:
88            field_dict["datasetId"] = dataset_id
89        if dataset_version_id is not UNSET:
90            field_dict["datasetVersionId"] = dataset_version_id
91        if cadence is not UNSET:
92            field_dict["cadence"] = cadence
93
94        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 96    @classmethod
 97    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 98        d = dict(src_dict)
 99        from_ = d.pop("from")
100
101        to = d.pop("to")
102
103        instrument = d.pop("instrument", UNSET)
104
105        dataset_id = d.pop("datasetId", UNSET)
106
107        dataset_version_id = d.pop("datasetVersionId", UNSET)
108
109        _cadence = d.pop("cadence", UNSET)
110        cadence: PrepareRequestCadence | Unset
111        if isinstance(_cadence, Unset):
112            cadence = UNSET
113        else:
114            cadence = PrepareRequestCadence(_cadence)
115
116        prepare_request = cls(
117            from_=from_,
118            to=to,
119            instrument=instrument,
120            dataset_id=dataset_id,
121            dataset_version_id=dataset_version_id,
122            cadence=cadence,
123        )
124
125        prepare_request.additional_properties = d
126        return prepare_request
additional_keys: list[str]
128    @property
129    def additional_keys(self) -> list[str]:
130        return list(self.additional_properties.keys())
class PrepareRequestCadence(builtins.str, enum.Enum):
 5class PrepareRequestCadence(str, Enum):
 6    VALUE_0 = "1s"
 7    VALUE_1 = "5s"
 8    VALUE_10 = "8h"
 9    VALUE_11 = "12h"
10    VALUE_12 = "1d"
11    VALUE_13 = "1w"
12    VALUE_14 = "1q"
13    VALUE_2 = "1m"
14    VALUE_3 = "3m"
15    VALUE_4 = "5m"
16    VALUE_5 = "15m"
17    VALUE_6 = "30m"
18    VALUE_7 = "1h"
19    VALUE_8 = "2h"
20    VALUE_9 = "4h"
21
22    def __str__(self) -> str:
23        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

VALUE_0 = <PrepareRequestCadence.VALUE_0: '1s'>
VALUE_1 = <PrepareRequestCadence.VALUE_1: '5s'>
VALUE_10 = <PrepareRequestCadence.VALUE_10: '8h'>
VALUE_11 = <PrepareRequestCadence.VALUE_11: '12h'>
VALUE_12 = <PrepareRequestCadence.VALUE_12: '1d'>
VALUE_13 = <PrepareRequestCadence.VALUE_13: '1w'>
VALUE_14 = <PrepareRequestCadence.VALUE_14: '1q'>
VALUE_2 = <PrepareRequestCadence.VALUE_2: '1m'>
VALUE_3 = <PrepareRequestCadence.VALUE_3: '3m'>
VALUE_4 = <PrepareRequestCadence.VALUE_4: '5m'>
VALUE_5 = <PrepareRequestCadence.VALUE_5: '15m'>
VALUE_6 = <PrepareRequestCadence.VALUE_6: '30m'>
VALUE_7 = <PrepareRequestCadence.VALUE_7: '1h'>
VALUE_8 = <PrepareRequestCadence.VALUE_8: '2h'>
VALUE_9 = <PrepareRequestCadence.VALUE_9: '4h'>
class ResponseError:
13@_attrs_define
14class ResponseError:
15    """General response error
16
17    Attributes:
18        code (int): Status code Example: 400.
19        message (str): Error description Example: Invalid request.
20    """
21
22    code: int
23    message: str
24    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
25
26    def to_dict(self) -> dict[str, Any]:
27        code = self.code
28
29        message = self.message
30
31        field_dict: dict[str, Any] = {}
32        field_dict.update(self.additional_properties)
33        field_dict.update(
34            {
35                "code": code,
36                "message": message,
37            }
38        )
39
40        return field_dict
41
42    @classmethod
43    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
44        d = dict(src_dict)
45        code = d.pop("code")
46
47        message = d.pop("message")
48
49        response_error = cls(
50            code=code,
51            message=message,
52        )
53
54        response_error.additional_properties = d
55        return response_error
56
57    @property
58    def additional_keys(self) -> list[str]:
59        return list(self.additional_properties.keys())
60
61    def __getitem__(self, key: str) -> Any:
62        return self.additional_properties[key]
63
64    def __setitem__(self, key: str, value: Any) -> None:
65        self.additional_properties[key] = value
66
67    def __delitem__(self, key: str) -> None:
68        del self.additional_properties[key]
69
70    def __contains__(self, key: str) -> bool:
71        return key in self.additional_properties

General response error

Attributes: code (int): Status code Example: 400. message (str): Error description Example: Invalid request.

ResponseError(code: int, message: str)
25def __init__(self, code, message):
26    self.code = code
27    self.message = message
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ResponseError.

code: int
message: str
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
26    def to_dict(self) -> dict[str, Any]:
27        code = self.code
28
29        message = self.message
30
31        field_dict: dict[str, Any] = {}
32        field_dict.update(self.additional_properties)
33        field_dict.update(
34            {
35                "code": code,
36                "message": message,
37            }
38        )
39
40        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
42    @classmethod
43    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
44        d = dict(src_dict)
45        code = d.pop("code")
46
47        message = d.pop("message")
48
49        response_error = cls(
50            code=code,
51            message=message,
52        )
53
54        response_error.additional_properties = d
55        return response_error
additional_keys: list[str]
57    @property
58    def additional_keys(self) -> list[str]:
59        return list(self.additional_properties.keys())
class ResultMap:
 23@_attrs_define
 24class ResultMap:
 25    """Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal,
 26    pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade.
 27    When signal storage is enabled, includes signal fields described below. `notices` carries what the run had to say
 28    about itself, and is absent when it had nothing.
 29
 30        Attributes:
 31            strategy_id (str): **Not the `strategyId` you compiled with** — this is the execution context id,
 32                `strategy:<user>:<strategyId>`. The compiled strategy's id is the last `:`-separated
 33                segment; that, not this whole string, is what `GET /strategy/{strategyId}` takes.
 34
 35                Take the segment after the last `:` rather than counting from the front: the shape has
 36                changed once already and callers that indexed a fixed position broke on it.
 37                 Example: strategy:00000000-0000-0000-0000-000000000000:2iyvtenlzh9dabqtxn7nbv.
 38            instrument (str): The instrument (currency pair) that was backtested Example: BTC/USDT.
 39            host_name (str | Unset): Identifier of the worker that executed the strategy. Useful when reporting issues so
 40                support can correlate with logs. Example: executor10.
 41            iops (float | Unset): Instrument operations per second throughput during execution Example: 123956.53.
 42            notices (list[Notice] | Unset): Diagnostics the engine raised over this run, each with `provenance: execute`.
 43
 44                **Absent means nothing was raised.** This is the one surface where silence is a real
 45                answer: the run happened, over your data, start to finish, and the engine found nothing
 46                worth saying. That is not true of the compile path, where an empty list only means a
 47                short synthetic series reached nothing — see `GET /strategy/{strategyId}`.
 48
 49                Notices are raised on failed and aborted runs too, and those are the ones most worth
 50                reading: a run that produced no trades often did so for a reason stated here.
 51            notices_truncated (int | Unset): How many notices were dropped past the cap of 50. Absent when none were. A
 52                large value usually means one fault repeating per instrument or per parameter vector rather than 50 distinct
 53                problems. Example: 3.
 54            pnl_total (float | Unset): Total profit and loss in the output currency Example: 42.75.
 55            pnl_total_percent (float | Unset): Total PnL as a percentage of the initial capital (`backtestFunding`). Zero
 56                when `backtestFunding` is 0. Example: 42.75.
 57            total_trades (int | Unset): Total number of trades executed by the strategy Example: 156.
 58            win_rate (float | Unset): Percentage of profitable trades (0-100) Example: 58.33.
 59            sharpe_ratio (float | Unset): Risk-adjusted return ratio (mean return / standard deviation of returns) Example:
 60                1.245.
 61            sortino_ratio (float | Unset): Downside risk-adjusted return ratio (mean return / downside deviation) Example:
 62                1.872.
 63            cagr (float | Unset): Compound Annual Growth Rate Example: 0.1534.
 64            max_drawdown (float | Unset): Maximum absolute drawdown in the output currency Example: 12.5.
 65            max_drawdown_percent (float | Unset): Maximum percentage drawdown from peak equity Example: 8.75.
 66            equity_curve (EquityCurveResult | Unset): An equity curve, shaped per `meta.outMode`: `points` when `ARRAY`,
 67                `timestamps` + `equities` (parallel arrays) when `SHORT`. Used identically wherever a curve is returned — a
 68                plain backtest's inline `equityCurve` and a sweep row's `equityCurve` are the same type. `url` is present
 69                *instead of* any points when the curve is served by pointer rather than inline (a sweep row's top-N winners
 70                only): `GET` it separately to fetch this exact same shape with the points populated.
 71            signal_count (int | Unset): Number of signals emitted during strategy execution Example: 100000.
 72            signals_id (str | Unset): Storage key for the signals file. Treat as opaque; use signalsUrl to download.
 73                Example: 00000000-0000-0000-0000-000000000000/exec/binance/3vsndwikcuaatjmb83fjtl.
 74            signals_url (str | Unset): HTTPS URL to download the signals Parquet file. Use signalsUpload to know when it's
 75                ready. Example:
 76                https://storage.qtsurfer.com/00000000-0000-0000-0000-000000000000/exec/binance/3vsndwikcuaatjmb83fjtl.parquet.
 77            signals_upload (ResultMapSignalsUpload | Unset): Upload status. Done = signal file is available at signalsUrl.
 78                Failed = upload error (see signalsUploadReason). Skipped = no signals emitted. Example: Done.
 79            signals_uploaded_at (datetime.datetime | Unset): ISO 8601 timestamp of when the upload completed. Only present
 80                when signalsUpload is Done. Example: 2026-03-18T13:21:48.170Z.
 81            signals_upload_reason (str | Unset): Human-readable reason when signalsUpload is Failed or Skipped. Example:
 82                signal file generation failed.
 83    """
 84
 85    strategy_id: str
 86    instrument: str
 87    host_name: str | Unset = UNSET
 88    iops: float | Unset = UNSET
 89    notices: list[Notice] | Unset = UNSET
 90    notices_truncated: int | Unset = UNSET
 91    pnl_total: float | Unset = UNSET
 92    pnl_total_percent: float | Unset = UNSET
 93    total_trades: int | Unset = UNSET
 94    win_rate: float | Unset = UNSET
 95    sharpe_ratio: float | Unset = UNSET
 96    sortino_ratio: float | Unset = UNSET
 97    cagr: float | Unset = UNSET
 98    max_drawdown: float | Unset = UNSET
 99    max_drawdown_percent: float | Unset = UNSET
100    equity_curve: EquityCurveResult | Unset = UNSET
101    signal_count: int | Unset = UNSET
102    signals_id: str | Unset = UNSET
103    signals_url: str | Unset = UNSET
104    signals_upload: ResultMapSignalsUpload | Unset = UNSET
105    signals_uploaded_at: datetime.datetime | Unset = UNSET
106    signals_upload_reason: str | Unset = UNSET
107    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
108
109    def to_dict(self) -> dict[str, Any]:
110        strategy_id = self.strategy_id
111
112        instrument = self.instrument
113
114        host_name = self.host_name
115
116        iops = self.iops
117
118        notices: list[dict[str, Any]] | Unset = UNSET
119        if not isinstance(self.notices, Unset):
120            notices = []
121            for notices_item_data in self.notices:
122                notices_item = notices_item_data.to_dict()
123                notices.append(notices_item)
124
125        notices_truncated = self.notices_truncated
126
127        pnl_total = self.pnl_total
128
129        pnl_total_percent = self.pnl_total_percent
130
131        total_trades = self.total_trades
132
133        win_rate = self.win_rate
134
135        sharpe_ratio = self.sharpe_ratio
136
137        sortino_ratio = self.sortino_ratio
138
139        cagr = self.cagr
140
141        max_drawdown = self.max_drawdown
142
143        max_drawdown_percent = self.max_drawdown_percent
144
145        equity_curve: dict[str, Any] | Unset = UNSET
146        if not isinstance(self.equity_curve, Unset):
147            equity_curve = self.equity_curve.to_dict()
148
149        signal_count = self.signal_count
150
151        signals_id = self.signals_id
152
153        signals_url = self.signals_url
154
155        signals_upload: str | Unset = UNSET
156        if not isinstance(self.signals_upload, Unset):
157            signals_upload = self.signals_upload.value
158
159        signals_uploaded_at: str | Unset = UNSET
160        if not isinstance(self.signals_uploaded_at, Unset):
161            signals_uploaded_at = self.signals_uploaded_at.isoformat()
162
163        signals_upload_reason = self.signals_upload_reason
164
165        field_dict: dict[str, Any] = {}
166        field_dict.update(self.additional_properties)
167        field_dict.update(
168            {
169                "strategyId": strategy_id,
170                "instrument": instrument,
171            }
172        )
173        if host_name is not UNSET:
174            field_dict["hostName"] = host_name
175        if iops is not UNSET:
176            field_dict["iops"] = iops
177        if notices is not UNSET:
178            field_dict["notices"] = notices
179        if notices_truncated is not UNSET:
180            field_dict["noticesTruncated"] = notices_truncated
181        if pnl_total is not UNSET:
182            field_dict["pnlTotal"] = pnl_total
183        if pnl_total_percent is not UNSET:
184            field_dict["pnlTotalPercent"] = pnl_total_percent
185        if total_trades is not UNSET:
186            field_dict["totalTrades"] = total_trades
187        if win_rate is not UNSET:
188            field_dict["winRate"] = win_rate
189        if sharpe_ratio is not UNSET:
190            field_dict["sharpeRatio"] = sharpe_ratio
191        if sortino_ratio is not UNSET:
192            field_dict["sortinoRatio"] = sortino_ratio
193        if cagr is not UNSET:
194            field_dict["cagr"] = cagr
195        if max_drawdown is not UNSET:
196            field_dict["maxDrawdown"] = max_drawdown
197        if max_drawdown_percent is not UNSET:
198            field_dict["maxDrawdownPercent"] = max_drawdown_percent
199        if equity_curve is not UNSET:
200            field_dict["equityCurve"] = equity_curve
201        if signal_count is not UNSET:
202            field_dict["signalCount"] = signal_count
203        if signals_id is not UNSET:
204            field_dict["signalsId"] = signals_id
205        if signals_url is not UNSET:
206            field_dict["signalsUrl"] = signals_url
207        if signals_upload is not UNSET:
208            field_dict["signalsUpload"] = signals_upload
209        if signals_uploaded_at is not UNSET:
210            field_dict["signalsUploadedAt"] = signals_uploaded_at
211        if signals_upload_reason is not UNSET:
212            field_dict["signalsUploadReason"] = signals_upload_reason
213
214        return field_dict
215
216    @classmethod
217    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
218        from ..models.equity_curve_result import EquityCurveResult
219        from ..models.notice import Notice
220
221        d = dict(src_dict)
222        strategy_id = d.pop("strategyId")
223
224        instrument = d.pop("instrument")
225
226        host_name = d.pop("hostName", UNSET)
227
228        iops = d.pop("iops", UNSET)
229
230        _notices = d.pop("notices", UNSET)
231        notices: list[Notice] | Unset = UNSET
232        if _notices is not UNSET:
233            notices = []
234            for notices_item_data in _notices:
235                notices_item = Notice.from_dict(notices_item_data)
236
237                notices.append(notices_item)
238
239        notices_truncated = d.pop("noticesTruncated", UNSET)
240
241        pnl_total = d.pop("pnlTotal", UNSET)
242
243        pnl_total_percent = d.pop("pnlTotalPercent", UNSET)
244
245        total_trades = d.pop("totalTrades", UNSET)
246
247        win_rate = d.pop("winRate", UNSET)
248
249        sharpe_ratio = d.pop("sharpeRatio", UNSET)
250
251        sortino_ratio = d.pop("sortinoRatio", UNSET)
252
253        cagr = d.pop("cagr", UNSET)
254
255        max_drawdown = d.pop("maxDrawdown", UNSET)
256
257        max_drawdown_percent = d.pop("maxDrawdownPercent", UNSET)
258
259        _equity_curve = d.pop("equityCurve", UNSET)
260        equity_curve: EquityCurveResult | Unset
261        if isinstance(_equity_curve, Unset):
262            equity_curve = UNSET
263        else:
264            equity_curve = EquityCurveResult.from_dict(_equity_curve)
265
266        signal_count = d.pop("signalCount", UNSET)
267
268        signals_id = d.pop("signalsId", UNSET)
269
270        signals_url = d.pop("signalsUrl", UNSET)
271
272        _signals_upload = d.pop("signalsUpload", UNSET)
273        signals_upload: ResultMapSignalsUpload | Unset
274        if isinstance(_signals_upload, Unset):
275            signals_upload = UNSET
276        else:
277            signals_upload = ResultMapSignalsUpload(_signals_upload)
278
279        _signals_uploaded_at = d.pop("signalsUploadedAt", UNSET)
280        signals_uploaded_at: datetime.datetime | Unset
281        if isinstance(_signals_uploaded_at, Unset):
282            signals_uploaded_at = UNSET
283        else:
284            signals_uploaded_at = isoparse(_signals_uploaded_at)
285
286        signals_upload_reason = d.pop("signalsUploadReason", UNSET)
287
288        result_map = cls(
289            strategy_id=strategy_id,
290            instrument=instrument,
291            host_name=host_name,
292            iops=iops,
293            notices=notices,
294            notices_truncated=notices_truncated,
295            pnl_total=pnl_total,
296            pnl_total_percent=pnl_total_percent,
297            total_trades=total_trades,
298            win_rate=win_rate,
299            sharpe_ratio=sharpe_ratio,
300            sortino_ratio=sortino_ratio,
301            cagr=cagr,
302            max_drawdown=max_drawdown,
303            max_drawdown_percent=max_drawdown_percent,
304            equity_curve=equity_curve,
305            signal_count=signal_count,
306            signals_id=signals_id,
307            signals_url=signals_url,
308            signals_upload=signals_upload,
309            signals_uploaded_at=signals_uploaded_at,
310            signals_upload_reason=signals_upload_reason,
311        )
312
313        result_map.additional_properties = d
314        return result_map
315
316    @property
317    def additional_keys(self) -> list[str]:
318        return list(self.additional_properties.keys())
319
320    def __getitem__(self, key: str) -> Any:
321        return self.additional_properties[key]
322
323    def __setitem__(self, key: str, value: Any) -> None:
324        self.additional_properties[key] = value
325
326    def __delitem__(self, key: str) -> None:
327        del self.additional_properties[key]
328
329    def __contains__(self, key: str) -> bool:
330        return key in self.additional_properties

Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade. When signal storage is enabled, includes signal fields described below. notices carries what the run had to say about itself, and is absent when it had nothing.

Attributes:
    strategy_id (str): **Not the `strategyId` you compiled with** — this is the execution context id,
        `strategy:<user>:<strategyId>`. The compiled strategy's id is the last `:`-separated
        segment; that, not this whole string, is what `GET /strategy/{strategyId}` takes.

        Take the segment after the last `:` rather than counting from the front: the shape has
        changed once already and callers that indexed a fixed position broke on it.
         Example: strategy:00000000-0000-0000-0000-000000000000:2iyvtenlzh9dabqtxn7nbv.
    instrument (str): The instrument (currency pair) that was backtested Example: BTC/USDT.
    host_name (str | Unset): Identifier of the worker that executed the strategy. Useful when reporting issues so
        support can correlate with logs. Example: executor10.
    iops (float | Unset): Instrument operations per second throughput during execution Example: 123956.53.
    notices (list[Notice] | Unset): Diagnostics the engine raised over this run, each with `provenance: execute`.

        **Absent means nothing was raised.** This is the one surface where silence is a real
        answer: the run happened, over your data, start to finish, and the engine found nothing
        worth saying. That is not true of the compile path, where an empty list only means a
        short synthetic series reached nothing — see `GET /strategy/{strategyId}`.

        Notices are raised on failed and aborted runs too, and those are the ones most worth
        reading: a run that produced no trades often did so for a reason stated here.
    notices_truncated (int | Unset): How many notices were dropped past the cap of 50. Absent when none were. A
        large value usually means one fault repeating per instrument or per parameter vector rather than 50 distinct
        problems. Example: 3.
    pnl_total (float | Unset): Total profit and loss in the output currency Example: 42.75.
    pnl_total_percent (float | Unset): Total PnL as a percentage of the initial capital (`backtestFunding`). Zero
        when `backtestFunding` is 0. Example: 42.75.
    total_trades (int | Unset): Total number of trades executed by the strategy Example: 156.
    win_rate (float | Unset): Percentage of profitable trades (0-100) Example: 58.33.
    sharpe_ratio (float | Unset): Risk-adjusted return ratio (mean return / standard deviation of returns) Example:
        1.245.
    sortino_ratio (float | Unset): Downside risk-adjusted return ratio (mean return / downside deviation) Example:
        1.872.
    cagr (float | Unset): Compound Annual Growth Rate Example: 0.1534.
    max_drawdown (float | Unset): Maximum absolute drawdown in the output currency Example: 12.5.
    max_drawdown_percent (float | Unset): Maximum percentage drawdown from peak equity Example: 8.75.
    equity_curve (EquityCurveResult | Unset): An equity curve, shaped per `meta.outMode`: `points` when `ARRAY`,
        `timestamps` + `equities` (parallel arrays) when `SHORT`. Used identically wherever a curve is returned — a
        plain backtest's inline `equityCurve` and a sweep row's `equityCurve` are the same type. `url` is present
        *instead of* any points when the curve is served by pointer rather than inline (a sweep row's top-N winners
        only): `GET` it separately to fetch this exact same shape with the points populated.
    signal_count (int | Unset): Number of signals emitted during strategy execution Example: 100000.
    signals_id (str | Unset): Storage key for the signals file. Treat as opaque; use signalsUrl to download.
        Example: 00000000-0000-0000-0000-000000000000/exec/binance/3vsndwikcuaatjmb83fjtl.
    signals_url (str | Unset): HTTPS URL to download the signals Parquet file. Use signalsUpload to know when it's
        ready. Example:
        https://storage.qtsurfer.com/00000000-0000-0000-0000-000000000000/exec/binance/3vsndwikcuaatjmb83fjtl.parquet.
    signals_upload (ResultMapSignalsUpload | Unset): Upload status. Done = signal file is available at signalsUrl.
        Failed = upload error (see signalsUploadReason). Skipped = no signals emitted. Example: Done.
    signals_uploaded_at (datetime.datetime | Unset): ISO 8601 timestamp of when the upload completed. Only present
        when signalsUpload is Done. Example: 2026-03-18T13:21:48.170Z.
    signals_upload_reason (str | Unset): Human-readable reason when signalsUpload is Failed or Skipped. Example:
        signal file generation failed.
ResultMap( strategy_id: str, instrument: str, host_name: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, iops: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, notices: list[Notice] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, notices_truncated: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, pnl_total: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, pnl_total_percent: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, total_trades: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, win_rate: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, sharpe_ratio: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, sortino_ratio: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, cagr: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, max_drawdown: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, max_drawdown_percent: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, equity_curve: EquityCurveResult | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, signal_count: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, signals_id: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, signals_url: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, signals_upload: ResultMapSignalsUpload | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, signals_uploaded_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, signals_upload_reason: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
45def __init__(self, strategy_id, instrument, host_name=attr_dict['host_name'].default, iops=attr_dict['iops'].default, notices=attr_dict['notices'].default, notices_truncated=attr_dict['notices_truncated'].default, pnl_total=attr_dict['pnl_total'].default, pnl_total_percent=attr_dict['pnl_total_percent'].default, total_trades=attr_dict['total_trades'].default, win_rate=attr_dict['win_rate'].default, sharpe_ratio=attr_dict['sharpe_ratio'].default, sortino_ratio=attr_dict['sortino_ratio'].default, cagr=attr_dict['cagr'].default, max_drawdown=attr_dict['max_drawdown'].default, max_drawdown_percent=attr_dict['max_drawdown_percent'].default, equity_curve=attr_dict['equity_curve'].default, signal_count=attr_dict['signal_count'].default, signals_id=attr_dict['signals_id'].default, signals_url=attr_dict['signals_url'].default, signals_upload=attr_dict['signals_upload'].default, signals_uploaded_at=attr_dict['signals_uploaded_at'].default, signals_upload_reason=attr_dict['signals_upload_reason'].default):
46    self.strategy_id = strategy_id
47    self.instrument = instrument
48    self.host_name = host_name
49    self.iops = iops
50    self.notices = notices
51    self.notices_truncated = notices_truncated
52    self.pnl_total = pnl_total
53    self.pnl_total_percent = pnl_total_percent
54    self.total_trades = total_trades
55    self.win_rate = win_rate
56    self.sharpe_ratio = sharpe_ratio
57    self.sortino_ratio = sortino_ratio
58    self.cagr = cagr
59    self.max_drawdown = max_drawdown
60    self.max_drawdown_percent = max_drawdown_percent
61    self.equity_curve = equity_curve
62    self.signal_count = signal_count
63    self.signals_id = signals_id
64    self.signals_url = signals_url
65    self.signals_upload = signals_upload
66    self.signals_uploaded_at = signals_uploaded_at
67    self.signals_upload_reason = signals_upload_reason
68    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ResultMap.

strategy_id: str
instrument: str
host_name: str | qtsurfer.api.client._generated.types.Unset
iops: float | qtsurfer.api.client._generated.types.Unset
notices: list[Notice] | qtsurfer.api.client._generated.types.Unset
notices_truncated: int | qtsurfer.api.client._generated.types.Unset
pnl_total: float | qtsurfer.api.client._generated.types.Unset
pnl_total_percent: float | qtsurfer.api.client._generated.types.Unset
total_trades: int | qtsurfer.api.client._generated.types.Unset
win_rate: float | qtsurfer.api.client._generated.types.Unset
sharpe_ratio: float | qtsurfer.api.client._generated.types.Unset
sortino_ratio: float | qtsurfer.api.client._generated.types.Unset
cagr: float | qtsurfer.api.client._generated.types.Unset
max_drawdown: float | qtsurfer.api.client._generated.types.Unset
max_drawdown_percent: float | qtsurfer.api.client._generated.types.Unset
equity_curve: EquityCurveResult | qtsurfer.api.client._generated.types.Unset
signal_count: int | qtsurfer.api.client._generated.types.Unset
signals_id: str | qtsurfer.api.client._generated.types.Unset
signals_url: str | qtsurfer.api.client._generated.types.Unset
signals_upload: ResultMapSignalsUpload | qtsurfer.api.client._generated.types.Unset
signals_uploaded_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset
signals_upload_reason: str | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
109    def to_dict(self) -> dict[str, Any]:
110        strategy_id = self.strategy_id
111
112        instrument = self.instrument
113
114        host_name = self.host_name
115
116        iops = self.iops
117
118        notices: list[dict[str, Any]] | Unset = UNSET
119        if not isinstance(self.notices, Unset):
120            notices = []
121            for notices_item_data in self.notices:
122                notices_item = notices_item_data.to_dict()
123                notices.append(notices_item)
124
125        notices_truncated = self.notices_truncated
126
127        pnl_total = self.pnl_total
128
129        pnl_total_percent = self.pnl_total_percent
130
131        total_trades = self.total_trades
132
133        win_rate = self.win_rate
134
135        sharpe_ratio = self.sharpe_ratio
136
137        sortino_ratio = self.sortino_ratio
138
139        cagr = self.cagr
140
141        max_drawdown = self.max_drawdown
142
143        max_drawdown_percent = self.max_drawdown_percent
144
145        equity_curve: dict[str, Any] | Unset = UNSET
146        if not isinstance(self.equity_curve, Unset):
147            equity_curve = self.equity_curve.to_dict()
148
149        signal_count = self.signal_count
150
151        signals_id = self.signals_id
152
153        signals_url = self.signals_url
154
155        signals_upload: str | Unset = UNSET
156        if not isinstance(self.signals_upload, Unset):
157            signals_upload = self.signals_upload.value
158
159        signals_uploaded_at: str | Unset = UNSET
160        if not isinstance(self.signals_uploaded_at, Unset):
161            signals_uploaded_at = self.signals_uploaded_at.isoformat()
162
163        signals_upload_reason = self.signals_upload_reason
164
165        field_dict: dict[str, Any] = {}
166        field_dict.update(self.additional_properties)
167        field_dict.update(
168            {
169                "strategyId": strategy_id,
170                "instrument": instrument,
171            }
172        )
173        if host_name is not UNSET:
174            field_dict["hostName"] = host_name
175        if iops is not UNSET:
176            field_dict["iops"] = iops
177        if notices is not UNSET:
178            field_dict["notices"] = notices
179        if notices_truncated is not UNSET:
180            field_dict["noticesTruncated"] = notices_truncated
181        if pnl_total is not UNSET:
182            field_dict["pnlTotal"] = pnl_total
183        if pnl_total_percent is not UNSET:
184            field_dict["pnlTotalPercent"] = pnl_total_percent
185        if total_trades is not UNSET:
186            field_dict["totalTrades"] = total_trades
187        if win_rate is not UNSET:
188            field_dict["winRate"] = win_rate
189        if sharpe_ratio is not UNSET:
190            field_dict["sharpeRatio"] = sharpe_ratio
191        if sortino_ratio is not UNSET:
192            field_dict["sortinoRatio"] = sortino_ratio
193        if cagr is not UNSET:
194            field_dict["cagr"] = cagr
195        if max_drawdown is not UNSET:
196            field_dict["maxDrawdown"] = max_drawdown
197        if max_drawdown_percent is not UNSET:
198            field_dict["maxDrawdownPercent"] = max_drawdown_percent
199        if equity_curve is not UNSET:
200            field_dict["equityCurve"] = equity_curve
201        if signal_count is not UNSET:
202            field_dict["signalCount"] = signal_count
203        if signals_id is not UNSET:
204            field_dict["signalsId"] = signals_id
205        if signals_url is not UNSET:
206            field_dict["signalsUrl"] = signals_url
207        if signals_upload is not UNSET:
208            field_dict["signalsUpload"] = signals_upload
209        if signals_uploaded_at is not UNSET:
210            field_dict["signalsUploadedAt"] = signals_uploaded_at
211        if signals_upload_reason is not UNSET:
212            field_dict["signalsUploadReason"] = signals_upload_reason
213
214        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
216    @classmethod
217    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
218        from ..models.equity_curve_result import EquityCurveResult
219        from ..models.notice import Notice
220
221        d = dict(src_dict)
222        strategy_id = d.pop("strategyId")
223
224        instrument = d.pop("instrument")
225
226        host_name = d.pop("hostName", UNSET)
227
228        iops = d.pop("iops", UNSET)
229
230        _notices = d.pop("notices", UNSET)
231        notices: list[Notice] | Unset = UNSET
232        if _notices is not UNSET:
233            notices = []
234            for notices_item_data in _notices:
235                notices_item = Notice.from_dict(notices_item_data)
236
237                notices.append(notices_item)
238
239        notices_truncated = d.pop("noticesTruncated", UNSET)
240
241        pnl_total = d.pop("pnlTotal", UNSET)
242
243        pnl_total_percent = d.pop("pnlTotalPercent", UNSET)
244
245        total_trades = d.pop("totalTrades", UNSET)
246
247        win_rate = d.pop("winRate", UNSET)
248
249        sharpe_ratio = d.pop("sharpeRatio", UNSET)
250
251        sortino_ratio = d.pop("sortinoRatio", UNSET)
252
253        cagr = d.pop("cagr", UNSET)
254
255        max_drawdown = d.pop("maxDrawdown", UNSET)
256
257        max_drawdown_percent = d.pop("maxDrawdownPercent", UNSET)
258
259        _equity_curve = d.pop("equityCurve", UNSET)
260        equity_curve: EquityCurveResult | Unset
261        if isinstance(_equity_curve, Unset):
262            equity_curve = UNSET
263        else:
264            equity_curve = EquityCurveResult.from_dict(_equity_curve)
265
266        signal_count = d.pop("signalCount", UNSET)
267
268        signals_id = d.pop("signalsId", UNSET)
269
270        signals_url = d.pop("signalsUrl", UNSET)
271
272        _signals_upload = d.pop("signalsUpload", UNSET)
273        signals_upload: ResultMapSignalsUpload | Unset
274        if isinstance(_signals_upload, Unset):
275            signals_upload = UNSET
276        else:
277            signals_upload = ResultMapSignalsUpload(_signals_upload)
278
279        _signals_uploaded_at = d.pop("signalsUploadedAt", UNSET)
280        signals_uploaded_at: datetime.datetime | Unset
281        if isinstance(_signals_uploaded_at, Unset):
282            signals_uploaded_at = UNSET
283        else:
284            signals_uploaded_at = isoparse(_signals_uploaded_at)
285
286        signals_upload_reason = d.pop("signalsUploadReason", UNSET)
287
288        result_map = cls(
289            strategy_id=strategy_id,
290            instrument=instrument,
291            host_name=host_name,
292            iops=iops,
293            notices=notices,
294            notices_truncated=notices_truncated,
295            pnl_total=pnl_total,
296            pnl_total_percent=pnl_total_percent,
297            total_trades=total_trades,
298            win_rate=win_rate,
299            sharpe_ratio=sharpe_ratio,
300            sortino_ratio=sortino_ratio,
301            cagr=cagr,
302            max_drawdown=max_drawdown,
303            max_drawdown_percent=max_drawdown_percent,
304            equity_curve=equity_curve,
305            signal_count=signal_count,
306            signals_id=signals_id,
307            signals_url=signals_url,
308            signals_upload=signals_upload,
309            signals_uploaded_at=signals_uploaded_at,
310            signals_upload_reason=signals_upload_reason,
311        )
312
313        result_map.additional_properties = d
314        return result_map
additional_keys: list[str]
316    @property
317    def additional_keys(self) -> list[str]:
318        return list(self.additional_properties.keys())
class ResultMapSignalsUpload(builtins.str, enum.Enum):
 5class ResultMapSignalsUpload(str, Enum):
 6    DONE = "Done"
 7    FAILED = "Failed"
 8    SKIPPED = "Skipped"
 9
10    def __str__(self) -> str:
11        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

DONE = <ResultMapSignalsUpload.DONE: 'Done'>
FAILED = <ResultMapSignalsUpload.FAILED: 'Failed'>
SKIPPED = <ResultMapSignalsUpload.SKIPPED: 'Skipped'>
class StrategyState:
 24@_attrs_define
 25class StrategyState:
 26    """What is known about a registered strategy: that it compiled, and what validating it found.
 27
 28    **`validation: passed` does not mean the strategy is correct.** It means the class loaded and
 29    survived the first event of a short synthetic run — a floor, not a guarantee. When
 30    `dryRunIncomplete` is true it is a lower floor still, because the run did not finish.
 31
 32        Example:
 33            {'strategyId': '6bsh31ikwkuivhtgcoa6s4', 'validation': 'passed', 'compiledAt': '2026-08-04T16:23:04Z',
 34                'requiredSources': ['Ticker'], 'validatedAt': '2026-08-04T16:24:11Z', 'notices': [{'level': 'WARN', 'code':
 35                'indicator.bar-data-on-ticker-path', 'message': 'Indicator requires bar data but is on the ticker path',
 36                'provenance': 'compile-dry-run'}], '_links': {'code': {'href': '/v1/strategy/6bsh31ikwkuivhtgcoa6s4/code'}}}
 37
 38        Attributes:
 39            strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
 40                always yields the same id, for every caller, whatever its formatting. See
 41                `POST /strategy` for exactly which rewrites preserve it and which do not.
 42                 Example: 6bsh31ikwkuivhtgcoa6s4.
 43            validation (StrategyStateValidation): * `not_validated` — registered, never checked. `POST
 44                /strategy/{strategyId}/validate`
 45                  checks it.
 46                * `pending` — a check was asked for and has not answered yet.
 47                * `passed` — the class loaded and survived its first event.
 48                * `failed` — it did not; `detail` says how.
 49                 Example: passed.
 50            compiled_at (datetime.datetime | Unset): When the live compilation was produced.
 51            required_sources (list[StrategyStateRequiredSourcesItem] | Unset): The market data a strategy needs, read off
 52                the compiled class rather than off anything
 53                you sent — `TickerStrategy`, `KlineStrategy` and `FundingRateStrategy` each declare one,
 54                and a `MultiSourceStrategy` declares a set.
 55
 56                **Absent is not "needs nothing".** A strategy always needs market data, so an absent
 57                field never means an empty requirement — it means the platform could not establish the
 58                answer without constructing your strategy, which it will not do to fill in a field.
 59                That happens for a `MultiSourceStrategy`, for a class that overrides
 60                `getMarketDataSource()`, and for anything registered before this field existed;
 61                re-registering the source fills it in.
 62                 Example: ['Ticker'].
 63            validated_at (datetime.datetime | Unset): When the verdict was recorded. Absent until there is one.
 64            detail (str | Unset): Why validation failed, or why a queued check has not reported. Present on `failed`, and
 65                alongside `validationStalled`.
 66            notices (list[Notice] | Unset): What the run surfaced. An empty or absent list is not a clean bill of health
 67                when
 68                `dryRunIncomplete` is true — see that field.
 69            notices_truncated (int | Unset): How many notices were dropped past the cap. Absent when none were. Example: 3.
 70            dry_run_incomplete (bool | Unset): The check did not finish its budget — it ran out of time, was refused because
 71                the
 72                platform was already holding too many unfinishable runs, or hit a failure attributable to
 73                the synthetic instrument rather than to your strategy. The verdict stands as far as it
 74                went; it simply reached less than a full run would.
 75            validation_stalled (bool | Unset): A queued check has not reported for far longer than one takes. Nothing is
 76                disproved about
 77                the strategy — the check has not run. Stop waiting and re-request it later.
 78            field_links (StrategyLinks | Unset): HAL `_links` for a strategy — present on a full `StrategyState` body (`GET
 79                /strategy/{strategyId}`, and `POST /strategy/{strategyId}/validate`'s already-validated
 80                `200`), absent from that same endpoint's `202` — a deliberately partial stub carrying only
 81                what is known before a check has even started. Following `code` can still `404` once
 82                present: it documents its own honest "nothing to return" for a strategy with no source of
 83                its own (a `REFERENCE` marketplace copy, or one resolved only through the platform's shared
 84                pool). This link says where to look, not that something is there.
 85    """
 86
 87    strategy_id: str
 88    validation: StrategyStateValidation
 89    compiled_at: datetime.datetime | Unset = UNSET
 90    required_sources: list[StrategyStateRequiredSourcesItem] | Unset = UNSET
 91    validated_at: datetime.datetime | Unset = UNSET
 92    detail: str | Unset = UNSET
 93    notices: list[Notice] | Unset = UNSET
 94    notices_truncated: int | Unset = UNSET
 95    dry_run_incomplete: bool | Unset = UNSET
 96    validation_stalled: bool | Unset = UNSET
 97    field_links: StrategyLinks | Unset = UNSET
 98    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 99
100    def to_dict(self) -> dict[str, Any]:
101        strategy_id = self.strategy_id
102
103        validation = self.validation.value
104
105        compiled_at: str | Unset = UNSET
106        if not isinstance(self.compiled_at, Unset):
107            compiled_at = self.compiled_at.isoformat()
108
109        required_sources: list[str] | Unset = UNSET
110        if not isinstance(self.required_sources, Unset):
111            required_sources = []
112            for required_sources_item_data in self.required_sources:
113                required_sources_item = required_sources_item_data.value
114                required_sources.append(required_sources_item)
115
116        validated_at: str | Unset = UNSET
117        if not isinstance(self.validated_at, Unset):
118            validated_at = self.validated_at.isoformat()
119
120        detail = self.detail
121
122        notices: list[dict[str, Any]] | Unset = UNSET
123        if not isinstance(self.notices, Unset):
124            notices = []
125            for notices_item_data in self.notices:
126                notices_item = notices_item_data.to_dict()
127                notices.append(notices_item)
128
129        notices_truncated = self.notices_truncated
130
131        dry_run_incomplete = self.dry_run_incomplete
132
133        validation_stalled = self.validation_stalled
134
135        field_links: dict[str, Any] | Unset = UNSET
136        if not isinstance(self.field_links, Unset):
137            field_links = self.field_links.to_dict()
138
139        field_dict: dict[str, Any] = {}
140        field_dict.update(self.additional_properties)
141        field_dict.update(
142            {
143                "strategyId": strategy_id,
144                "validation": validation,
145            }
146        )
147        if compiled_at is not UNSET:
148            field_dict["compiledAt"] = compiled_at
149        if required_sources is not UNSET:
150            field_dict["requiredSources"] = required_sources
151        if validated_at is not UNSET:
152            field_dict["validatedAt"] = validated_at
153        if detail is not UNSET:
154            field_dict["detail"] = detail
155        if notices is not UNSET:
156            field_dict["notices"] = notices
157        if notices_truncated is not UNSET:
158            field_dict["noticesTruncated"] = notices_truncated
159        if dry_run_incomplete is not UNSET:
160            field_dict["dryRunIncomplete"] = dry_run_incomplete
161        if validation_stalled is not UNSET:
162            field_dict["validationStalled"] = validation_stalled
163        if field_links is not UNSET:
164            field_dict["_links"] = field_links
165
166        return field_dict
167
168    @classmethod
169    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
170        from ..models.notice import Notice
171        from ..models.strategy_links import StrategyLinks
172
173        d = dict(src_dict)
174        strategy_id = d.pop("strategyId")
175
176        validation = StrategyStateValidation(d.pop("validation"))
177
178        _compiled_at = d.pop("compiledAt", UNSET)
179        compiled_at: datetime.datetime | Unset
180        if isinstance(_compiled_at, Unset):
181            compiled_at = UNSET
182        else:
183            compiled_at = isoparse(_compiled_at)
184
185        _required_sources = d.pop("requiredSources", UNSET)
186        required_sources: list[StrategyStateRequiredSourcesItem] | Unset = UNSET
187        if _required_sources is not UNSET:
188            required_sources = []
189            for required_sources_item_data in _required_sources:
190                required_sources_item = StrategyStateRequiredSourcesItem(required_sources_item_data)
191
192                required_sources.append(required_sources_item)
193
194        _validated_at = d.pop("validatedAt", UNSET)
195        validated_at: datetime.datetime | Unset
196        if isinstance(_validated_at, Unset):
197            validated_at = UNSET
198        else:
199            validated_at = isoparse(_validated_at)
200
201        detail = d.pop("detail", UNSET)
202
203        _notices = d.pop("notices", UNSET)
204        notices: list[Notice] | Unset = UNSET
205        if _notices is not UNSET:
206            notices = []
207            for notices_item_data in _notices:
208                notices_item = Notice.from_dict(notices_item_data)
209
210                notices.append(notices_item)
211
212        notices_truncated = d.pop("noticesTruncated", UNSET)
213
214        dry_run_incomplete = d.pop("dryRunIncomplete", UNSET)
215
216        validation_stalled = d.pop("validationStalled", UNSET)
217
218        _field_links = d.pop("_links", UNSET)
219        field_links: StrategyLinks | Unset
220        if isinstance(_field_links, Unset):
221            field_links = UNSET
222        else:
223            field_links = StrategyLinks.from_dict(_field_links)
224
225        strategy_state = cls(
226            strategy_id=strategy_id,
227            validation=validation,
228            compiled_at=compiled_at,
229            required_sources=required_sources,
230            validated_at=validated_at,
231            detail=detail,
232            notices=notices,
233            notices_truncated=notices_truncated,
234            dry_run_incomplete=dry_run_incomplete,
235            validation_stalled=validation_stalled,
236            field_links=field_links,
237        )
238
239        strategy_state.additional_properties = d
240        return strategy_state
241
242    @property
243    def additional_keys(self) -> list[str]:
244        return list(self.additional_properties.keys())
245
246    def __getitem__(self, key: str) -> Any:
247        return self.additional_properties[key]
248
249    def __setitem__(self, key: str, value: Any) -> None:
250        self.additional_properties[key] = value
251
252    def __delitem__(self, key: str) -> None:
253        del self.additional_properties[key]
254
255    def __contains__(self, key: str) -> bool:
256        return key in self.additional_properties

What is known about a registered strategy: that it compiled, and what validating it found.

validation: passed does not mean the strategy is correct. It means the class loaded and survived the first event of a short synthetic run — a floor, not a guarantee. When dryRunIncomplete is true it is a lower floor still, because the run did not finish.

Example:
    {'strategyId': '6bsh31ikwkuivhtgcoa6s4', 'validation': 'passed', 'compiledAt': '2026-08-04T16:23:04Z',
        'requiredSources': ['Ticker'], 'validatedAt': '2026-08-04T16:24:11Z', 'notices': [{'level': 'WARN', 'code':
        'indicator.bar-data-on-ticker-path', 'message': 'Indicator requires bar data but is on the ticker path',
        'provenance': 'compile-dry-run'}], '_links': {'code': {'href': '/v1/strategy/6bsh31ikwkuivhtgcoa6s4/code'}}}

Attributes:
    strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
        always yields the same id, for every caller, whatever its formatting. See
        `POST /strategy` for exactly which rewrites preserve it and which do not.
         Example: 6bsh31ikwkuivhtgcoa6s4.
    validation (StrategyStateValidation): * `not_validated` — registered, never checked. `POST
        /strategy/{strategyId}/validate`
          checks it.
        * `pending` — a check was asked for and has not answered yet.
        * `passed` — the class loaded and survived its first event.
        * `failed` — it did not; `detail` says how.
         Example: passed.
    compiled_at (datetime.datetime | Unset): When the live compilation was produced.
    required_sources (list[StrategyStateRequiredSourcesItem] | Unset): The market data a strategy needs, read off
        the compiled class rather than off anything
        you sent — `TickerStrategy`, `KlineStrategy` and `FundingRateStrategy` each declare one,
        and a `MultiSourceStrategy` declares a set.

        **Absent is not "needs nothing".** A strategy always needs market data, so an absent
        field never means an empty requirement — it means the platform could not establish the
        answer without constructing your strategy, which it will not do to fill in a field.
        That happens for a `MultiSourceStrategy`, for a class that overrides
        `getMarketDataSource()`, and for anything registered before this field existed;
        re-registering the source fills it in.
         Example: ['Ticker'].
    validated_at (datetime.datetime | Unset): When the verdict was recorded. Absent until there is one.
    detail (str | Unset): Why validation failed, or why a queued check has not reported. Present on `failed`, and
        alongside `validationStalled`.
    notices (list[Notice] | Unset): What the run surfaced. An empty or absent list is not a clean bill of health
        when
        `dryRunIncomplete` is true — see that field.
    notices_truncated (int | Unset): How many notices were dropped past the cap. Absent when none were. Example: 3.
    dry_run_incomplete (bool | Unset): The check did not finish its budget — it ran out of time, was refused because
        the
        platform was already holding too many unfinishable runs, or hit a failure attributable to
        the synthetic instrument rather than to your strategy. The verdict stands as far as it
        went; it simply reached less than a full run would.
    validation_stalled (bool | Unset): A queued check has not reported for far longer than one takes. Nothing is
        disproved about
        the strategy — the check has not run. Stop waiting and re-request it later.
    field_links (StrategyLinks | Unset): HAL `_links` for a strategy — present on a full `StrategyState` body (`GET
        /strategy/{strategyId}`, and `POST /strategy/{strategyId}/validate`'s already-validated
        `200`), absent from that same endpoint's `202` — a deliberately partial stub carrying only
        what is known before a check has even started. Following `code` can still `404` once
        present: it documents its own honest "nothing to return" for a strategy with no source of
        its own (a `REFERENCE` marketplace copy, or one resolved only through the platform's shared
        pool). This link says where to look, not that something is there.
StrategyState( strategy_id: str, validation: StrategyStateValidation, compiled_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, required_sources: list[StrategyStateRequiredSourcesItem] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, validated_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, detail: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, notices: list[Notice] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, notices_truncated: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, dry_run_incomplete: bool | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, validation_stalled: bool | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, field_links: StrategyLinks | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
34def __init__(self, strategy_id, validation, compiled_at=attr_dict['compiled_at'].default, required_sources=attr_dict['required_sources'].default, validated_at=attr_dict['validated_at'].default, detail=attr_dict['detail'].default, notices=attr_dict['notices'].default, notices_truncated=attr_dict['notices_truncated'].default, dry_run_incomplete=attr_dict['dry_run_incomplete'].default, validation_stalled=attr_dict['validation_stalled'].default, field_links=attr_dict['field_links'].default):
35    self.strategy_id = strategy_id
36    self.validation = validation
37    self.compiled_at = compiled_at
38    self.required_sources = required_sources
39    self.validated_at = validated_at
40    self.detail = detail
41    self.notices = notices
42    self.notices_truncated = notices_truncated
43    self.dry_run_incomplete = dry_run_incomplete
44    self.validation_stalled = validation_stalled
45    self.field_links = field_links
46    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class StrategyState.

strategy_id: str
compiled_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset
required_sources: list[StrategyStateRequiredSourcesItem] | qtsurfer.api.client._generated.types.Unset
validated_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset
detail: str | qtsurfer.api.client._generated.types.Unset
notices: list[Notice] | qtsurfer.api.client._generated.types.Unset
notices_truncated: int | qtsurfer.api.client._generated.types.Unset
dry_run_incomplete: bool | qtsurfer.api.client._generated.types.Unset
validation_stalled: bool | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
100    def to_dict(self) -> dict[str, Any]:
101        strategy_id = self.strategy_id
102
103        validation = self.validation.value
104
105        compiled_at: str | Unset = UNSET
106        if not isinstance(self.compiled_at, Unset):
107            compiled_at = self.compiled_at.isoformat()
108
109        required_sources: list[str] | Unset = UNSET
110        if not isinstance(self.required_sources, Unset):
111            required_sources = []
112            for required_sources_item_data in self.required_sources:
113                required_sources_item = required_sources_item_data.value
114                required_sources.append(required_sources_item)
115
116        validated_at: str | Unset = UNSET
117        if not isinstance(self.validated_at, Unset):
118            validated_at = self.validated_at.isoformat()
119
120        detail = self.detail
121
122        notices: list[dict[str, Any]] | Unset = UNSET
123        if not isinstance(self.notices, Unset):
124            notices = []
125            for notices_item_data in self.notices:
126                notices_item = notices_item_data.to_dict()
127                notices.append(notices_item)
128
129        notices_truncated = self.notices_truncated
130
131        dry_run_incomplete = self.dry_run_incomplete
132
133        validation_stalled = self.validation_stalled
134
135        field_links: dict[str, Any] | Unset = UNSET
136        if not isinstance(self.field_links, Unset):
137            field_links = self.field_links.to_dict()
138
139        field_dict: dict[str, Any] = {}
140        field_dict.update(self.additional_properties)
141        field_dict.update(
142            {
143                "strategyId": strategy_id,
144                "validation": validation,
145            }
146        )
147        if compiled_at is not UNSET:
148            field_dict["compiledAt"] = compiled_at
149        if required_sources is not UNSET:
150            field_dict["requiredSources"] = required_sources
151        if validated_at is not UNSET:
152            field_dict["validatedAt"] = validated_at
153        if detail is not UNSET:
154            field_dict["detail"] = detail
155        if notices is not UNSET:
156            field_dict["notices"] = notices
157        if notices_truncated is not UNSET:
158            field_dict["noticesTruncated"] = notices_truncated
159        if dry_run_incomplete is not UNSET:
160            field_dict["dryRunIncomplete"] = dry_run_incomplete
161        if validation_stalled is not UNSET:
162            field_dict["validationStalled"] = validation_stalled
163        if field_links is not UNSET:
164            field_dict["_links"] = field_links
165
166        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
168    @classmethod
169    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
170        from ..models.notice import Notice
171        from ..models.strategy_links import StrategyLinks
172
173        d = dict(src_dict)
174        strategy_id = d.pop("strategyId")
175
176        validation = StrategyStateValidation(d.pop("validation"))
177
178        _compiled_at = d.pop("compiledAt", UNSET)
179        compiled_at: datetime.datetime | Unset
180        if isinstance(_compiled_at, Unset):
181            compiled_at = UNSET
182        else:
183            compiled_at = isoparse(_compiled_at)
184
185        _required_sources = d.pop("requiredSources", UNSET)
186        required_sources: list[StrategyStateRequiredSourcesItem] | Unset = UNSET
187        if _required_sources is not UNSET:
188            required_sources = []
189            for required_sources_item_data in _required_sources:
190                required_sources_item = StrategyStateRequiredSourcesItem(required_sources_item_data)
191
192                required_sources.append(required_sources_item)
193
194        _validated_at = d.pop("validatedAt", UNSET)
195        validated_at: datetime.datetime | Unset
196        if isinstance(_validated_at, Unset):
197            validated_at = UNSET
198        else:
199            validated_at = isoparse(_validated_at)
200
201        detail = d.pop("detail", UNSET)
202
203        _notices = d.pop("notices", UNSET)
204        notices: list[Notice] | Unset = UNSET
205        if _notices is not UNSET:
206            notices = []
207            for notices_item_data in _notices:
208                notices_item = Notice.from_dict(notices_item_data)
209
210                notices.append(notices_item)
211
212        notices_truncated = d.pop("noticesTruncated", UNSET)
213
214        dry_run_incomplete = d.pop("dryRunIncomplete", UNSET)
215
216        validation_stalled = d.pop("validationStalled", UNSET)
217
218        _field_links = d.pop("_links", UNSET)
219        field_links: StrategyLinks | Unset
220        if isinstance(_field_links, Unset):
221            field_links = UNSET
222        else:
223            field_links = StrategyLinks.from_dict(_field_links)
224
225        strategy_state = cls(
226            strategy_id=strategy_id,
227            validation=validation,
228            compiled_at=compiled_at,
229            required_sources=required_sources,
230            validated_at=validated_at,
231            detail=detail,
232            notices=notices,
233            notices_truncated=notices_truncated,
234            dry_run_incomplete=dry_run_incomplete,
235            validation_stalled=validation_stalled,
236            field_links=field_links,
237        )
238
239        strategy_state.additional_properties = d
240        return strategy_state
additional_keys: list[str]
242    @property
243    def additional_keys(self) -> list[str]:
244        return list(self.additional_properties.keys())
class StrategyStateRequiredSourcesItem(builtins.str, enum.Enum):
 5class StrategyStateRequiredSourcesItem(str, Enum):
 6    FUNDINGRATE = "FundingRate"
 7    KLINE = "KLine"
 8    TICKER = "Ticker"
 9
10    def __str__(self) -> str:
11        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

FUNDINGRATE = <StrategyStateRequiredSourcesItem.FUNDINGRATE: 'FundingRate'>
class StrategyStateValidation(builtins.str, enum.Enum):
 5class StrategyStateValidation(str, Enum):
 6    FAILED = "failed"
 7    NOT_VALIDATED = "not_validated"
 8    PASSED = "passed"
 9    PENDING = "pending"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

FAILED = <StrategyStateValidation.FAILED: 'failed'>
NOT_VALIDATED = <StrategyStateValidation.NOT_VALIDATED: 'not_validated'>
PASSED = <StrategyStateValidation.PASSED: 'passed'>
PENDING = <StrategyStateValidation.PENDING: 'pending'>
class StrategySummary:
 17@_attrs_define
 18class StrategySummary:
 19    """One entry from `GET /strategies` — the same provenance a full `StrategyState` carries
 20    (`compiledAt`, `requiredSources`), without its validation state, so listing stays cheap
 21    regardless of how many strategies you have registered. Check a specific strategy's
 22    validation with `GET /strategy/{strategyId}`.
 23
 24        Attributes:
 25            strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
 26                always yields the same id, for every caller, whatever its formatting. See
 27                `POST /strategy` for exactly which rewrites preserve it and which do not.
 28                 Example: 6bsh31ikwkuivhtgcoa6s4.
 29            compiled_at (datetime.datetime | Unset): When the live compilation was produced.
 30            required_sources (list[str] | Unset): The market data this strategy needs. Absent, not empty, when it could
 31                not be established without constructing the strategy.
 32    """
 33
 34    strategy_id: str
 35    compiled_at: datetime.datetime | Unset = UNSET
 36    required_sources: list[str] | Unset = UNSET
 37    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 38
 39    def to_dict(self) -> dict[str, Any]:
 40        strategy_id = self.strategy_id
 41
 42        compiled_at: str | Unset = UNSET
 43        if not isinstance(self.compiled_at, Unset):
 44            compiled_at = self.compiled_at.isoformat()
 45
 46        required_sources: list[str] | Unset = UNSET
 47        if not isinstance(self.required_sources, Unset):
 48            required_sources = self.required_sources
 49
 50        field_dict: dict[str, Any] = {}
 51        field_dict.update(self.additional_properties)
 52        field_dict.update(
 53            {
 54                "strategyId": strategy_id,
 55            }
 56        )
 57        if compiled_at is not UNSET:
 58            field_dict["compiledAt"] = compiled_at
 59        if required_sources is not UNSET:
 60            field_dict["requiredSources"] = required_sources
 61
 62        return field_dict
 63
 64    @classmethod
 65    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 66        d = dict(src_dict)
 67        strategy_id = d.pop("strategyId")
 68
 69        _compiled_at = d.pop("compiledAt", UNSET)
 70        compiled_at: datetime.datetime | Unset
 71        if isinstance(_compiled_at, Unset):
 72            compiled_at = UNSET
 73        else:
 74            compiled_at = isoparse(_compiled_at)
 75
 76        required_sources = cast(list[str], d.pop("requiredSources", UNSET))
 77
 78        strategy_summary = cls(
 79            strategy_id=strategy_id,
 80            compiled_at=compiled_at,
 81            required_sources=required_sources,
 82        )
 83
 84        strategy_summary.additional_properties = d
 85        return strategy_summary
 86
 87    @property
 88    def additional_keys(self) -> list[str]:
 89        return list(self.additional_properties.keys())
 90
 91    def __getitem__(self, key: str) -> Any:
 92        return self.additional_properties[key]
 93
 94    def __setitem__(self, key: str, value: Any) -> None:
 95        self.additional_properties[key] = value
 96
 97    def __delitem__(self, key: str) -> None:
 98        del self.additional_properties[key]
 99
100    def __contains__(self, key: str) -> bool:
101        return key in self.additional_properties

One entry from GET /strategies — the same provenance a full StrategyState carries (compiledAt, requiredSources), without its validation state, so listing stays cheap regardless of how many strategies you have registered. Check a specific strategy's validation with GET /strategy/{strategyId}.

Attributes:
    strategy_id (str): Unique identifier for a compiled strategy, derived from the source itself: the same code
        always yields the same id, for every caller, whatever its formatting. See
        `POST /strategy` for exactly which rewrites preserve it and which do not.
         Example: 6bsh31ikwkuivhtgcoa6s4.
    compiled_at (datetime.datetime | Unset): When the live compilation was produced.
    required_sources (list[str] | Unset): The market data this strategy needs. Absent, not empty, when it could
        not be established without constructing the strategy.
StrategySummary( strategy_id: str, compiled_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, required_sources: list[str] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
26def __init__(self, strategy_id, compiled_at=attr_dict['compiled_at'].default, required_sources=attr_dict['required_sources'].default):
27    self.strategy_id = strategy_id
28    self.compiled_at = compiled_at
29    self.required_sources = required_sources
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class StrategySummary.

strategy_id: str
compiled_at: datetime.datetime | qtsurfer.api.client._generated.types.Unset
required_sources: list[str] | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
39    def to_dict(self) -> dict[str, Any]:
40        strategy_id = self.strategy_id
41
42        compiled_at: str | Unset = UNSET
43        if not isinstance(self.compiled_at, Unset):
44            compiled_at = self.compiled_at.isoformat()
45
46        required_sources: list[str] | Unset = UNSET
47        if not isinstance(self.required_sources, Unset):
48            required_sources = self.required_sources
49
50        field_dict: dict[str, Any] = {}
51        field_dict.update(self.additional_properties)
52        field_dict.update(
53            {
54                "strategyId": strategy_id,
55            }
56        )
57        if compiled_at is not UNSET:
58            field_dict["compiledAt"] = compiled_at
59        if required_sources is not UNSET:
60            field_dict["requiredSources"] = required_sources
61
62        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
64    @classmethod
65    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
66        d = dict(src_dict)
67        strategy_id = d.pop("strategyId")
68
69        _compiled_at = d.pop("compiledAt", UNSET)
70        compiled_at: datetime.datetime | Unset
71        if isinstance(_compiled_at, Unset):
72            compiled_at = UNSET
73        else:
74            compiled_at = isoparse(_compiled_at)
75
76        required_sources = cast(list[str], d.pop("requiredSources", UNSET))
77
78        strategy_summary = cls(
79            strategy_id=strategy_id,
80            compiled_at=compiled_at,
81            required_sources=required_sources,
82        )
83
84        strategy_summary.additional_properties = d
85        return strategy_summary
additional_keys: list[str]
87    @property
88    def additional_keys(self) -> list[str]:
89        return list(self.additional_properties.keys())
class SweepAxisType0:
12@_attrs_define
13class SweepAxisType0:
14    """
15    Attributes:
16        from_ (float):
17        to (float):
18        step (float):
19    """
20
21    from_: float
22    to: float
23    step: float
24
25    def to_dict(self) -> dict[str, Any]:
26        from_ = self.from_
27
28        to = self.to
29
30        step = self.step
31
32        field_dict: dict[str, Any] = {}
33
34        field_dict.update(
35            {
36                "from": from_,
37                "to": to,
38                "step": step,
39            }
40        )
41
42        return field_dict
43
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        from_ = d.pop("from")
48
49        to = d.pop("to")
50
51        step = d.pop("step")
52
53        sweep_axis_type_0 = cls(
54            from_=from_,
55            to=to,
56            step=step,
57        )
58
59        return sweep_axis_type_0

Attributes: from_ (float): to (float): step (float):

SweepAxisType0(from_: float, to: float, step: float)
25def __init__(self, from_, to, step):
26    self.from_ = from_
27    self.to = to
28    self.step = step

Method generated by attrs for class SweepAxisType0.

from_: float
to: float
step: float
def to_dict(self) -> dict[str, typing.Any]:
25    def to_dict(self) -> dict[str, Any]:
26        from_ = self.from_
27
28        to = self.to
29
30        step = self.step
31
32        field_dict: dict[str, Any] = {}
33
34        field_dict.update(
35            {
36                "from": from_,
37                "to": to,
38                "step": step,
39            }
40        )
41
42        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
44    @classmethod
45    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
46        d = dict(src_dict)
47        from_ = d.pop("from")
48
49        to = d.pop("to")
50
51        step = d.pop("step")
52
53        sweep_axis_type_0 = cls(
54            from_=from_,
55            to=to,
56            step=step,
57        )
58
59        return sweep_axis_type_0
class SweepAxisType1:
12@_attrs_define
13class SweepAxisType1:
14    """
15    Attributes:
16        values (list[bool | float]):
17    """
18
19    values: list[bool | float]
20
21    def to_dict(self) -> dict[str, Any]:
22        values = []
23        for values_item_data in self.values:
24            values_item: bool | float
25            values_item = values_item_data
26            values.append(values_item)
27
28        field_dict: dict[str, Any] = {}
29
30        field_dict.update(
31            {
32                "values": values,
33            }
34        )
35
36        return field_dict
37
38    @classmethod
39    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
40        d = dict(src_dict)
41        values = []
42        _values = d.pop("values")
43        for values_item_data in _values:
44
45            def _parse_values_item(data: object) -> bool | float:
46                return cast(bool | float, data)
47
48            values_item = _parse_values_item(values_item_data)
49
50            values.append(values_item)
51
52        sweep_axis_type_1 = cls(
53            values=values,
54        )
55
56        return sweep_axis_type_1

Attributes: values (list[bool | float]):

SweepAxisType1(values: list[bool | float])
23def __init__(self, values):
24    self.values = values

Method generated by attrs for class SweepAxisType1.

values: list[bool | float]
def to_dict(self) -> dict[str, typing.Any]:
21    def to_dict(self) -> dict[str, Any]:
22        values = []
23        for values_item_data in self.values:
24            values_item: bool | float
25            values_item = values_item_data
26            values.append(values_item)
27
28        field_dict: dict[str, Any] = {}
29
30        field_dict.update(
31            {
32                "values": values,
33            }
34        )
35
36        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
38    @classmethod
39    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
40        d = dict(src_dict)
41        values = []
42        _values = d.pop("values")
43        for values_item_data in _values:
44
45            def _parse_values_item(data: object) -> bool | float:
46                return cast(bool | float, data)
47
48            values_item = _parse_values_item(values_item_data)
49
50            values.append(values_item)
51
52        sweep_axis_type_1 = cls(
53            values=values,
54        )
55
56        return sweep_axis_type_1
class SweepBaseConfig:
 16@_attrs_define
 17class SweepBaseConfig:
 18    """
 19    Attributes:
 20        initial_funding (float | Unset):  Default: 10000.0.
 21        fee_rate (float | Unset):  Default: 0.001.
 22        buy_fee_rate (float | Unset):
 23        sell_fee_rate (float | Unset):
 24        fee_leg (SweepBaseConfigFeeLeg | Unset):  Default: SweepBaseConfigFeeLeg.RECEIVED.
 25        percent_amount_to_lock (float | Unset):
 26    """
 27
 28    initial_funding: float | Unset = 10000.0
 29    fee_rate: float | Unset = 0.001
 30    buy_fee_rate: float | Unset = UNSET
 31    sell_fee_rate: float | Unset = UNSET
 32    fee_leg: SweepBaseConfigFeeLeg | Unset = SweepBaseConfigFeeLeg.RECEIVED
 33    percent_amount_to_lock: float | Unset = UNSET
 34    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 35
 36    def to_dict(self) -> dict[str, Any]:
 37        initial_funding = self.initial_funding
 38
 39        fee_rate = self.fee_rate
 40
 41        buy_fee_rate = self.buy_fee_rate
 42
 43        sell_fee_rate = self.sell_fee_rate
 44
 45        fee_leg: str | Unset = UNSET
 46        if not isinstance(self.fee_leg, Unset):
 47            fee_leg = self.fee_leg.value
 48
 49        percent_amount_to_lock = self.percent_amount_to_lock
 50
 51        field_dict: dict[str, Any] = {}
 52        field_dict.update(self.additional_properties)
 53        field_dict.update({})
 54        if initial_funding is not UNSET:
 55            field_dict["initialFunding"] = initial_funding
 56        if fee_rate is not UNSET:
 57            field_dict["feeRate"] = fee_rate
 58        if buy_fee_rate is not UNSET:
 59            field_dict["buyFeeRate"] = buy_fee_rate
 60        if sell_fee_rate is not UNSET:
 61            field_dict["sellFeeRate"] = sell_fee_rate
 62        if fee_leg is not UNSET:
 63            field_dict["feeLeg"] = fee_leg
 64        if percent_amount_to_lock is not UNSET:
 65            field_dict["percentAmountToLock"] = percent_amount_to_lock
 66
 67        return field_dict
 68
 69    @classmethod
 70    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 71        d = dict(src_dict)
 72        initial_funding = d.pop("initialFunding", UNSET)
 73
 74        fee_rate = d.pop("feeRate", UNSET)
 75
 76        buy_fee_rate = d.pop("buyFeeRate", UNSET)
 77
 78        sell_fee_rate = d.pop("sellFeeRate", UNSET)
 79
 80        _fee_leg = d.pop("feeLeg", UNSET)
 81        fee_leg: SweepBaseConfigFeeLeg | Unset
 82        if isinstance(_fee_leg, Unset):
 83            fee_leg = UNSET
 84        else:
 85            fee_leg = SweepBaseConfigFeeLeg(_fee_leg)
 86
 87        percent_amount_to_lock = d.pop("percentAmountToLock", UNSET)
 88
 89        sweep_base_config = cls(
 90            initial_funding=initial_funding,
 91            fee_rate=fee_rate,
 92            buy_fee_rate=buy_fee_rate,
 93            sell_fee_rate=sell_fee_rate,
 94            fee_leg=fee_leg,
 95            percent_amount_to_lock=percent_amount_to_lock,
 96        )
 97
 98        sweep_base_config.additional_properties = d
 99        return sweep_base_config
100
101    @property
102    def additional_keys(self) -> list[str]:
103        return list(self.additional_properties.keys())
104
105    def __getitem__(self, key: str) -> Any:
106        return self.additional_properties[key]
107
108    def __setitem__(self, key: str, value: Any) -> None:
109        self.additional_properties[key] = value
110
111    def __delitem__(self, key: str) -> None:
112        del self.additional_properties[key]
113
114    def __contains__(self, key: str) -> bool:
115        return key in self.additional_properties

Attributes: initial_funding (float | Unset): Default: 10000.0. fee_rate (float | Unset): Default: 0.001. buy_fee_rate (float | Unset): sell_fee_rate (float | Unset): fee_leg (SweepBaseConfigFeeLeg | Unset): Default: SweepBaseConfigFeeLeg.RECEIVED. percent_amount_to_lock (float | Unset):

SweepBaseConfig( initial_funding: float | qtsurfer.api.client._generated.types.Unset = 10000.0, fee_rate: float | qtsurfer.api.client._generated.types.Unset = 0.001, buy_fee_rate: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, sell_fee_rate: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, fee_leg: SweepBaseConfigFeeLeg | qtsurfer.api.client._generated.types.Unset = <SweepBaseConfigFeeLeg.RECEIVED: 'RECEIVED'>, percent_amount_to_lock: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
29def __init__(self, initial_funding=attr_dict['initial_funding'].default, fee_rate=attr_dict['fee_rate'].default, buy_fee_rate=attr_dict['buy_fee_rate'].default, sell_fee_rate=attr_dict['sell_fee_rate'].default, fee_leg=attr_dict['fee_leg'].default, percent_amount_to_lock=attr_dict['percent_amount_to_lock'].default):
30    self.initial_funding = initial_funding
31    self.fee_rate = fee_rate
32    self.buy_fee_rate = buy_fee_rate
33    self.sell_fee_rate = sell_fee_rate
34    self.fee_leg = fee_leg
35    self.percent_amount_to_lock = percent_amount_to_lock
36    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepBaseConfig.

initial_funding: float | qtsurfer.api.client._generated.types.Unset
fee_rate: float | qtsurfer.api.client._generated.types.Unset
buy_fee_rate: float | qtsurfer.api.client._generated.types.Unset
sell_fee_rate: float | qtsurfer.api.client._generated.types.Unset
fee_leg: SweepBaseConfigFeeLeg | qtsurfer.api.client._generated.types.Unset
percent_amount_to_lock: float | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
36    def to_dict(self) -> dict[str, Any]:
37        initial_funding = self.initial_funding
38
39        fee_rate = self.fee_rate
40
41        buy_fee_rate = self.buy_fee_rate
42
43        sell_fee_rate = self.sell_fee_rate
44
45        fee_leg: str | Unset = UNSET
46        if not isinstance(self.fee_leg, Unset):
47            fee_leg = self.fee_leg.value
48
49        percent_amount_to_lock = self.percent_amount_to_lock
50
51        field_dict: dict[str, Any] = {}
52        field_dict.update(self.additional_properties)
53        field_dict.update({})
54        if initial_funding is not UNSET:
55            field_dict["initialFunding"] = initial_funding
56        if fee_rate is not UNSET:
57            field_dict["feeRate"] = fee_rate
58        if buy_fee_rate is not UNSET:
59            field_dict["buyFeeRate"] = buy_fee_rate
60        if sell_fee_rate is not UNSET:
61            field_dict["sellFeeRate"] = sell_fee_rate
62        if fee_leg is not UNSET:
63            field_dict["feeLeg"] = fee_leg
64        if percent_amount_to_lock is not UNSET:
65            field_dict["percentAmountToLock"] = percent_amount_to_lock
66
67        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
69    @classmethod
70    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
71        d = dict(src_dict)
72        initial_funding = d.pop("initialFunding", UNSET)
73
74        fee_rate = d.pop("feeRate", UNSET)
75
76        buy_fee_rate = d.pop("buyFeeRate", UNSET)
77
78        sell_fee_rate = d.pop("sellFeeRate", UNSET)
79
80        _fee_leg = d.pop("feeLeg", UNSET)
81        fee_leg: SweepBaseConfigFeeLeg | Unset
82        if isinstance(_fee_leg, Unset):
83            fee_leg = UNSET
84        else:
85            fee_leg = SweepBaseConfigFeeLeg(_fee_leg)
86
87        percent_amount_to_lock = d.pop("percentAmountToLock", UNSET)
88
89        sweep_base_config = cls(
90            initial_funding=initial_funding,
91            fee_rate=fee_rate,
92            buy_fee_rate=buy_fee_rate,
93            sell_fee_rate=sell_fee_rate,
94            fee_leg=fee_leg,
95            percent_amount_to_lock=percent_amount_to_lock,
96        )
97
98        sweep_base_config.additional_properties = d
99        return sweep_base_config
additional_keys: list[str]
101    @property
102    def additional_keys(self) -> list[str]:
103        return list(self.additional_properties.keys())
class SweepBaseConfigFeeLeg(builtins.str, enum.Enum):
 5class SweepBaseConfigFeeLeg(str, Enum):
 6    BASE = "BASE"
 7    QUOTE = "QUOTE"
 8    RECEIVED = "RECEIVED"
 9
10    def __str__(self) -> str:
11        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

BASE = <SweepBaseConfigFeeLeg.BASE: 'BASE'>
QUOTE = <SweepBaseConfigFeeLeg.QUOTE: 'QUOTE'>
RECEIVED = <SweepBaseConfigFeeLeg.RECEIVED: 'RECEIVED'>
class SweepHeatmap:
19@_attrs_define
20class SweepHeatmap:
21    """The surface for one pair of axes, with all others collapsed away.
22
23    Attributes:
24        param_a (str | Unset):
25        param_b (str | Unset):
26        cells (list[SweepHeatmapCell] | Unset):
27    """
28
29    param_a: str | Unset = UNSET
30    param_b: str | Unset = UNSET
31    cells: list[SweepHeatmapCell] | Unset = UNSET
32    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
33
34    def to_dict(self) -> dict[str, Any]:
35        param_a = self.param_a
36
37        param_b = self.param_b
38
39        cells: list[dict[str, Any]] | Unset = UNSET
40        if not isinstance(self.cells, Unset):
41            cells = []
42            for cells_item_data in self.cells:
43                cells_item = cells_item_data.to_dict()
44                cells.append(cells_item)
45
46        field_dict: dict[str, Any] = {}
47        field_dict.update(self.additional_properties)
48        field_dict.update({})
49        if param_a is not UNSET:
50            field_dict["paramA"] = param_a
51        if param_b is not UNSET:
52            field_dict["paramB"] = param_b
53        if cells is not UNSET:
54            field_dict["cells"] = cells
55
56        return field_dict
57
58    @classmethod
59    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
60        from ..models.sweep_heatmap_cell import SweepHeatmapCell
61
62        d = dict(src_dict)
63        param_a = d.pop("paramA", UNSET)
64
65        param_b = d.pop("paramB", UNSET)
66
67        _cells = d.pop("cells", UNSET)
68        cells: list[SweepHeatmapCell] | Unset = UNSET
69        if _cells is not UNSET:
70            cells = []
71            for cells_item_data in _cells:
72                cells_item = SweepHeatmapCell.from_dict(cells_item_data)
73
74                cells.append(cells_item)
75
76        sweep_heatmap = cls(
77            param_a=param_a,
78            param_b=param_b,
79            cells=cells,
80        )
81
82        sweep_heatmap.additional_properties = d
83        return sweep_heatmap
84
85    @property
86    def additional_keys(self) -> list[str]:
87        return list(self.additional_properties.keys())
88
89    def __getitem__(self, key: str) -> Any:
90        return self.additional_properties[key]
91
92    def __setitem__(self, key: str, value: Any) -> None:
93        self.additional_properties[key] = value
94
95    def __delitem__(self, key: str) -> None:
96        del self.additional_properties[key]
97
98    def __contains__(self, key: str) -> bool:
99        return key in self.additional_properties

The surface for one pair of axes, with all others collapsed away.

Attributes: param_a (str | Unset): param_b (str | Unset): cells (list[SweepHeatmapCell] | Unset):

SweepHeatmap( param_a: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, param_b: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, cells: list[SweepHeatmapCell] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
26def __init__(self, param_a=attr_dict['param_a'].default, param_b=attr_dict['param_b'].default, cells=attr_dict['cells'].default):
27    self.param_a = param_a
28    self.param_b = param_b
29    self.cells = cells
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepHeatmap.

param_a: str | qtsurfer.api.client._generated.types.Unset
param_b: str | qtsurfer.api.client._generated.types.Unset
cells: list[SweepHeatmapCell] | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
34    def to_dict(self) -> dict[str, Any]:
35        param_a = self.param_a
36
37        param_b = self.param_b
38
39        cells: list[dict[str, Any]] | Unset = UNSET
40        if not isinstance(self.cells, Unset):
41            cells = []
42            for cells_item_data in self.cells:
43                cells_item = cells_item_data.to_dict()
44                cells.append(cells_item)
45
46        field_dict: dict[str, Any] = {}
47        field_dict.update(self.additional_properties)
48        field_dict.update({})
49        if param_a is not UNSET:
50            field_dict["paramA"] = param_a
51        if param_b is not UNSET:
52            field_dict["paramB"] = param_b
53        if cells is not UNSET:
54            field_dict["cells"] = cells
55
56        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
58    @classmethod
59    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
60        from ..models.sweep_heatmap_cell import SweepHeatmapCell
61
62        d = dict(src_dict)
63        param_a = d.pop("paramA", UNSET)
64
65        param_b = d.pop("paramB", UNSET)
66
67        _cells = d.pop("cells", UNSET)
68        cells: list[SweepHeatmapCell] | Unset = UNSET
69        if _cells is not UNSET:
70            cells = []
71            for cells_item_data in _cells:
72                cells_item = SweepHeatmapCell.from_dict(cells_item_data)
73
74                cells.append(cells_item)
75
76        sweep_heatmap = cls(
77            param_a=param_a,
78            param_b=param_b,
79            cells=cells,
80        )
81
82        sweep_heatmap.additional_properties = d
83        return sweep_heatmap
additional_keys: list[str]
85    @property
86    def additional_keys(self) -> list[str]:
87        return list(self.additional_properties.keys())
class SweepHeatmapCell:
15@_attrs_define
16class SweepHeatmapCell:
17    """
18    Attributes:
19        value_a (Any | Unset):
20        value_b (Any | Unset):
21        count (int | Unset):
22        best (float | Unset):
23        mean (float | Unset):
24    """
25
26    value_a: Any | Unset = UNSET
27    value_b: Any | Unset = UNSET
28    count: int | Unset = UNSET
29    best: float | Unset = UNSET
30    mean: float | Unset = UNSET
31    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
32
33    def to_dict(self) -> dict[str, Any]:
34        value_a = self.value_a
35
36        value_b = self.value_b
37
38        count = self.count
39
40        best = self.best
41
42        mean = self.mean
43
44        field_dict: dict[str, Any] = {}
45        field_dict.update(self.additional_properties)
46        field_dict.update({})
47        if value_a is not UNSET:
48            field_dict["valueA"] = value_a
49        if value_b is not UNSET:
50            field_dict["valueB"] = value_b
51        if count is not UNSET:
52            field_dict["count"] = count
53        if best is not UNSET:
54            field_dict["best"] = best
55        if mean is not UNSET:
56            field_dict["mean"] = mean
57
58        return field_dict
59
60    @classmethod
61    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
62        d = dict(src_dict)
63        value_a = d.pop("valueA", UNSET)
64
65        value_b = d.pop("valueB", UNSET)
66
67        count = d.pop("count", UNSET)
68
69        best = d.pop("best", UNSET)
70
71        mean = d.pop("mean", UNSET)
72
73        sweep_heatmap_cell = cls(
74            value_a=value_a,
75            value_b=value_b,
76            count=count,
77            best=best,
78            mean=mean,
79        )
80
81        sweep_heatmap_cell.additional_properties = d
82        return sweep_heatmap_cell
83
84    @property
85    def additional_keys(self) -> list[str]:
86        return list(self.additional_properties.keys())
87
88    def __getitem__(self, key: str) -> Any:
89        return self.additional_properties[key]
90
91    def __setitem__(self, key: str, value: Any) -> None:
92        self.additional_properties[key] = value
93
94    def __delitem__(self, key: str) -> None:
95        del self.additional_properties[key]
96
97    def __contains__(self, key: str) -> bool:
98        return key in self.additional_properties

Attributes: value_a (Any | Unset): value_b (Any | Unset): count (int | Unset): best (float | Unset): mean (float | Unset):

SweepHeatmapCell( value_a: typing.Any | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, value_b: typing.Any | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, count: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, best: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, mean: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
28def __init__(self, value_a=attr_dict['value_a'].default, value_b=attr_dict['value_b'].default, count=attr_dict['count'].default, best=attr_dict['best'].default, mean=attr_dict['mean'].default):
29    self.value_a = value_a
30    self.value_b = value_b
31    self.count = count
32    self.best = best
33    self.mean = mean
34    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepHeatmapCell.

value_a: typing.Any | qtsurfer.api.client._generated.types.Unset
value_b: typing.Any | qtsurfer.api.client._generated.types.Unset
count: int | qtsurfer.api.client._generated.types.Unset
best: float | qtsurfer.api.client._generated.types.Unset
mean: float | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
33    def to_dict(self) -> dict[str, Any]:
34        value_a = self.value_a
35
36        value_b = self.value_b
37
38        count = self.count
39
40        best = self.best
41
42        mean = self.mean
43
44        field_dict: dict[str, Any] = {}
45        field_dict.update(self.additional_properties)
46        field_dict.update({})
47        if value_a is not UNSET:
48            field_dict["valueA"] = value_a
49        if value_b is not UNSET:
50            field_dict["valueB"] = value_b
51        if count is not UNSET:
52            field_dict["count"] = count
53        if best is not UNSET:
54            field_dict["best"] = best
55        if mean is not UNSET:
56            field_dict["mean"] = mean
57
58        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
60    @classmethod
61    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
62        d = dict(src_dict)
63        value_a = d.pop("valueA", UNSET)
64
65        value_b = d.pop("valueB", UNSET)
66
67        count = d.pop("count", UNSET)
68
69        best = d.pop("best", UNSET)
70
71        mean = d.pop("mean", UNSET)
72
73        sweep_heatmap_cell = cls(
74            value_a=value_a,
75            value_b=value_b,
76            count=count,
77            best=best,
78            mean=mean,
79        )
80
81        sweep_heatmap_cell.additional_properties = d
82        return sweep_heatmap_cell
additional_keys: list[str]
84    @property
85    def additional_keys(self) -> list[str]:
86        return list(self.additional_properties.keys())
class SweepMarginal:
19@_attrs_define
20class SweepMarginal:
21    """One axis, with every other axis collapsed away.
22
23    Attributes:
24        param (str | Unset):
25        points (list[SweepMarginalPoint] | Unset):
26    """
27
28    param: str | Unset = UNSET
29    points: list[SweepMarginalPoint] | Unset = UNSET
30    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
31
32    def to_dict(self) -> dict[str, Any]:
33        param = self.param
34
35        points: list[dict[str, Any]] | Unset = UNSET
36        if not isinstance(self.points, Unset):
37            points = []
38            for points_item_data in self.points:
39                points_item = points_item_data.to_dict()
40                points.append(points_item)
41
42        field_dict: dict[str, Any] = {}
43        field_dict.update(self.additional_properties)
44        field_dict.update({})
45        if param is not UNSET:
46            field_dict["param"] = param
47        if points is not UNSET:
48            field_dict["points"] = points
49
50        return field_dict
51
52    @classmethod
53    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
54        from ..models.sweep_marginal_point import SweepMarginalPoint
55
56        d = dict(src_dict)
57        param = d.pop("param", UNSET)
58
59        _points = d.pop("points", UNSET)
60        points: list[SweepMarginalPoint] | Unset = UNSET
61        if _points is not UNSET:
62            points = []
63            for points_item_data in _points:
64                points_item = SweepMarginalPoint.from_dict(points_item_data)
65
66                points.append(points_item)
67
68        sweep_marginal = cls(
69            param=param,
70            points=points,
71        )
72
73        sweep_marginal.additional_properties = d
74        return sweep_marginal
75
76    @property
77    def additional_keys(self) -> list[str]:
78        return list(self.additional_properties.keys())
79
80    def __getitem__(self, key: str) -> Any:
81        return self.additional_properties[key]
82
83    def __setitem__(self, key: str, value: Any) -> None:
84        self.additional_properties[key] = value
85
86    def __delitem__(self, key: str) -> None:
87        del self.additional_properties[key]
88
89    def __contains__(self, key: str) -> bool:
90        return key in self.additional_properties

One axis, with every other axis collapsed away.

Attributes: param (str | Unset): points (list[SweepMarginalPoint] | Unset):

SweepMarginal( param: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, points: list[SweepMarginalPoint] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
25def __init__(self, param=attr_dict['param'].default, points=attr_dict['points'].default):
26    self.param = param
27    self.points = points
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepMarginal.

param: str | qtsurfer.api.client._generated.types.Unset
points: list[SweepMarginalPoint] | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
32    def to_dict(self) -> dict[str, Any]:
33        param = self.param
34
35        points: list[dict[str, Any]] | Unset = UNSET
36        if not isinstance(self.points, Unset):
37            points = []
38            for points_item_data in self.points:
39                points_item = points_item_data.to_dict()
40                points.append(points_item)
41
42        field_dict: dict[str, Any] = {}
43        field_dict.update(self.additional_properties)
44        field_dict.update({})
45        if param is not UNSET:
46            field_dict["param"] = param
47        if points is not UNSET:
48            field_dict["points"] = points
49
50        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
52    @classmethod
53    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
54        from ..models.sweep_marginal_point import SweepMarginalPoint
55
56        d = dict(src_dict)
57        param = d.pop("param", UNSET)
58
59        _points = d.pop("points", UNSET)
60        points: list[SweepMarginalPoint] | Unset = UNSET
61        if _points is not UNSET:
62            points = []
63            for points_item_data in _points:
64                points_item = SweepMarginalPoint.from_dict(points_item_data)
65
66                points.append(points_item)
67
68        sweep_marginal = cls(
69            param=param,
70            points=points,
71        )
72
73        sweep_marginal.additional_properties = d
74        return sweep_marginal
additional_keys: list[str]
76    @property
77    def additional_keys(self) -> list[str]:
78        return list(self.additional_properties.keys())
class SweepMarginalPoint:
 15@_attrs_define
 16class SweepMarginalPoint:
 17    """How the objective behaved at one value of one axis. `best` and `mean` disagreeing is informative rather than noise:
 18    a high `best` with a poor `mean` marks a value that only works alongside particular settings of the other axes.
 19
 20        Attributes:
 21            value (Any | Unset): The axis value, as it appears in a run's parameters.
 22            count (int | Unset): Non-aborted runs that used this value.
 23            best (float | Unset):
 24            mean (float | Unset):
 25            worst (float | Unset):
 26    """
 27
 28    value: Any | Unset = UNSET
 29    count: int | Unset = UNSET
 30    best: float | Unset = UNSET
 31    mean: float | Unset = UNSET
 32    worst: float | Unset = UNSET
 33    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 34
 35    def to_dict(self) -> dict[str, Any]:
 36        value = self.value
 37
 38        count = self.count
 39
 40        best = self.best
 41
 42        mean = self.mean
 43
 44        worst = self.worst
 45
 46        field_dict: dict[str, Any] = {}
 47        field_dict.update(self.additional_properties)
 48        field_dict.update({})
 49        if value is not UNSET:
 50            field_dict["value"] = value
 51        if count is not UNSET:
 52            field_dict["count"] = count
 53        if best is not UNSET:
 54            field_dict["best"] = best
 55        if mean is not UNSET:
 56            field_dict["mean"] = mean
 57        if worst is not UNSET:
 58            field_dict["worst"] = worst
 59
 60        return field_dict
 61
 62    @classmethod
 63    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 64        d = dict(src_dict)
 65        value = d.pop("value", UNSET)
 66
 67        count = d.pop("count", UNSET)
 68
 69        best = d.pop("best", UNSET)
 70
 71        mean = d.pop("mean", UNSET)
 72
 73        worst = d.pop("worst", UNSET)
 74
 75        sweep_marginal_point = cls(
 76            value=value,
 77            count=count,
 78            best=best,
 79            mean=mean,
 80            worst=worst,
 81        )
 82
 83        sweep_marginal_point.additional_properties = d
 84        return sweep_marginal_point
 85
 86    @property
 87    def additional_keys(self) -> list[str]:
 88        return list(self.additional_properties.keys())
 89
 90    def __getitem__(self, key: str) -> Any:
 91        return self.additional_properties[key]
 92
 93    def __setitem__(self, key: str, value: Any) -> None:
 94        self.additional_properties[key] = value
 95
 96    def __delitem__(self, key: str) -> None:
 97        del self.additional_properties[key]
 98
 99    def __contains__(self, key: str) -> bool:
100        return key in self.additional_properties

How the objective behaved at one value of one axis. best and mean disagreeing is informative rather than noise: a high best with a poor mean marks a value that only works alongside particular settings of the other axes.

Attributes:
    value (Any | Unset): The axis value, as it appears in a run's parameters.
    count (int | Unset): Non-aborted runs that used this value.
    best (float | Unset):
    mean (float | Unset):
    worst (float | Unset):
SweepMarginalPoint( value: typing.Any | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, count: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, best: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, mean: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, worst: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
28def __init__(self, value=attr_dict['value'].default, count=attr_dict['count'].default, best=attr_dict['best'].default, mean=attr_dict['mean'].default, worst=attr_dict['worst'].default):
29    self.value = value
30    self.count = count
31    self.best = best
32    self.mean = mean
33    self.worst = worst
34    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepMarginalPoint.

value: typing.Any | qtsurfer.api.client._generated.types.Unset
count: int | qtsurfer.api.client._generated.types.Unset
best: float | qtsurfer.api.client._generated.types.Unset
mean: float | qtsurfer.api.client._generated.types.Unset
worst: float | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
35    def to_dict(self) -> dict[str, Any]:
36        value = self.value
37
38        count = self.count
39
40        best = self.best
41
42        mean = self.mean
43
44        worst = self.worst
45
46        field_dict: dict[str, Any] = {}
47        field_dict.update(self.additional_properties)
48        field_dict.update({})
49        if value is not UNSET:
50            field_dict["value"] = value
51        if count is not UNSET:
52            field_dict["count"] = count
53        if best is not UNSET:
54            field_dict["best"] = best
55        if mean is not UNSET:
56            field_dict["mean"] = mean
57        if worst is not UNSET:
58            field_dict["worst"] = worst
59
60        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
62    @classmethod
63    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
64        d = dict(src_dict)
65        value = d.pop("value", UNSET)
66
67        count = d.pop("count", UNSET)
68
69        best = d.pop("best", UNSET)
70
71        mean = d.pop("mean", UNSET)
72
73        worst = d.pop("worst", UNSET)
74
75        sweep_marginal_point = cls(
76            value=value,
77            count=count,
78            best=best,
79            mean=mean,
80            worst=worst,
81        )
82
83        sweep_marginal_point.additional_properties = d
84        return sweep_marginal_point
additional_keys: list[str]
86    @property
87    def additional_keys(self) -> list[str]:
88        return list(self.additional_properties.keys())
class SweepProgress:
 15@_attrs_define
 16class SweepProgress:
 17    """How far along a sweep is, and — when the sweep is still running — enough to tell a healthy one from a stuck one. The
 18    counts partition the shards (or, for a walk-forward sweep, the folds): every unit is either finished, failed,
 19    waiting to be retried, or not yet started.
 20
 21        Attributes:
 22            done (int):
 23            total (int):
 24            aborted (int): Individual runs that executed and aborted. A row-level count: a shard that fails before producing
 25                any rows leaves this at 0, which is why `failedShards` exists alongside it.
 26            shard_count (int):
 27            pending_shards (int):
 28            failed_shards (int): Shards (or folds) that failed and will not be retried. Distinct from `aborted`: this counts
 29                whole units that never reported, not runs that ran badly.
 30            retrying (int): Units whose last attempt failed on something transient — an I/O error, a worker that died mid-
 31                read — and which are queued to be attempted again. Not counted as failures, because they have not failed yet; a
 32                sweep with a non-zero value here is still expected to complete.
 33            not_started (int): Units that have not reported anything yet. Covers both work still queued behind other work
 34                and work claimed by a worker that stopped before it began, which is why a sweep with a persistent value here and
 35                a rising `stalledSeconds` is worth looking at.
 36            stalled_seconds (int | Unset): Seconds since anything last advanced. Omitted on a finished sweep, where it would
 37                only measure how long ago it finished, and on sweeps submitted before this field existed.
 38            eta_seconds (int | Unset): Rough seconds remaining, extrapolated from the rate observed so far and assuming
 39                nothing else competes for workers. Runs conservative in practice — it has measured 2–5× long when a sweep spent
 40                part of its life waiting to be retried, since that wait dilutes the observed rate. **Omitted, never zero, when
 41                it cannot be computed**: a sweep with nothing finished yet has no rate to extrapolate from, and a zero would
 42                read as "about to finish". Excludes queue wait entirely; `retrying` and `stalledSeconds` are where that shows
 43                up.
 44    """
 45
 46    done: int
 47    total: int
 48    aborted: int
 49    shard_count: int
 50    pending_shards: int
 51    failed_shards: int
 52    retrying: int
 53    not_started: int
 54    stalled_seconds: int | Unset = UNSET
 55    eta_seconds: int | Unset = UNSET
 56    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 57
 58    def to_dict(self) -> dict[str, Any]:
 59        done = self.done
 60
 61        total = self.total
 62
 63        aborted = self.aborted
 64
 65        shard_count = self.shard_count
 66
 67        pending_shards = self.pending_shards
 68
 69        failed_shards = self.failed_shards
 70
 71        retrying = self.retrying
 72
 73        not_started = self.not_started
 74
 75        stalled_seconds = self.stalled_seconds
 76
 77        eta_seconds = self.eta_seconds
 78
 79        field_dict: dict[str, Any] = {}
 80        field_dict.update(self.additional_properties)
 81        field_dict.update(
 82            {
 83                "done": done,
 84                "total": total,
 85                "aborted": aborted,
 86                "shardCount": shard_count,
 87                "pendingShards": pending_shards,
 88                "failedShards": failed_shards,
 89                "retrying": retrying,
 90                "notStarted": not_started,
 91            }
 92        )
 93        if stalled_seconds is not UNSET:
 94            field_dict["stalledSeconds"] = stalled_seconds
 95        if eta_seconds is not UNSET:
 96            field_dict["etaSeconds"] = eta_seconds
 97
 98        return field_dict
 99
100    @classmethod
101    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
102        d = dict(src_dict)
103        done = d.pop("done")
104
105        total = d.pop("total")
106
107        aborted = d.pop("aborted")
108
109        shard_count = d.pop("shardCount")
110
111        pending_shards = d.pop("pendingShards")
112
113        failed_shards = d.pop("failedShards")
114
115        retrying = d.pop("retrying")
116
117        not_started = d.pop("notStarted")
118
119        stalled_seconds = d.pop("stalledSeconds", UNSET)
120
121        eta_seconds = d.pop("etaSeconds", UNSET)
122
123        sweep_progress = cls(
124            done=done,
125            total=total,
126            aborted=aborted,
127            shard_count=shard_count,
128            pending_shards=pending_shards,
129            failed_shards=failed_shards,
130            retrying=retrying,
131            not_started=not_started,
132            stalled_seconds=stalled_seconds,
133            eta_seconds=eta_seconds,
134        )
135
136        sweep_progress.additional_properties = d
137        return sweep_progress
138
139    @property
140    def additional_keys(self) -> list[str]:
141        return list(self.additional_properties.keys())
142
143    def __getitem__(self, key: str) -> Any:
144        return self.additional_properties[key]
145
146    def __setitem__(self, key: str, value: Any) -> None:
147        self.additional_properties[key] = value
148
149    def __delitem__(self, key: str) -> None:
150        del self.additional_properties[key]
151
152    def __contains__(self, key: str) -> bool:
153        return key in self.additional_properties

How far along a sweep is, and — when the sweep is still running — enough to tell a healthy one from a stuck one. The counts partition the shards (or, for a walk-forward sweep, the folds): every unit is either finished, failed, waiting to be retried, or not yet started.

Attributes:
    done (int):
    total (int):
    aborted (int): Individual runs that executed and aborted. A row-level count: a shard that fails before producing
        any rows leaves this at 0, which is why `failedShards` exists alongside it.
    shard_count (int):
    pending_shards (int):
    failed_shards (int): Shards (or folds) that failed and will not be retried. Distinct from `aborted`: this counts
        whole units that never reported, not runs that ran badly.
    retrying (int): Units whose last attempt failed on something transient — an I/O error, a worker that died mid-
        read — and which are queued to be attempted again. Not counted as failures, because they have not failed yet; a
        sweep with a non-zero value here is still expected to complete.
    not_started (int): Units that have not reported anything yet. Covers both work still queued behind other work
        and work claimed by a worker that stopped before it began, which is why a sweep with a persistent value here and
        a rising `stalledSeconds` is worth looking at.
    stalled_seconds (int | Unset): Seconds since anything last advanced. Omitted on a finished sweep, where it would
        only measure how long ago it finished, and on sweeps submitted before this field existed.
    eta_seconds (int | Unset): Rough seconds remaining, extrapolated from the rate observed so far and assuming
        nothing else competes for workers. Runs conservative in practice — it has measured 2–5× long when a sweep spent
        part of its life waiting to be retried, since that wait dilutes the observed rate. **Omitted, never zero, when
        it cannot be computed**: a sweep with nothing finished yet has no rate to extrapolate from, and a zero would
        read as "about to finish". Excludes queue wait entirely; `retrying` and `stalledSeconds` are where that shows
        up.
SweepProgress( done: int, total: int, aborted: int, shard_count: int, pending_shards: int, failed_shards: int, retrying: int, not_started: int, stalled_seconds: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, eta_seconds: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
33def __init__(self, done, total, aborted, shard_count, pending_shards, failed_shards, retrying, not_started, stalled_seconds=attr_dict['stalled_seconds'].default, eta_seconds=attr_dict['eta_seconds'].default):
34    self.done = done
35    self.total = total
36    self.aborted = aborted
37    self.shard_count = shard_count
38    self.pending_shards = pending_shards
39    self.failed_shards = failed_shards
40    self.retrying = retrying
41    self.not_started = not_started
42    self.stalled_seconds = stalled_seconds
43    self.eta_seconds = eta_seconds
44    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepProgress.

done: int
total: int
aborted: int
shard_count: int
pending_shards: int
failed_shards: int
retrying: int
not_started: int
stalled_seconds: int | qtsurfer.api.client._generated.types.Unset
eta_seconds: int | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
58    def to_dict(self) -> dict[str, Any]:
59        done = self.done
60
61        total = self.total
62
63        aborted = self.aborted
64
65        shard_count = self.shard_count
66
67        pending_shards = self.pending_shards
68
69        failed_shards = self.failed_shards
70
71        retrying = self.retrying
72
73        not_started = self.not_started
74
75        stalled_seconds = self.stalled_seconds
76
77        eta_seconds = self.eta_seconds
78
79        field_dict: dict[str, Any] = {}
80        field_dict.update(self.additional_properties)
81        field_dict.update(
82            {
83                "done": done,
84                "total": total,
85                "aborted": aborted,
86                "shardCount": shard_count,
87                "pendingShards": pending_shards,
88                "failedShards": failed_shards,
89                "retrying": retrying,
90                "notStarted": not_started,
91            }
92        )
93        if stalled_seconds is not UNSET:
94            field_dict["stalledSeconds"] = stalled_seconds
95        if eta_seconds is not UNSET:
96            field_dict["etaSeconds"] = eta_seconds
97
98        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
100    @classmethod
101    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
102        d = dict(src_dict)
103        done = d.pop("done")
104
105        total = d.pop("total")
106
107        aborted = d.pop("aborted")
108
109        shard_count = d.pop("shardCount")
110
111        pending_shards = d.pop("pendingShards")
112
113        failed_shards = d.pop("failedShards")
114
115        retrying = d.pop("retrying")
116
117        not_started = d.pop("notStarted")
118
119        stalled_seconds = d.pop("stalledSeconds", UNSET)
120
121        eta_seconds = d.pop("etaSeconds", UNSET)
122
123        sweep_progress = cls(
124            done=done,
125            total=total,
126            aborted=aborted,
127            shard_count=shard_count,
128            pending_shards=pending_shards,
129            failed_shards=failed_shards,
130            retrying=retrying,
131            not_started=not_started,
132            stalled_seconds=stalled_seconds,
133            eta_seconds=eta_seconds,
134        )
135
136        sweep_progress.additional_properties = d
137        return sweep_progress
additional_keys: list[str]
139    @property
140    def additional_keys(self) -> list[str]:
141        return list(self.additional_properties.keys())
class SweepRunRow:
 20@_attrs_define
 21class SweepRunRow:
 22    """
 23    Attributes:
 24        run_ix (int): Deterministic zero-based expansion index, stable across shards and ranking.
 25        params (SweepRunRowParams):
 26        sharpe (float):
 27        sortino (float):
 28        pnl (float): Absolute net PnL in the output currency.
 29        pnl_pct (float):
 30        cagr (float):
 31        max_dd_pct (float):
 32        trades (int):
 33        win_rate (float):
 34        below_trade_floor (bool):
 35        aborted (bool):
 36        runtime_ms (int):
 37        rank (int | Unset): Present only in the `ranked` view.
 38        plateau_score (float | Unset): The objective of the worst run in this point's immediate neighbourhood — how well
 39            the region around it holds up, not how well it scored itself. Present only in the `ranked` view when plateau
 40            ranking applied. Always read together with `neighbourCount`.
 41        neighbour_count (int | Unset): How many neighbouring parameter points backed the `plateauScore`. Zero means the
 42            point had no neighbours in the grid, so its score is unevidenced rather than confirmed — the value alone cannot
 43            be distinguished from a genuinely robust one.
 44        deflated_sharpe (float | Unset): Probability that this run's Sharpe reflects real edge rather than the best draw
 45            from however many parameter vectors were tried. Above ~0.95 the result survives the multiple-testing correction;
 46            near 0.5 or below it is indistinguishable from the best of a pile of coin flips. Absent on aborted runs, and on
 47            sweeps with too few trials to establish any dispersion to deflate against.
 48        equity_curve (EquityCurveResult | Unset): An equity curve, shaped per `meta.outMode`: `points` when `ARRAY`,
 49            `timestamps` + `equities` (parallel arrays) when `SHORT`. Used identically wherever a curve is returned — a
 50            plain backtest's inline `equityCurve` and a sweep row's `equityCurve` are the same type. `url` is present
 51            *instead of* any points when the curve is served by pointer rather than inline (a sweep row's top-N winners
 52            only): `GET` it separately to fetch this exact same shape with the points populated.
 53    """
 54
 55    run_ix: int
 56    params: SweepRunRowParams
 57    sharpe: float
 58    sortino: float
 59    pnl: float
 60    pnl_pct: float
 61    cagr: float
 62    max_dd_pct: float
 63    trades: int
 64    win_rate: float
 65    below_trade_floor: bool
 66    aborted: bool
 67    runtime_ms: int
 68    rank: int | Unset = UNSET
 69    plateau_score: float | Unset = UNSET
 70    neighbour_count: int | Unset = UNSET
 71    deflated_sharpe: float | Unset = UNSET
 72    equity_curve: EquityCurveResult | Unset = UNSET
 73    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 74
 75    def to_dict(self) -> dict[str, Any]:
 76        run_ix = self.run_ix
 77
 78        params = self.params.to_dict()
 79
 80        sharpe = self.sharpe
 81
 82        sortino = self.sortino
 83
 84        pnl = self.pnl
 85
 86        pnl_pct = self.pnl_pct
 87
 88        cagr = self.cagr
 89
 90        max_dd_pct = self.max_dd_pct
 91
 92        trades = self.trades
 93
 94        win_rate = self.win_rate
 95
 96        below_trade_floor = self.below_trade_floor
 97
 98        aborted = self.aborted
 99
100        runtime_ms = self.runtime_ms
101
102        rank = self.rank
103
104        plateau_score = self.plateau_score
105
106        neighbour_count = self.neighbour_count
107
108        deflated_sharpe = self.deflated_sharpe
109
110        equity_curve: dict[str, Any] | Unset = UNSET
111        if not isinstance(self.equity_curve, Unset):
112            equity_curve = self.equity_curve.to_dict()
113
114        field_dict: dict[str, Any] = {}
115        field_dict.update(self.additional_properties)
116        field_dict.update(
117            {
118                "runIx": run_ix,
119                "params": params,
120                "sharpe": sharpe,
121                "sortino": sortino,
122                "pnl": pnl,
123                "pnlPct": pnl_pct,
124                "cagr": cagr,
125                "maxDdPct": max_dd_pct,
126                "trades": trades,
127                "winRate": win_rate,
128                "belowTradeFloor": below_trade_floor,
129                "aborted": aborted,
130                "runtimeMs": runtime_ms,
131            }
132        )
133        if rank is not UNSET:
134            field_dict["rank"] = rank
135        if plateau_score is not UNSET:
136            field_dict["plateauScore"] = plateau_score
137        if neighbour_count is not UNSET:
138            field_dict["neighbourCount"] = neighbour_count
139        if deflated_sharpe is not UNSET:
140            field_dict["deflatedSharpe"] = deflated_sharpe
141        if equity_curve is not UNSET:
142            field_dict["equityCurve"] = equity_curve
143
144        return field_dict
145
146    @classmethod
147    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
148        from ..models.equity_curve_result import EquityCurveResult
149        from ..models.sweep_run_row_params import SweepRunRowParams
150
151        d = dict(src_dict)
152        run_ix = d.pop("runIx")
153
154        params = SweepRunRowParams.from_dict(d.pop("params"))
155
156        sharpe = d.pop("sharpe")
157
158        sortino = d.pop("sortino")
159
160        pnl = d.pop("pnl")
161
162        pnl_pct = d.pop("pnlPct")
163
164        cagr = d.pop("cagr")
165
166        max_dd_pct = d.pop("maxDdPct")
167
168        trades = d.pop("trades")
169
170        win_rate = d.pop("winRate")
171
172        below_trade_floor = d.pop("belowTradeFloor")
173
174        aborted = d.pop("aborted")
175
176        runtime_ms = d.pop("runtimeMs")
177
178        rank = d.pop("rank", UNSET)
179
180        plateau_score = d.pop("plateauScore", UNSET)
181
182        neighbour_count = d.pop("neighbourCount", UNSET)
183
184        deflated_sharpe = d.pop("deflatedSharpe", UNSET)
185
186        _equity_curve = d.pop("equityCurve", UNSET)
187        equity_curve: EquityCurveResult | Unset
188        if isinstance(_equity_curve, Unset):
189            equity_curve = UNSET
190        else:
191            equity_curve = EquityCurveResult.from_dict(_equity_curve)
192
193        sweep_run_row = cls(
194            run_ix=run_ix,
195            params=params,
196            sharpe=sharpe,
197            sortino=sortino,
198            pnl=pnl,
199            pnl_pct=pnl_pct,
200            cagr=cagr,
201            max_dd_pct=max_dd_pct,
202            trades=trades,
203            win_rate=win_rate,
204            below_trade_floor=below_trade_floor,
205            aborted=aborted,
206            runtime_ms=runtime_ms,
207            rank=rank,
208            plateau_score=plateau_score,
209            neighbour_count=neighbour_count,
210            deflated_sharpe=deflated_sharpe,
211            equity_curve=equity_curve,
212        )
213
214        sweep_run_row.additional_properties = d
215        return sweep_run_row
216
217    @property
218    def additional_keys(self) -> list[str]:
219        return list(self.additional_properties.keys())
220
221    def __getitem__(self, key: str) -> Any:
222        return self.additional_properties[key]
223
224    def __setitem__(self, key: str, value: Any) -> None:
225        self.additional_properties[key] = value
226
227    def __delitem__(self, key: str) -> None:
228        del self.additional_properties[key]
229
230    def __contains__(self, key: str) -> bool:
231        return key in self.additional_properties

Attributes: run_ix (int): Deterministic zero-based expansion index, stable across shards and ranking. params (SweepRunRowParams): sharpe (float): sortino (float): pnl (float): Absolute net PnL in the output currency. pnl_pct (float): cagr (float): max_dd_pct (float): trades (int): win_rate (float): below_trade_floor (bool): aborted (bool): runtime_ms (int): rank (int | Unset): Present only in the ranked view. plateau_score (float | Unset): The objective of the worst run in this point's immediate neighbourhood — how well the region around it holds up, not how well it scored itself. Present only in the ranked view when plateau ranking applied. Always read together with neighbourCount. neighbour_count (int | Unset): How many neighbouring parameter points backed the plateauScore. Zero means the point had no neighbours in the grid, so its score is unevidenced rather than confirmed — the value alone cannot be distinguished from a genuinely robust one. deflated_sharpe (float | Unset): Probability that this run's Sharpe reflects real edge rather than the best draw from however many parameter vectors were tried. Above ~0.95 the result survives the multiple-testing correction; near 0.5 or below it is indistinguishable from the best of a pile of coin flips. Absent on aborted runs, and on sweeps with too few trials to establish any dispersion to deflate against. equity_curve (EquityCurveResult | Unset): An equity curve, shaped per meta.outMode: points when ARRAY, timestamps + equities (parallel arrays) when SHORT. Used identically wherever a curve is returned — a plain backtest's inline equityCurve and a sweep row's equityCurve are the same type. url is present instead of any points when the curve is served by pointer rather than inline (a sweep row's top-N winners only): GET it separately to fetch this exact same shape with the points populated.

SweepRunRow( run_ix: int, params: SweepRunRowParams, sharpe: float, sortino: float, pnl: float, pnl_pct: float, cagr: float, max_dd_pct: float, trades: int, win_rate: float, below_trade_floor: bool, aborted: bool, runtime_ms: int, rank: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, plateau_score: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, neighbour_count: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, deflated_sharpe: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, equity_curve: EquityCurveResult | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
41def __init__(self, run_ix, params, sharpe, sortino, pnl, pnl_pct, cagr, max_dd_pct, trades, win_rate, below_trade_floor, aborted, runtime_ms, rank=attr_dict['rank'].default, plateau_score=attr_dict['plateau_score'].default, neighbour_count=attr_dict['neighbour_count'].default, deflated_sharpe=attr_dict['deflated_sharpe'].default, equity_curve=attr_dict['equity_curve'].default):
42    self.run_ix = run_ix
43    self.params = params
44    self.sharpe = sharpe
45    self.sortino = sortino
46    self.pnl = pnl
47    self.pnl_pct = pnl_pct
48    self.cagr = cagr
49    self.max_dd_pct = max_dd_pct
50    self.trades = trades
51    self.win_rate = win_rate
52    self.below_trade_floor = below_trade_floor
53    self.aborted = aborted
54    self.runtime_ms = runtime_ms
55    self.rank = rank
56    self.plateau_score = plateau_score
57    self.neighbour_count = neighbour_count
58    self.deflated_sharpe = deflated_sharpe
59    self.equity_curve = equity_curve
60    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepRunRow.

run_ix: int
sharpe: float
sortino: float
pnl: float
pnl_pct: float
cagr: float
max_dd_pct: float
trades: int
win_rate: float
below_trade_floor: bool
aborted: bool
runtime_ms: int
rank: int | qtsurfer.api.client._generated.types.Unset
plateau_score: float | qtsurfer.api.client._generated.types.Unset
neighbour_count: int | qtsurfer.api.client._generated.types.Unset
deflated_sharpe: float | qtsurfer.api.client._generated.types.Unset
equity_curve: EquityCurveResult | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
 75    def to_dict(self) -> dict[str, Any]:
 76        run_ix = self.run_ix
 77
 78        params = self.params.to_dict()
 79
 80        sharpe = self.sharpe
 81
 82        sortino = self.sortino
 83
 84        pnl = self.pnl
 85
 86        pnl_pct = self.pnl_pct
 87
 88        cagr = self.cagr
 89
 90        max_dd_pct = self.max_dd_pct
 91
 92        trades = self.trades
 93
 94        win_rate = self.win_rate
 95
 96        below_trade_floor = self.below_trade_floor
 97
 98        aborted = self.aborted
 99
100        runtime_ms = self.runtime_ms
101
102        rank = self.rank
103
104        plateau_score = self.plateau_score
105
106        neighbour_count = self.neighbour_count
107
108        deflated_sharpe = self.deflated_sharpe
109
110        equity_curve: dict[str, Any] | Unset = UNSET
111        if not isinstance(self.equity_curve, Unset):
112            equity_curve = self.equity_curve.to_dict()
113
114        field_dict: dict[str, Any] = {}
115        field_dict.update(self.additional_properties)
116        field_dict.update(
117            {
118                "runIx": run_ix,
119                "params": params,
120                "sharpe": sharpe,
121                "sortino": sortino,
122                "pnl": pnl,
123                "pnlPct": pnl_pct,
124                "cagr": cagr,
125                "maxDdPct": max_dd_pct,
126                "trades": trades,
127                "winRate": win_rate,
128                "belowTradeFloor": below_trade_floor,
129                "aborted": aborted,
130                "runtimeMs": runtime_ms,
131            }
132        )
133        if rank is not UNSET:
134            field_dict["rank"] = rank
135        if plateau_score is not UNSET:
136            field_dict["plateauScore"] = plateau_score
137        if neighbour_count is not UNSET:
138            field_dict["neighbourCount"] = neighbour_count
139        if deflated_sharpe is not UNSET:
140            field_dict["deflatedSharpe"] = deflated_sharpe
141        if equity_curve is not UNSET:
142            field_dict["equityCurve"] = equity_curve
143
144        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
146    @classmethod
147    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
148        from ..models.equity_curve_result import EquityCurveResult
149        from ..models.sweep_run_row_params import SweepRunRowParams
150
151        d = dict(src_dict)
152        run_ix = d.pop("runIx")
153
154        params = SweepRunRowParams.from_dict(d.pop("params"))
155
156        sharpe = d.pop("sharpe")
157
158        sortino = d.pop("sortino")
159
160        pnl = d.pop("pnl")
161
162        pnl_pct = d.pop("pnlPct")
163
164        cagr = d.pop("cagr")
165
166        max_dd_pct = d.pop("maxDdPct")
167
168        trades = d.pop("trades")
169
170        win_rate = d.pop("winRate")
171
172        below_trade_floor = d.pop("belowTradeFloor")
173
174        aborted = d.pop("aborted")
175
176        runtime_ms = d.pop("runtimeMs")
177
178        rank = d.pop("rank", UNSET)
179
180        plateau_score = d.pop("plateauScore", UNSET)
181
182        neighbour_count = d.pop("neighbourCount", UNSET)
183
184        deflated_sharpe = d.pop("deflatedSharpe", UNSET)
185
186        _equity_curve = d.pop("equityCurve", UNSET)
187        equity_curve: EquityCurveResult | Unset
188        if isinstance(_equity_curve, Unset):
189            equity_curve = UNSET
190        else:
191            equity_curve = EquityCurveResult.from_dict(_equity_curve)
192
193        sweep_run_row = cls(
194            run_ix=run_ix,
195            params=params,
196            sharpe=sharpe,
197            sortino=sortino,
198            pnl=pnl,
199            pnl_pct=pnl_pct,
200            cagr=cagr,
201            max_dd_pct=max_dd_pct,
202            trades=trades,
203            win_rate=win_rate,
204            below_trade_floor=below_trade_floor,
205            aborted=aborted,
206            runtime_ms=runtime_ms,
207            rank=rank,
208            plateau_score=plateau_score,
209            neighbour_count=neighbour_count,
210            deflated_sharpe=deflated_sharpe,
211            equity_curve=equity_curve,
212        )
213
214        sweep_run_row.additional_properties = d
215        return sweep_run_row
additional_keys: list[str]
217    @property
218    def additional_keys(self) -> list[str]:
219        return list(self.additional_properties.keys())
class SweepRunRowParams:
13@_attrs_define
14class SweepRunRowParams:
15    """ """
16
17    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
18
19    def to_dict(self) -> dict[str, Any]:
20
21        field_dict: dict[str, Any] = {}
22        field_dict.update(self.additional_properties)
23
24        return field_dict
25
26    @classmethod
27    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
28        d = dict(src_dict)
29        sweep_run_row_params = cls()
30
31        sweep_run_row_params.additional_properties = d
32        return sweep_run_row_params
33
34    @property
35    def additional_keys(self) -> list[str]:
36        return list(self.additional_properties.keys())
37
38    def __getitem__(self, key: str) -> Any:
39        return self.additional_properties[key]
40
41    def __setitem__(self, key: str, value: Any) -> None:
42        self.additional_properties[key] = value
43
44    def __delitem__(self, key: str) -> None:
45        del self.additional_properties[key]
46
47    def __contains__(self, key: str) -> bool:
48        return key in self.additional_properties
SweepRunRowParams()
23def __init__(self, ):
24    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepRunRowParams.

additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
19    def to_dict(self) -> dict[str, Any]:
20
21        field_dict: dict[str, Any] = {}
22        field_dict.update(self.additional_properties)
23
24        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
26    @classmethod
27    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
28        d = dict(src_dict)
29        sweep_run_row_params = cls()
30
31        sweep_run_row_params.additional_properties = d
32        return sweep_run_row_params
additional_keys: list[str]
34    @property
35    def additional_keys(self) -> list[str]:
36        return list(self.additional_properties.keys())
class SweepSensitivity:
 22@_attrs_define
 23class SweepSensitivity:
 24    """Sensitivity aggregates over a sweep's stored rows. Marginals are always complete; heatmaps may be capped, in which
 25    case `heatmapsTruncated` is true.
 26
 27        Attributes:
 28            sweep_id (str | Unset):
 29            status (SweepSensitivityStatus | Unset):
 30            objective (SweepSensitivityObjective | Unset):
 31            rows_analysed (int | Unset): Rows available when this was computed. Grows while a sweep is still running.
 32            marginals (list[SweepMarginal] | Unset):
 33            heatmaps (list[SweepHeatmap] | Unset):
 34            heatmaps_truncated (bool | Unset): True when at least one two-parameter surface was left out to stay inside the
 35                response budget. Told explicitly because a silently short list would read as "these are all the interactions",
 36                which is the wrong thing to conclude from a sensitivity view.
 37    """
 38
 39    sweep_id: str | Unset = UNSET
 40    status: SweepSensitivityStatus | Unset = UNSET
 41    objective: SweepSensitivityObjective | Unset = UNSET
 42    rows_analysed: int | Unset = UNSET
 43    marginals: list[SweepMarginal] | Unset = UNSET
 44    heatmaps: list[SweepHeatmap] | Unset = UNSET
 45    heatmaps_truncated: bool | Unset = UNSET
 46    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 47
 48    def to_dict(self) -> dict[str, Any]:
 49        sweep_id = self.sweep_id
 50
 51        status: str | Unset = UNSET
 52        if not isinstance(self.status, Unset):
 53            status = self.status.value
 54
 55        objective: str | Unset = UNSET
 56        if not isinstance(self.objective, Unset):
 57            objective = self.objective.value
 58
 59        rows_analysed = self.rows_analysed
 60
 61        marginals: list[dict[str, Any]] | Unset = UNSET
 62        if not isinstance(self.marginals, Unset):
 63            marginals = []
 64            for marginals_item_data in self.marginals:
 65                marginals_item = marginals_item_data.to_dict()
 66                marginals.append(marginals_item)
 67
 68        heatmaps: list[dict[str, Any]] | Unset = UNSET
 69        if not isinstance(self.heatmaps, Unset):
 70            heatmaps = []
 71            for heatmaps_item_data in self.heatmaps:
 72                heatmaps_item = heatmaps_item_data.to_dict()
 73                heatmaps.append(heatmaps_item)
 74
 75        heatmaps_truncated = self.heatmaps_truncated
 76
 77        field_dict: dict[str, Any] = {}
 78        field_dict.update(self.additional_properties)
 79        field_dict.update({})
 80        if sweep_id is not UNSET:
 81            field_dict["sweepId"] = sweep_id
 82        if status is not UNSET:
 83            field_dict["status"] = status
 84        if objective is not UNSET:
 85            field_dict["objective"] = objective
 86        if rows_analysed is not UNSET:
 87            field_dict["rowsAnalysed"] = rows_analysed
 88        if marginals is not UNSET:
 89            field_dict["marginals"] = marginals
 90        if heatmaps is not UNSET:
 91            field_dict["heatmaps"] = heatmaps
 92        if heatmaps_truncated is not UNSET:
 93            field_dict["heatmapsTruncated"] = heatmaps_truncated
 94
 95        return field_dict
 96
 97    @classmethod
 98    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 99        from ..models.sweep_heatmap import SweepHeatmap
100        from ..models.sweep_marginal import SweepMarginal
101
102        d = dict(src_dict)
103        sweep_id = d.pop("sweepId", UNSET)
104
105        _status = d.pop("status", UNSET)
106        status: SweepSensitivityStatus | Unset
107        if isinstance(_status, Unset):
108            status = UNSET
109        else:
110            status = SweepSensitivityStatus(_status)
111
112        _objective = d.pop("objective", UNSET)
113        objective: SweepSensitivityObjective | Unset
114        if isinstance(_objective, Unset):
115            objective = UNSET
116        else:
117            objective = SweepSensitivityObjective(_objective)
118
119        rows_analysed = d.pop("rowsAnalysed", UNSET)
120
121        _marginals = d.pop("marginals", UNSET)
122        marginals: list[SweepMarginal] | Unset = UNSET
123        if _marginals is not UNSET:
124            marginals = []
125            for marginals_item_data in _marginals:
126                marginals_item = SweepMarginal.from_dict(marginals_item_data)
127
128                marginals.append(marginals_item)
129
130        _heatmaps = d.pop("heatmaps", UNSET)
131        heatmaps: list[SweepHeatmap] | Unset = UNSET
132        if _heatmaps is not UNSET:
133            heatmaps = []
134            for heatmaps_item_data in _heatmaps:
135                heatmaps_item = SweepHeatmap.from_dict(heatmaps_item_data)
136
137                heatmaps.append(heatmaps_item)
138
139        heatmaps_truncated = d.pop("heatmapsTruncated", UNSET)
140
141        sweep_sensitivity = cls(
142            sweep_id=sweep_id,
143            status=status,
144            objective=objective,
145            rows_analysed=rows_analysed,
146            marginals=marginals,
147            heatmaps=heatmaps,
148            heatmaps_truncated=heatmaps_truncated,
149        )
150
151        sweep_sensitivity.additional_properties = d
152        return sweep_sensitivity
153
154    @property
155    def additional_keys(self) -> list[str]:
156        return list(self.additional_properties.keys())
157
158    def __getitem__(self, key: str) -> Any:
159        return self.additional_properties[key]
160
161    def __setitem__(self, key: str, value: Any) -> None:
162        self.additional_properties[key] = value
163
164    def __delitem__(self, key: str) -> None:
165        del self.additional_properties[key]
166
167    def __contains__(self, key: str) -> bool:
168        return key in self.additional_properties

Sensitivity aggregates over a sweep's stored rows. Marginals are always complete; heatmaps may be capped, in which case heatmapsTruncated is true.

Attributes:
    sweep_id (str | Unset):
    status (SweepSensitivityStatus | Unset):
    objective (SweepSensitivityObjective | Unset):
    rows_analysed (int | Unset): Rows available when this was computed. Grows while a sweep is still running.
    marginals (list[SweepMarginal] | Unset):
    heatmaps (list[SweepHeatmap] | Unset):
    heatmaps_truncated (bool | Unset): True when at least one two-parameter surface was left out to stay inside the
        response budget. Told explicitly because a silently short list would read as "these are all the interactions",
        which is the wrong thing to conclude from a sensitivity view.
SweepSensitivity( sweep_id: str | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, status: SweepSensitivityStatus | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, objective: SweepSensitivityObjective | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, rows_analysed: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, marginals: list[SweepMarginal] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, heatmaps: list[SweepHeatmap] | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, heatmaps_truncated: bool | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
30def __init__(self, sweep_id=attr_dict['sweep_id'].default, status=attr_dict['status'].default, objective=attr_dict['objective'].default, rows_analysed=attr_dict['rows_analysed'].default, marginals=attr_dict['marginals'].default, heatmaps=attr_dict['heatmaps'].default, heatmaps_truncated=attr_dict['heatmaps_truncated'].default):
31    self.sweep_id = sweep_id
32    self.status = status
33    self.objective = objective
34    self.rows_analysed = rows_analysed
35    self.marginals = marginals
36    self.heatmaps = heatmaps
37    self.heatmaps_truncated = heatmaps_truncated
38    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepSensitivity.

sweep_id: str | qtsurfer.api.client._generated.types.Unset
status: SweepSensitivityStatus | qtsurfer.api.client._generated.types.Unset
objective: SweepSensitivityObjective | qtsurfer.api.client._generated.types.Unset
rows_analysed: int | qtsurfer.api.client._generated.types.Unset
marginals: list[SweepMarginal] | qtsurfer.api.client._generated.types.Unset
heatmaps: list[SweepHeatmap] | qtsurfer.api.client._generated.types.Unset
heatmaps_truncated: bool | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
48    def to_dict(self) -> dict[str, Any]:
49        sweep_id = self.sweep_id
50
51        status: str | Unset = UNSET
52        if not isinstance(self.status, Unset):
53            status = self.status.value
54
55        objective: str | Unset = UNSET
56        if not isinstance(self.objective, Unset):
57            objective = self.objective.value
58
59        rows_analysed = self.rows_analysed
60
61        marginals: list[dict[str, Any]] | Unset = UNSET
62        if not isinstance(self.marginals, Unset):
63            marginals = []
64            for marginals_item_data in self.marginals:
65                marginals_item = marginals_item_data.to_dict()
66                marginals.append(marginals_item)
67
68        heatmaps: list[dict[str, Any]] | Unset = UNSET
69        if not isinstance(self.heatmaps, Unset):
70            heatmaps = []
71            for heatmaps_item_data in self.heatmaps:
72                heatmaps_item = heatmaps_item_data.to_dict()
73                heatmaps.append(heatmaps_item)
74
75        heatmaps_truncated = self.heatmaps_truncated
76
77        field_dict: dict[str, Any] = {}
78        field_dict.update(self.additional_properties)
79        field_dict.update({})
80        if sweep_id is not UNSET:
81            field_dict["sweepId"] = sweep_id
82        if status is not UNSET:
83            field_dict["status"] = status
84        if objective is not UNSET:
85            field_dict["objective"] = objective
86        if rows_analysed is not UNSET:
87            field_dict["rowsAnalysed"] = rows_analysed
88        if marginals is not UNSET:
89            field_dict["marginals"] = marginals
90        if heatmaps is not UNSET:
91            field_dict["heatmaps"] = heatmaps
92        if heatmaps_truncated is not UNSET:
93            field_dict["heatmapsTruncated"] = heatmaps_truncated
94
95        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 97    @classmethod
 98    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 99        from ..models.sweep_heatmap import SweepHeatmap
100        from ..models.sweep_marginal import SweepMarginal
101
102        d = dict(src_dict)
103        sweep_id = d.pop("sweepId", UNSET)
104
105        _status = d.pop("status", UNSET)
106        status: SweepSensitivityStatus | Unset
107        if isinstance(_status, Unset):
108            status = UNSET
109        else:
110            status = SweepSensitivityStatus(_status)
111
112        _objective = d.pop("objective", UNSET)
113        objective: SweepSensitivityObjective | Unset
114        if isinstance(_objective, Unset):
115            objective = UNSET
116        else:
117            objective = SweepSensitivityObjective(_objective)
118
119        rows_analysed = d.pop("rowsAnalysed", UNSET)
120
121        _marginals = d.pop("marginals", UNSET)
122        marginals: list[SweepMarginal] | Unset = UNSET
123        if _marginals is not UNSET:
124            marginals = []
125            for marginals_item_data in _marginals:
126                marginals_item = SweepMarginal.from_dict(marginals_item_data)
127
128                marginals.append(marginals_item)
129
130        _heatmaps = d.pop("heatmaps", UNSET)
131        heatmaps: list[SweepHeatmap] | Unset = UNSET
132        if _heatmaps is not UNSET:
133            heatmaps = []
134            for heatmaps_item_data in _heatmaps:
135                heatmaps_item = SweepHeatmap.from_dict(heatmaps_item_data)
136
137                heatmaps.append(heatmaps_item)
138
139        heatmaps_truncated = d.pop("heatmapsTruncated", UNSET)
140
141        sweep_sensitivity = cls(
142            sweep_id=sweep_id,
143            status=status,
144            objective=objective,
145            rows_analysed=rows_analysed,
146            marginals=marginals,
147            heatmaps=heatmaps,
148            heatmaps_truncated=heatmaps_truncated,
149        )
150
151        sweep_sensitivity.additional_properties = d
152        return sweep_sensitivity
additional_keys: list[str]
154    @property
155    def additional_keys(self) -> list[str]:
156        return list(self.additional_properties.keys())
class SweepSensitivityObjective(builtins.str, enum.Enum):
 5class SweepSensitivityObjective(str, Enum):
 6    MAXDD = "maxdd"
 7    PNL = "pnl"
 8    SHARPE = "sharpe"
 9    SORTINO = "sortino"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

MAXDD = <SweepSensitivityObjective.MAXDD: 'maxdd'>
SHARPE = <SweepSensitivityObjective.SHARPE: 'sharpe'>
SORTINO = <SweepSensitivityObjective.SORTINO: 'sortino'>
class SweepSensitivityStatus(builtins.str, enum.Enum):
 5class SweepSensitivityStatus(str, Enum):
 6    CANCELLED = "CANCELLED"
 7    COMPLETED = "COMPLETED"
 8    PARTIAL = "PARTIAL"
 9    RUNNING = "RUNNING"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

CANCELLED = <SweepSensitivityStatus.CANCELLED: 'CANCELLED'>
COMPLETED = <SweepSensitivityStatus.COMPLETED: 'COMPLETED'>
PARTIAL = <SweepSensitivityStatus.PARTIAL: 'PARTIAL'>
RUNNING = <SweepSensitivityStatus.RUNNING: 'RUNNING'>
class SweepSpecRequest:
 21@_attrs_define
 22class SweepSpecRequest:
 23    """
 24    Example:
 25        {'sampler': 'lhs', 'seed': 487221, 'samples': 100, 'objective': 'sharpe', 'params': {'rsiPeriod': {'from': 7,
 26            'to': 28, 'step': 1}, 'useTrendFilter': {'values': [True, False]}}}
 27
 28    Attributes:
 29        params (SweepSpecRequestParams):
 30        sampler (SweepSpecRequestSampler | Unset):  Default: SweepSpecRequestSampler.GRID.
 31        seed (int | Unset): Reproducibility seed. If omitted, the server generates one with Java's
 32            `L64X128MixRandom` generator and returns the effective value. The range
 33            is limited to JavaScript-safe integers so generated clients can replay it exactly.
 34        samples (int | Unset): Number of samples for `random` and `lhs`; ignored by `grid`.
 35        objective (SweepSpecRequestObjective | Unset):  Default: SweepSpecRequestObjective.SHARPE.
 36    """
 37
 38    params: SweepSpecRequestParams
 39    sampler: SweepSpecRequestSampler | Unset = SweepSpecRequestSampler.GRID
 40    seed: int | Unset = UNSET
 41    samples: int | Unset = UNSET
 42    objective: SweepSpecRequestObjective | Unset = SweepSpecRequestObjective.SHARPE
 43    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 44
 45    def to_dict(self) -> dict[str, Any]:
 46        params = self.params.to_dict()
 47
 48        sampler: str | Unset = UNSET
 49        if not isinstance(self.sampler, Unset):
 50            sampler = self.sampler.value
 51
 52        seed = self.seed
 53
 54        samples = self.samples
 55
 56        objective: str | Unset = UNSET
 57        if not isinstance(self.objective, Unset):
 58            objective = self.objective.value
 59
 60        field_dict: dict[str, Any] = {}
 61        field_dict.update(self.additional_properties)
 62        field_dict.update(
 63            {
 64                "params": params,
 65            }
 66        )
 67        if sampler is not UNSET:
 68            field_dict["sampler"] = sampler
 69        if seed is not UNSET:
 70            field_dict["seed"] = seed
 71        if samples is not UNSET:
 72            field_dict["samples"] = samples
 73        if objective is not UNSET:
 74            field_dict["objective"] = objective
 75
 76        return field_dict
 77
 78    @classmethod
 79    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 80        from ..models.sweep_spec_request_params import SweepSpecRequestParams
 81
 82        d = dict(src_dict)
 83        params = SweepSpecRequestParams.from_dict(d.pop("params"))
 84
 85        _sampler = d.pop("sampler", UNSET)
 86        sampler: SweepSpecRequestSampler | Unset
 87        if isinstance(_sampler, Unset):
 88            sampler = UNSET
 89        else:
 90            sampler = SweepSpecRequestSampler(_sampler)
 91
 92        seed = d.pop("seed", UNSET)
 93
 94        samples = d.pop("samples", UNSET)
 95
 96        _objective = d.pop("objective", UNSET)
 97        objective: SweepSpecRequestObjective | Unset
 98        if isinstance(_objective, Unset):
 99            objective = UNSET
100        else:
101            objective = SweepSpecRequestObjective(_objective)
102
103        sweep_spec_request = cls(
104            params=params,
105            sampler=sampler,
106            seed=seed,
107            samples=samples,
108            objective=objective,
109        )
110
111        sweep_spec_request.additional_properties = d
112        return sweep_spec_request
113
114    @property
115    def additional_keys(self) -> list[str]:
116        return list(self.additional_properties.keys())
117
118    def __getitem__(self, key: str) -> Any:
119        return self.additional_properties[key]
120
121    def __setitem__(self, key: str, value: Any) -> None:
122        self.additional_properties[key] = value
123
124    def __delitem__(self, key: str) -> None:
125        del self.additional_properties[key]
126
127    def __contains__(self, key: str) -> bool:
128        return key in self.additional_properties

Example: {'sampler': 'lhs', 'seed': 487221, 'samples': 100, 'objective': 'sharpe', 'params': {'rsiPeriod': {'from': 7, 'to': 28, 'step': 1}, 'useTrendFilter': {'values': [True, False]}}}

Attributes: params (SweepSpecRequestParams): sampler (SweepSpecRequestSampler | Unset): Default: SweepSpecRequestSampler.GRID. seed (int | Unset): Reproducibility seed. If omitted, the server generates one with Java's L64X128MixRandom generator and returns the effective value. The range is limited to JavaScript-safe integers so generated clients can replay it exactly. samples (int | Unset): Number of samples for random and lhs; ignored by grid. objective (SweepSpecRequestObjective | Unset): Default: SweepSpecRequestObjective.SHARPE.

SweepSpecRequest( params: SweepSpecRequestParams, sampler: SweepSpecRequestSampler | qtsurfer.api.client._generated.types.Unset = <SweepSpecRequestSampler.GRID: 'grid'>, seed: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, samples: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, objective: SweepSpecRequestObjective | qtsurfer.api.client._generated.types.Unset = <SweepSpecRequestObjective.SHARPE: 'sharpe'>)
28def __init__(self, params, sampler=attr_dict['sampler'].default, seed=attr_dict['seed'].default, samples=attr_dict['samples'].default, objective=attr_dict['objective'].default):
29    self.params = params
30    self.sampler = sampler
31    self.seed = seed
32    self.samples = samples
33    self.objective = objective
34    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepSpecRequest.

sampler: SweepSpecRequestSampler | qtsurfer.api.client._generated.types.Unset
seed: int | qtsurfer.api.client._generated.types.Unset
samples: int | qtsurfer.api.client._generated.types.Unset
objective: SweepSpecRequestObjective | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
45    def to_dict(self) -> dict[str, Any]:
46        params = self.params.to_dict()
47
48        sampler: str | Unset = UNSET
49        if not isinstance(self.sampler, Unset):
50            sampler = self.sampler.value
51
52        seed = self.seed
53
54        samples = self.samples
55
56        objective: str | Unset = UNSET
57        if not isinstance(self.objective, Unset):
58            objective = self.objective.value
59
60        field_dict: dict[str, Any] = {}
61        field_dict.update(self.additional_properties)
62        field_dict.update(
63            {
64                "params": params,
65            }
66        )
67        if sampler is not UNSET:
68            field_dict["sampler"] = sampler
69        if seed is not UNSET:
70            field_dict["seed"] = seed
71        if samples is not UNSET:
72            field_dict["samples"] = samples
73        if objective is not UNSET:
74            field_dict["objective"] = objective
75
76        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 78    @classmethod
 79    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 80        from ..models.sweep_spec_request_params import SweepSpecRequestParams
 81
 82        d = dict(src_dict)
 83        params = SweepSpecRequestParams.from_dict(d.pop("params"))
 84
 85        _sampler = d.pop("sampler", UNSET)
 86        sampler: SweepSpecRequestSampler | Unset
 87        if isinstance(_sampler, Unset):
 88            sampler = UNSET
 89        else:
 90            sampler = SweepSpecRequestSampler(_sampler)
 91
 92        seed = d.pop("seed", UNSET)
 93
 94        samples = d.pop("samples", UNSET)
 95
 96        _objective = d.pop("objective", UNSET)
 97        objective: SweepSpecRequestObjective | Unset
 98        if isinstance(_objective, Unset):
 99            objective = UNSET
100        else:
101            objective = SweepSpecRequestObjective(_objective)
102
103        sweep_spec_request = cls(
104            params=params,
105            sampler=sampler,
106            seed=seed,
107            samples=samples,
108            objective=objective,
109        )
110
111        sweep_spec_request.additional_properties = d
112        return sweep_spec_request
additional_keys: list[str]
114    @property
115    def additional_keys(self) -> list[str]:
116        return list(self.additional_properties.keys())
class SweepSpecRequestObjective(builtins.str, enum.Enum):
 5class SweepSpecRequestObjective(str, Enum):
 6    MAXDD = "maxdd"
 7    PNL = "pnl"
 8    SHARPE = "sharpe"
 9    SORTINO = "sortino"
10
11    def __str__(self) -> str:
12        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

MAXDD = <SweepSpecRequestObjective.MAXDD: 'maxdd'>
SHARPE = <SweepSpecRequestObjective.SHARPE: 'sharpe'>
SORTINO = <SweepSpecRequestObjective.SORTINO: 'sortino'>
class SweepSpecRequestParams:
18@_attrs_define
19class SweepSpecRequestParams:
20    """ """
21
22    additional_properties: dict[str, SweepAxisType0 | SweepAxisType1] = _attrs_field(init=False, factory=dict)
23
24    def to_dict(self) -> dict[str, Any]:
25        from ..models.sweep_axis_type_0 import SweepAxisType0
26
27        field_dict: dict[str, Any] = {}
28        for prop_name, prop in self.additional_properties.items():
29            if isinstance(prop, SweepAxisType0):
30                field_dict[prop_name] = prop.to_dict()
31            else:
32                field_dict[prop_name] = prop.to_dict()
33
34        return field_dict
35
36    @classmethod
37    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
38        from ..models.sweep_axis_type_0 import SweepAxisType0
39        from ..models.sweep_axis_type_1 import SweepAxisType1
40
41        d = dict(src_dict)
42        sweep_spec_request_params = cls()
43
44        additional_properties = {}
45        for prop_name, prop_dict in d.items():
46
47            def _parse_additional_property(data: object) -> SweepAxisType0 | SweepAxisType1:
48                try:
49                    if not isinstance(data, dict):
50                        raise TypeError()
51                    componentsschemas_sweep_axis_type_0 = SweepAxisType0.from_dict(data)
52
53                    return componentsschemas_sweep_axis_type_0
54                except (TypeError, ValueError, AttributeError, KeyError):
55                    pass
56                if not isinstance(data, dict):
57                    raise TypeError()
58                componentsschemas_sweep_axis_type_1 = SweepAxisType1.from_dict(data)
59
60                return componentsschemas_sweep_axis_type_1
61
62            additional_property = _parse_additional_property(prop_dict)
63
64            additional_properties[prop_name] = additional_property
65
66        sweep_spec_request_params.additional_properties = additional_properties
67        return sweep_spec_request_params
68
69    @property
70    def additional_keys(self) -> list[str]:
71        return list(self.additional_properties.keys())
72
73    def __getitem__(self, key: str) -> SweepAxisType0 | SweepAxisType1:
74        return self.additional_properties[key]
75
76    def __setitem__(self, key: str, value: SweepAxisType0 | SweepAxisType1) -> None:
77        self.additional_properties[key] = value
78
79    def __delitem__(self, key: str) -> None:
80        del self.additional_properties[key]
81
82    def __contains__(self, key: str) -> bool:
83        return key in self.additional_properties
SweepSpecRequestParams()
23def __init__(self, ):
24    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class SweepSpecRequestParams.

additional_properties: dict[str, SweepAxisType0 | SweepAxisType1]
def to_dict(self) -> dict[str, typing.Any]:
24    def to_dict(self) -> dict[str, Any]:
25        from ..models.sweep_axis_type_0 import SweepAxisType0
26
27        field_dict: dict[str, Any] = {}
28        for prop_name, prop in self.additional_properties.items():
29            if isinstance(prop, SweepAxisType0):
30                field_dict[prop_name] = prop.to_dict()
31            else:
32                field_dict[prop_name] = prop.to_dict()
33
34        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
36    @classmethod
37    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
38        from ..models.sweep_axis_type_0 import SweepAxisType0
39        from ..models.sweep_axis_type_1 import SweepAxisType1
40
41        d = dict(src_dict)
42        sweep_spec_request_params = cls()
43
44        additional_properties = {}
45        for prop_name, prop_dict in d.items():
46
47            def _parse_additional_property(data: object) -> SweepAxisType0 | SweepAxisType1:
48                try:
49                    if not isinstance(data, dict):
50                        raise TypeError()
51                    componentsschemas_sweep_axis_type_0 = SweepAxisType0.from_dict(data)
52
53                    return componentsschemas_sweep_axis_type_0
54                except (TypeError, ValueError, AttributeError, KeyError):
55                    pass
56                if not isinstance(data, dict):
57                    raise TypeError()
58                componentsschemas_sweep_axis_type_1 = SweepAxisType1.from_dict(data)
59
60                return componentsschemas_sweep_axis_type_1
61
62            additional_property = _parse_additional_property(prop_dict)
63
64            additional_properties[prop_name] = additional_property
65
66        sweep_spec_request_params.additional_properties = additional_properties
67        return sweep_spec_request_params
additional_keys: list[str]
69    @property
70    def additional_keys(self) -> list[str]:
71        return list(self.additional_properties.keys())
class SweepSpecRequestSampler(builtins.str, enum.Enum):
 5class SweepSpecRequestSampler(str, Enum):
 6    GRID = "grid"
 7    LHS = "lhs"
 8    RANDOM = "random"
 9
10    def __str__(self) -> str:
11        return str(self.value)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

GRID = <SweepSpecRequestSampler.GRID: 'grid'>
RANDOM = <SweepSpecRequestSampler.RANDOM: 'random'>
class WalkForwardAccepted:
13@_attrs_define
14class WalkForwardAccepted:
15    """Echo of the accepted walk-forward configuration, present only when the submit carried one. `inSamplePct` is the
16    resolved value, so a request that omitted it can see what it got.
17
18        Attributes:
19            folds (int):
20            in_sample_pct (int):
21            total_runs (int): What this sweep actually costs, `folds × (grid size + 1)` — the in-sample runs for every fold
22                plus each fold's one out-of-sample run. Deliberately distinct from the top-level `totalRuns`, which stays the
23                size of the grid that was submitted.
24    """
25
26    folds: int
27    in_sample_pct: int
28    total_runs: int
29    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
30
31    def to_dict(self) -> dict[str, Any]:
32        folds = self.folds
33
34        in_sample_pct = self.in_sample_pct
35
36        total_runs = self.total_runs
37
38        field_dict: dict[str, Any] = {}
39        field_dict.update(self.additional_properties)
40        field_dict.update(
41            {
42                "folds": folds,
43                "inSamplePct": in_sample_pct,
44                "totalRuns": total_runs,
45            }
46        )
47
48        return field_dict
49
50    @classmethod
51    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
52        d = dict(src_dict)
53        folds = d.pop("folds")
54
55        in_sample_pct = d.pop("inSamplePct")
56
57        total_runs = d.pop("totalRuns")
58
59        walk_forward_accepted = cls(
60            folds=folds,
61            in_sample_pct=in_sample_pct,
62            total_runs=total_runs,
63        )
64
65        walk_forward_accepted.additional_properties = d
66        return walk_forward_accepted
67
68    @property
69    def additional_keys(self) -> list[str]:
70        return list(self.additional_properties.keys())
71
72    def __getitem__(self, key: str) -> Any:
73        return self.additional_properties[key]
74
75    def __setitem__(self, key: str, value: Any) -> None:
76        self.additional_properties[key] = value
77
78    def __delitem__(self, key: str) -> None:
79        del self.additional_properties[key]
80
81    def __contains__(self, key: str) -> bool:
82        return key in self.additional_properties

Echo of the accepted walk-forward configuration, present only when the submit carried one. inSamplePct is the resolved value, so a request that omitted it can see what it got.

Attributes:
    folds (int):
    in_sample_pct (int):
    total_runs (int): What this sweep actually costs, `folds × (grid size + 1)` — the in-sample runs for every fold
        plus each fold's one out-of-sample run. Deliberately distinct from the top-level `totalRuns`, which stays the
        size of the grid that was submitted.
WalkForwardAccepted(folds: int, in_sample_pct: int, total_runs: int)
26def __init__(self, folds, in_sample_pct, total_runs):
27    self.folds = folds
28    self.in_sample_pct = in_sample_pct
29    self.total_runs = total_runs
30    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class WalkForwardAccepted.

folds: int
in_sample_pct: int
total_runs: int
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
31    def to_dict(self) -> dict[str, Any]:
32        folds = self.folds
33
34        in_sample_pct = self.in_sample_pct
35
36        total_runs = self.total_runs
37
38        field_dict: dict[str, Any] = {}
39        field_dict.update(self.additional_properties)
40        field_dict.update(
41            {
42                "folds": folds,
43                "inSamplePct": in_sample_pct,
44                "totalRuns": total_runs,
45            }
46        )
47
48        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
50    @classmethod
51    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
52        d = dict(src_dict)
53        folds = d.pop("folds")
54
55        in_sample_pct = d.pop("inSamplePct")
56
57        total_runs = d.pop("totalRuns")
58
59        walk_forward_accepted = cls(
60            folds=folds,
61            in_sample_pct=in_sample_pct,
62            total_runs=total_runs,
63        )
64
65        walk_forward_accepted.additional_properties = d
66        return walk_forward_accepted
additional_keys: list[str]
68    @property
69    def additional_keys(self) -> list[str]:
70        return list(self.additional_properties.keys())
class WalkForwardFold:
 18@_attrs_define
 19class WalkForwardFold:
 20    """What one fold concluded. The out-of-sample row is the answer; the in-sample figure is only there to be compared
 21    against it, since any grid produces a flattering in-sample winner — that is what optimizing does. The gap between
 22    them is the whole reading.
 23
 24        Attributes:
 25            fold_ix (int): Position in the walk-forward sequence, oldest first.
 26            in_sample_from (int): First index of the optimization window, into the prepared session.
 27            in_sample_to (int): End of the optimization window, exclusive — and where scoring begins.
 28            out_of_sample_to (int): End of the scoring window, exclusive.
 29            params (WalkForwardFoldParams): The parameter vector that won this fold's optimization window.
 30            in_sample_sharpe (float): How that winner scored on the window it was chosen on.
 31            out_of_sample (SweepRunRow):
 32            vectors_run (int): Vectors this fold evaluated in-sample before picking its winner.
 33    """
 34
 35    fold_ix: int
 36    in_sample_from: int
 37    in_sample_to: int
 38    out_of_sample_to: int
 39    params: WalkForwardFoldParams
 40    in_sample_sharpe: float
 41    out_of_sample: SweepRunRow
 42    vectors_run: int
 43    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 44
 45    def to_dict(self) -> dict[str, Any]:
 46        fold_ix = self.fold_ix
 47
 48        in_sample_from = self.in_sample_from
 49
 50        in_sample_to = self.in_sample_to
 51
 52        out_of_sample_to = self.out_of_sample_to
 53
 54        params = self.params.to_dict()
 55
 56        in_sample_sharpe = self.in_sample_sharpe
 57
 58        out_of_sample = self.out_of_sample.to_dict()
 59
 60        vectors_run = self.vectors_run
 61
 62        field_dict: dict[str, Any] = {}
 63        field_dict.update(self.additional_properties)
 64        field_dict.update(
 65            {
 66                "foldIx": fold_ix,
 67                "inSampleFrom": in_sample_from,
 68                "inSampleTo": in_sample_to,
 69                "outOfSampleTo": out_of_sample_to,
 70                "params": params,
 71                "inSampleSharpe": in_sample_sharpe,
 72                "outOfSample": out_of_sample,
 73                "vectorsRun": vectors_run,
 74            }
 75        )
 76
 77        return field_dict
 78
 79    @classmethod
 80    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 81        from ..models.sweep_run_row import SweepRunRow
 82        from ..models.walk_forward_fold_params import WalkForwardFoldParams
 83
 84        d = dict(src_dict)
 85        fold_ix = d.pop("foldIx")
 86
 87        in_sample_from = d.pop("inSampleFrom")
 88
 89        in_sample_to = d.pop("inSampleTo")
 90
 91        out_of_sample_to = d.pop("outOfSampleTo")
 92
 93        params = WalkForwardFoldParams.from_dict(d.pop("params"))
 94
 95        in_sample_sharpe = d.pop("inSampleSharpe")
 96
 97        out_of_sample = SweepRunRow.from_dict(d.pop("outOfSample"))
 98
 99        vectors_run = d.pop("vectorsRun")
100
101        walk_forward_fold = cls(
102            fold_ix=fold_ix,
103            in_sample_from=in_sample_from,
104            in_sample_to=in_sample_to,
105            out_of_sample_to=out_of_sample_to,
106            params=params,
107            in_sample_sharpe=in_sample_sharpe,
108            out_of_sample=out_of_sample,
109            vectors_run=vectors_run,
110        )
111
112        walk_forward_fold.additional_properties = d
113        return walk_forward_fold
114
115    @property
116    def additional_keys(self) -> list[str]:
117        return list(self.additional_properties.keys())
118
119    def __getitem__(self, key: str) -> Any:
120        return self.additional_properties[key]
121
122    def __setitem__(self, key: str, value: Any) -> None:
123        self.additional_properties[key] = value
124
125    def __delitem__(self, key: str) -> None:
126        del self.additional_properties[key]
127
128    def __contains__(self, key: str) -> bool:
129        return key in self.additional_properties

What one fold concluded. The out-of-sample row is the answer; the in-sample figure is only there to be compared against it, since any grid produces a flattering in-sample winner — that is what optimizing does. The gap between them is the whole reading.

Attributes:
    fold_ix (int): Position in the walk-forward sequence, oldest first.
    in_sample_from (int): First index of the optimization window, into the prepared session.
    in_sample_to (int): End of the optimization window, exclusive — and where scoring begins.
    out_of_sample_to (int): End of the scoring window, exclusive.
    params (WalkForwardFoldParams): The parameter vector that won this fold's optimization window.
    in_sample_sharpe (float): How that winner scored on the window it was chosen on.
    out_of_sample (SweepRunRow):
    vectors_run (int): Vectors this fold evaluated in-sample before picking its winner.
WalkForwardFold( fold_ix: int, in_sample_from: int, in_sample_to: int, out_of_sample_to: int, params: WalkForwardFoldParams, in_sample_sharpe: float, out_of_sample: SweepRunRow, vectors_run: int)
31def __init__(self, fold_ix, in_sample_from, in_sample_to, out_of_sample_to, params, in_sample_sharpe, out_of_sample, vectors_run):
32    self.fold_ix = fold_ix
33    self.in_sample_from = in_sample_from
34    self.in_sample_to = in_sample_to
35    self.out_of_sample_to = out_of_sample_to
36    self.params = params
37    self.in_sample_sharpe = in_sample_sharpe
38    self.out_of_sample = out_of_sample
39    self.vectors_run = vectors_run
40    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class WalkForwardFold.

fold_ix: int
in_sample_from: int
in_sample_to: int
out_of_sample_to: int
in_sample_sharpe: float
out_of_sample: SweepRunRow
vectors_run: int
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
45    def to_dict(self) -> dict[str, Any]:
46        fold_ix = self.fold_ix
47
48        in_sample_from = self.in_sample_from
49
50        in_sample_to = self.in_sample_to
51
52        out_of_sample_to = self.out_of_sample_to
53
54        params = self.params.to_dict()
55
56        in_sample_sharpe = self.in_sample_sharpe
57
58        out_of_sample = self.out_of_sample.to_dict()
59
60        vectors_run = self.vectors_run
61
62        field_dict: dict[str, Any] = {}
63        field_dict.update(self.additional_properties)
64        field_dict.update(
65            {
66                "foldIx": fold_ix,
67                "inSampleFrom": in_sample_from,
68                "inSampleTo": in_sample_to,
69                "outOfSampleTo": out_of_sample_to,
70                "params": params,
71                "inSampleSharpe": in_sample_sharpe,
72                "outOfSample": out_of_sample,
73                "vectorsRun": vectors_run,
74            }
75        )
76
77        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 79    @classmethod
 80    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 81        from ..models.sweep_run_row import SweepRunRow
 82        from ..models.walk_forward_fold_params import WalkForwardFoldParams
 83
 84        d = dict(src_dict)
 85        fold_ix = d.pop("foldIx")
 86
 87        in_sample_from = d.pop("inSampleFrom")
 88
 89        in_sample_to = d.pop("inSampleTo")
 90
 91        out_of_sample_to = d.pop("outOfSampleTo")
 92
 93        params = WalkForwardFoldParams.from_dict(d.pop("params"))
 94
 95        in_sample_sharpe = d.pop("inSampleSharpe")
 96
 97        out_of_sample = SweepRunRow.from_dict(d.pop("outOfSample"))
 98
 99        vectors_run = d.pop("vectorsRun")
100
101        walk_forward_fold = cls(
102            fold_ix=fold_ix,
103            in_sample_from=in_sample_from,
104            in_sample_to=in_sample_to,
105            out_of_sample_to=out_of_sample_to,
106            params=params,
107            in_sample_sharpe=in_sample_sharpe,
108            out_of_sample=out_of_sample,
109            vectors_run=vectors_run,
110        )
111
112        walk_forward_fold.additional_properties = d
113        return walk_forward_fold
additional_keys: list[str]
115    @property
116    def additional_keys(self) -> list[str]:
117        return list(self.additional_properties.keys())
class WalkForwardFoldParams:
13@_attrs_define
14class WalkForwardFoldParams:
15    """The parameter vector that won this fold's optimization window."""
16
17    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
18
19    def to_dict(self) -> dict[str, Any]:
20
21        field_dict: dict[str, Any] = {}
22        field_dict.update(self.additional_properties)
23
24        return field_dict
25
26    @classmethod
27    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
28        d = dict(src_dict)
29        walk_forward_fold_params = cls()
30
31        walk_forward_fold_params.additional_properties = d
32        return walk_forward_fold_params
33
34    @property
35    def additional_keys(self) -> list[str]:
36        return list(self.additional_properties.keys())
37
38    def __getitem__(self, key: str) -> Any:
39        return self.additional_properties[key]
40
41    def __setitem__(self, key: str, value: Any) -> None:
42        self.additional_properties[key] = value
43
44    def __delitem__(self, key: str) -> None:
45        del self.additional_properties[key]
46
47    def __contains__(self, key: str) -> bool:
48        return key in self.additional_properties

The parameter vector that won this fold's optimization window.

WalkForwardFoldParams()
23def __init__(self, ):
24    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class WalkForwardFoldParams.

additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
19    def to_dict(self) -> dict[str, Any]:
20
21        field_dict: dict[str, Any] = {}
22        field_dict.update(self.additional_properties)
23
24        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
26    @classmethod
27    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
28        d = dict(src_dict)
29        walk_forward_fold_params = cls()
30
31        walk_forward_fold_params.additional_properties = d
32        return walk_forward_fold_params
additional_keys: list[str]
34    @property
35    def additional_keys(self) -> list[str]:
36        return list(self.additional_properties.keys())
class WalkForwardRequest:
15@_attrs_define
16class WalkForwardRequest:
17    """Opt in to walk-forward validation. Present, the sweep runs as F sequential folds and the result gains a
18    `walkForward` section; absent, nothing about the sweep changes. Two requests that differ only in this block are two
19    different sweeps and do not deduplicate against each other.
20
21        Attributes:
22            folds (int): How many sequential optimize-then-score windows to run. Two is the minimum for a reason, and it is
23                structural rather than a tuning choice: parameter drift is measured between consecutive fold winners, and a
24                single fold — one train/test split with no sequence — has no consecutive pair to compare, so it would report the
25                strongest possible stability having measured nothing.
26                The upper bound is a server setting (12 by default) and is deliberately not pinned here, since a spec that
27                hardcodes a tunable limit lies the day it is raised. Exceeding it, or exceeding the sweep budget once multiplied
28                by the grid size, is a 400.
29            in_sample_pct (int | Unset): Share of the session each fold spends optimizing; the remainder is where its winner
30                is scored. Lower values leave more data to be scored on and, on short sessions, are also what lets the requested
31                fold count tile the data at all. Default: 66.
32    """
33
34    folds: int
35    in_sample_pct: int | Unset = 66
36    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
37
38    def to_dict(self) -> dict[str, Any]:
39        folds = self.folds
40
41        in_sample_pct = self.in_sample_pct
42
43        field_dict: dict[str, Any] = {}
44        field_dict.update(self.additional_properties)
45        field_dict.update(
46            {
47                "folds": folds,
48            }
49        )
50        if in_sample_pct is not UNSET:
51            field_dict["inSamplePct"] = in_sample_pct
52
53        return field_dict
54
55    @classmethod
56    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
57        d = dict(src_dict)
58        folds = d.pop("folds")
59
60        in_sample_pct = d.pop("inSamplePct", UNSET)
61
62        walk_forward_request = cls(
63            folds=folds,
64            in_sample_pct=in_sample_pct,
65        )
66
67        walk_forward_request.additional_properties = d
68        return walk_forward_request
69
70    @property
71    def additional_keys(self) -> list[str]:
72        return list(self.additional_properties.keys())
73
74    def __getitem__(self, key: str) -> Any:
75        return self.additional_properties[key]
76
77    def __setitem__(self, key: str, value: Any) -> None:
78        self.additional_properties[key] = value
79
80    def __delitem__(self, key: str) -> None:
81        del self.additional_properties[key]
82
83    def __contains__(self, key: str) -> bool:
84        return key in self.additional_properties

Opt in to walk-forward validation. Present, the sweep runs as F sequential folds and the result gains a walkForward section; absent, nothing about the sweep changes. Two requests that differ only in this block are two different sweeps and do not deduplicate against each other.

Attributes:
    folds (int): How many sequential optimize-then-score windows to run. Two is the minimum for a reason, and it is
        structural rather than a tuning choice: parameter drift is measured between consecutive fold winners, and a
        single fold — one train/test split with no sequence — has no consecutive pair to compare, so it would report the
        strongest possible stability having measured nothing.
        The upper bound is a server setting (12 by default) and is deliberately not pinned here, since a spec that
        hardcodes a tunable limit lies the day it is raised. Exceeding it, or exceeding the sweep budget once multiplied
        by the grid size, is a 400.
    in_sample_pct (int | Unset): Share of the session each fold spends optimizing; the remainder is where its winner
        is scored. Lower values leave more data to be scored on and, on short sessions, are also what lets the requested
        fold count tile the data at all. Default: 66.
WalkForwardRequest( folds: int, in_sample_pct: int | qtsurfer.api.client._generated.types.Unset = 66)
25def __init__(self, folds, in_sample_pct=attr_dict['in_sample_pct'].default):
26    self.folds = folds
27    self.in_sample_pct = in_sample_pct
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class WalkForwardRequest.

folds: int
in_sample_pct: int | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
38    def to_dict(self) -> dict[str, Any]:
39        folds = self.folds
40
41        in_sample_pct = self.in_sample_pct
42
43        field_dict: dict[str, Any] = {}
44        field_dict.update(self.additional_properties)
45        field_dict.update(
46            {
47                "folds": folds,
48            }
49        )
50        if in_sample_pct is not UNSET:
51            field_dict["inSamplePct"] = in_sample_pct
52
53        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
55    @classmethod
56    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
57        d = dict(src_dict)
58        folds = d.pop("folds")
59
60        in_sample_pct = d.pop("inSamplePct", UNSET)
61
62        walk_forward_request = cls(
63            folds=folds,
64            in_sample_pct=in_sample_pct,
65        )
66
67        walk_forward_request.additional_properties = d
68        return walk_forward_request
additional_keys: list[str]
70    @property
71    def additional_keys(self) -> list[str]:
72        return list(self.additional_properties.keys())
class WalkForwardResult:
 19@_attrs_define
 20class WalkForwardResult:
 21    """Present only on a sweep submitted with `walkForward`, and present from acceptance onward — its presence, not its
 22    contents, is what identifies a walk-forward sweep. `completedFolds` is 0 while the first fold is still running.
 23
 24        Attributes:
 25            folds (int): Folds requested at submit.
 26            completed_folds (int): Folds that have finished and reported a winner.
 27            results (list[WalkForwardFold]): One entry per completed fold, oldest first.
 28            in_sample_pct (int | Unset): Resolved in-sample share each fold optimized on.
 29            param_drift (float | Unset): Mean normalized lattice distance between consecutive fold winners. Low is good:
 30                winners that stay in a tight band fold after fold are evidence the parameter means something, while winners that
 31                jump across the grid every time are the sweep re-fitting noise, and that backtest will not survive contact with
 32                live data. **Absent is not zero** — the field is omitted whenever the figure could not be computed (fewer than
 33                two folds finished, no stored grid to place winners on), because zero is itself a meaningful reading here and a
 34                placeholder would be indistinguishable from perfect stability.
 35    """
 36
 37    folds: int
 38    completed_folds: int
 39    results: list[WalkForwardFold]
 40    in_sample_pct: int | Unset = UNSET
 41    param_drift: float | Unset = UNSET
 42    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 43
 44    def to_dict(self) -> dict[str, Any]:
 45        folds = self.folds
 46
 47        completed_folds = self.completed_folds
 48
 49        results = []
 50        for results_item_data in self.results:
 51            results_item = results_item_data.to_dict()
 52            results.append(results_item)
 53
 54        in_sample_pct = self.in_sample_pct
 55
 56        param_drift = self.param_drift
 57
 58        field_dict: dict[str, Any] = {}
 59        field_dict.update(self.additional_properties)
 60        field_dict.update(
 61            {
 62                "folds": folds,
 63                "completedFolds": completed_folds,
 64                "results": results,
 65            }
 66        )
 67        if in_sample_pct is not UNSET:
 68            field_dict["inSamplePct"] = in_sample_pct
 69        if param_drift is not UNSET:
 70            field_dict["paramDrift"] = param_drift
 71
 72        return field_dict
 73
 74    @classmethod
 75    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 76        from ..models.walk_forward_fold import WalkForwardFold
 77
 78        d = dict(src_dict)
 79        folds = d.pop("folds")
 80
 81        completed_folds = d.pop("completedFolds")
 82
 83        results = []
 84        _results = d.pop("results")
 85        for results_item_data in _results:
 86            results_item = WalkForwardFold.from_dict(results_item_data)
 87
 88            results.append(results_item)
 89
 90        in_sample_pct = d.pop("inSamplePct", UNSET)
 91
 92        param_drift = d.pop("paramDrift", UNSET)
 93
 94        walk_forward_result = cls(
 95            folds=folds,
 96            completed_folds=completed_folds,
 97            results=results,
 98            in_sample_pct=in_sample_pct,
 99            param_drift=param_drift,
100        )
101
102        walk_forward_result.additional_properties = d
103        return walk_forward_result
104
105    @property
106    def additional_keys(self) -> list[str]:
107        return list(self.additional_properties.keys())
108
109    def __getitem__(self, key: str) -> Any:
110        return self.additional_properties[key]
111
112    def __setitem__(self, key: str, value: Any) -> None:
113        self.additional_properties[key] = value
114
115    def __delitem__(self, key: str) -> None:
116        del self.additional_properties[key]
117
118    def __contains__(self, key: str) -> bool:
119        return key in self.additional_properties

Present only on a sweep submitted with walkForward, and present from acceptance onward — its presence, not its contents, is what identifies a walk-forward sweep. completedFolds is 0 while the first fold is still running.

Attributes:
    folds (int): Folds requested at submit.
    completed_folds (int): Folds that have finished and reported a winner.
    results (list[WalkForwardFold]): One entry per completed fold, oldest first.
    in_sample_pct (int | Unset): Resolved in-sample share each fold optimized on.
    param_drift (float | Unset): Mean normalized lattice distance between consecutive fold winners. Low is good:
        winners that stay in a tight band fold after fold are evidence the parameter means something, while winners that
        jump across the grid every time are the sweep re-fitting noise, and that backtest will not survive contact with
        live data. **Absent is not zero** — the field is omitted whenever the figure could not be computed (fewer than
        two folds finished, no stored grid to place winners on), because zero is itself a meaningful reading here and a
        placeholder would be indistinguishable from perfect stability.
WalkForwardResult( folds: int, completed_folds: int, results: list[WalkForwardFold], in_sample_pct: int | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>, param_drift: float | qtsurfer.api.client._generated.types.Unset = <qtsurfer.api.client._generated.types.Unset object>)
28def __init__(self, folds, completed_folds, results, in_sample_pct=attr_dict['in_sample_pct'].default, param_drift=attr_dict['param_drift'].default):
29    self.folds = folds
30    self.completed_folds = completed_folds
31    self.results = results
32    self.in_sample_pct = in_sample_pct
33    self.param_drift = param_drift
34    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class WalkForwardResult.

folds: int
completed_folds: int
results: list[WalkForwardFold]
in_sample_pct: int | qtsurfer.api.client._generated.types.Unset
param_drift: float | qtsurfer.api.client._generated.types.Unset
additional_properties: dict[str, typing.Any]
def to_dict(self) -> dict[str, typing.Any]:
44    def to_dict(self) -> dict[str, Any]:
45        folds = self.folds
46
47        completed_folds = self.completed_folds
48
49        results = []
50        for results_item_data in self.results:
51            results_item = results_item_data.to_dict()
52            results.append(results_item)
53
54        in_sample_pct = self.in_sample_pct
55
56        param_drift = self.param_drift
57
58        field_dict: dict[str, Any] = {}
59        field_dict.update(self.additional_properties)
60        field_dict.update(
61            {
62                "folds": folds,
63                "completedFolds": completed_folds,
64                "results": results,
65            }
66        )
67        if in_sample_pct is not UNSET:
68            field_dict["inSamplePct"] = in_sample_pct
69        if param_drift is not UNSET:
70            field_dict["paramDrift"] = param_drift
71
72        return field_dict
@classmethod
def from_dict(cls: type[~T], src_dict: Mapping[str, typing.Any]) -> ~T:
 74    @classmethod
 75    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 76        from ..models.walk_forward_fold import WalkForwardFold
 77
 78        d = dict(src_dict)
 79        folds = d.pop("folds")
 80
 81        completed_folds = d.pop("completedFolds")
 82
 83        results = []
 84        _results = d.pop("results")
 85        for results_item_data in _results:
 86            results_item = WalkForwardFold.from_dict(results_item_data)
 87
 88            results.append(results_item)
 89
 90        in_sample_pct = d.pop("inSamplePct", UNSET)
 91
 92        param_drift = d.pop("paramDrift", UNSET)
 93
 94        walk_forward_result = cls(
 95            folds=folds,
 96            completed_folds=completed_folds,
 97            results=results,
 98            in_sample_pct=in_sample_pct,
 99            param_drift=param_drift,
100        )
101
102        walk_forward_result.additional_properties = d
103        return walk_forward_result
additional_keys: list[str]
105    @property
106    def additional_keys(self) -> list[str]:
107        return list(self.additional_properties.keys())