-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparam_space.py
More file actions
124 lines (100 loc) · 3.61 KB
/
Copy pathparam_space.py
File metadata and controls
124 lines (100 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""Parameter space descriptors for strategy optimisation.
Each descriptor defines a single tuneable parameter: its name, type, and
the range or set of values Optuna is allowed to sample from.
These objects are intentionally data-only (no logic). The translation from
descriptor → Optuna trial suggestion lives in ``objective.py``, keeping this
module dependency-free and easily unit-testable.
Usage example
-------------
>>> from trade_lab.optimization.param_space import IntParam, FloatParam, CategoricalParam
>>>
>>> param_space = [
... IntParam('fast_period', low=5, high=50),
... IntParam('slow_period', low=20, high=200, step=5),
... FloatParam('weight_ema', low=0.1, high=2.0),
... FloatParam('weight_rsi', low=0.1, high=2.0),
... CategoricalParam('allow_short', choices=[True, False]),
... ]
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
@dataclass
class IntParam:
"""Integer parameter sampled uniformly over [low, high].
Parameters
----------
name : str
Parameter name. Used as the key in the params dict passed to the
strategy factory. Must be unique within a param space list.
low : int
Inclusive lower bound.
high : int
Inclusive upper bound.
step : int
Step size between candidate values. Default 1 (every integer).
Useful for parameters with natural granularity, e.g. periods in
multiples of 5.
Examples
--------
>>> IntParam('rsi_period', low=7, high=28) # 7, 8, ..., 28
>>> IntParam('slow_period', low=20, high=200, step=5) # 20, 25, ..., 200
"""
name: str
low: int
high: int
step: int = 1
@dataclass
class FloatParam:
"""Floating-point parameter sampled over [low, high].
Parameters
----------
name : str
Parameter name.
low : float
Inclusive lower bound.
high : float
Inclusive upper bound.
log : bool
If True, values are sampled on a log scale. Useful for parameters
that span several orders of magnitude where small values matter
proportionally more (e.g. learning rates, regularisation strengths).
Both ``low`` and ``high`` must be strictly positive when log=True.
Examples
--------
>>> FloatParam('weight_ema', low=0.1, high=3.0)
>>> FloatParam('learning_rate', low=1e-5, high=1e-1, log=True)
"""
name: str
low: float
high: float
log: bool = False
@dataclass
class CategoricalParam:
"""Parameter sampled from a fixed set of discrete choices.
The choices list may contain any type that is hashable and can be stored
in an Optuna trial: str, int, float, bool. Mixed types in a single
CategoricalParam are supported by Optuna but discouraged — use separate
params instead for clarity.
Parameters
----------
name : str
Parameter name.
choices : list[Any]
Ordered list of candidate values. Must contain at least two elements.
Examples
--------
>>> CategoricalParam('allow_short', choices=[True, False])
>>> CategoricalParam('signal_type', choices=['ohlc', 'heikin_ashi'])
"""
name: str
choices: list[Any] = field(default_factory=list)
def __post_init__(self) -> None:
if len(self.choices) < 2:
raise ValueError(
f"CategoricalParam '{self.name}' must have at least 2 choices, "
f"got {len(self.choices)}."
)
# Type alias for a mixed param space list — used in type hints throughout
# the optimisation module.
ParamSpace = list[IntParam | FloatParam | CategoricalParam]