Skip to content

Commit ac3e774

Browse files
committed
[Optimizer] allow multi exchange backtests and handle settings fallbacks
tmp
1 parent 3717d37 commit ac3e774

File tree

3 files changed

+128
-60
lines changed

3 files changed

+128
-60
lines changed

octobot/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@
204204
OPTIMIZER_DEFAULT_MIN_MUTATION_PROBABILITY_PERCENT = decimal.Decimal(10)
205205
OPTIMIZER_DEFAULT_MAX_MUTATION_NUMBER_MULTIPLIER = 3
206206
OPTIMIZER_DEFAULT_DB_UPDATE_PERIOD = 15
207+
OPTIMIZER_DEFAULT_TARGET_FITNESS_SCORE = None
207208

208209
# Databases
209210
DEFAULT_MAX_TOTAL_RUN_DATABASES_SIZE = 1000000000 # 1GB

octobot/enums.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class OptimizerConfig(enum.Enum):
3838
DATA_FILES = "data_files"
3939
OPTIMIZER_CONFIG = "optimizer_config"
4040
EXCHANGE_TYPE = "exchange_type"
41+
EXCHANGE_IDS = "exchange_ids"
42+
EXCHANGE_ID = "exchange_id"
4143
QUEUE_SIZE = "queue_size"
4244
EMPTY_THE_QUEUE = "empty_the_queue"
4345
START_TIMESTAMP = "start_timestamp"

octobot/strategy_optimizer/optimizer_settings.py

Lines changed: 125 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -21,72 +21,142 @@
2121
import octobot.strategy_optimizer.fitness_parameter as fitness_parameter
2222
import octobot.strategy_optimizer.optimizer_filter as optimizer_filter
2323
import octobot.strategy_optimizer.optimizer_constraint as optimizer_constraint
24+
import octobot_backtesting.constants as backtesting_constants
2425

2526

2627
class OptimizerSettings:
2728
def __init__(self, settings_dict=None):
2829
if settings_dict is None:
2930
settings_dict = {}
3031
# generic
31-
self.optimizer_config = settings_dict.get(enums.OptimizerConfig.OPTIMIZER_CONFIG.value, None)
32-
self.randomly_chose_runs = settings_dict.get(enums.OptimizerConfig.RANDOMLY_CHOSE_RUNS.value,
33-
constants.OPTIMIZER_DEFAULT_RANDOMLY_CHOSE_RUNS)
34-
self.data_files = settings_dict.get(enums.OptimizerConfig.DATA_FILES.value)
35-
self.start_timestamp = settings_dict.get(enums.OptimizerConfig.START_TIMESTAMP.value, None)
36-
self.end_timestamp = settings_dict.get(enums.OptimizerConfig.END_TIMESTAMP.value, None)
37-
self.required_idle_cores = int(settings_dict.get(enums.OptimizerConfig.IDLE_CORES.value,
38-
constants.OPTIMIZER_DEFAULT_REQUIRED_IDLE_CORES))
39-
self.notify_when_complete = settings_dict.get(enums.OptimizerConfig.NOTIFY_WHEN_COMPLETE.value,
40-
constants.OPTIMIZER_DEFAULT_NOTIFY_WHEN_COMPLETE)
41-
self.optimizer_mode = settings_dict.get(enums.OptimizerConfig.MODE.value,
42-
enums.OptimizerModes.NORMAL.value)
32+
self.exchange_ids = settings_dict.get(
33+
enums.OptimizerConfig.EXCHANGE_IDS.value,
34+
)
35+
if not self.exchange_ids:
36+
self.exchange_ids = [
37+
settings_dict.get(enums.OptimizerConfig.EXCHANGE_ID.value)
38+
]
39+
40+
self.exchange_type = settings_dict.get(
41+
enums.OptimizerConfig.EXCHANGE_TYPE.value
42+
)
43+
self.optimizer_config = (
44+
settings_dict.get(enums.OptimizerConfig.OPTIMIZER_CONFIG.value) or None
45+
)
46+
self.randomly_chose_runs = (
47+
settings_dict.get(enums.OptimizerConfig.RANDOMLY_CHOSE_RUNS.value)
48+
or constants.OPTIMIZER_DEFAULT_RANDOMLY_CHOSE_RUNS
49+
)
50+
51+
self.data_files = settings_dict.get(
52+
enums.OptimizerConfig.DATA_FILES.value,
53+
[backtesting_constants.CONFIG_CURRENT_BOT_DATA],
54+
)
55+
if not isinstance(self.data_files, list):
56+
self.data_files = [self.data_files]
57+
if backtesting_constants.CONFIG_CURRENT_BOT_DATA in self.data_files:
58+
self.data_files = [backtesting_constants.CONFIG_CURRENT_BOT_DATA]
59+
self.start_timestamp = settings_dict.get(
60+
enums.OptimizerConfig.START_TIMESTAMP.value, None
61+
)
62+
self.end_timestamp = settings_dict.get(
63+
enums.OptimizerConfig.END_TIMESTAMP.value, None
64+
)
65+
self.required_idle_cores = int(
66+
settings_dict.get(enums.OptimizerConfig.IDLE_CORES.value)
67+
or constants.OPTIMIZER_DEFAULT_REQUIRED_IDLE_CORES
68+
)
69+
self.notify_when_complete = (
70+
settings_dict.get(enums.OptimizerConfig.NOTIFY_WHEN_COMPLETE.value)
71+
or constants.OPTIMIZER_DEFAULT_NOTIFY_WHEN_COMPLETE
72+
)
73+
self.optimizer_mode = settings_dict.get(
74+
enums.OptimizerConfig.MODE.value, enums.OptimizerModes.NORMAL.value
75+
)
4376
optimizer_id = settings_dict.get(enums.OptimizerConfig.OPTIMIZER_ID.value, 1)
4477
self.optimizer_id = optimizer_id if optimizer_id is None else int(optimizer_id)
45-
self.optimizer_ids = settings_dict.get(enums.OptimizerConfig.OPTIMIZER_IDS.value)
46-
self.optimizer_mode = settings_dict.get(enums.OptimizerConfig.MODE.value,
47-
enums.OptimizerModes.NORMAL.value)
48-
self.queue_size = int(settings_dict.get(enums.OptimizerConfig.QUEUE_SIZE.value,
49-
constants.OPTIMIZER_DEFAULT_QUEUE_SIZE))
50-
self.empty_the_queue = settings_dict.get(enums.OptimizerConfig.EMPTY_THE_QUEUE.value, False)
78+
self.optimizer_ids = settings_dict.get(
79+
enums.OptimizerConfig.OPTIMIZER_IDS.value
80+
)
81+
self.optimizer_mode = settings_dict.get(
82+
enums.OptimizerConfig.MODE.value, enums.OptimizerModes.NORMAL.value
83+
)
84+
self.queue_size = int(
85+
settings_dict.get(
86+
enums.OptimizerConfig.QUEUE_SIZE.value,
87+
constants.OPTIMIZER_DEFAULT_QUEUE_SIZE,
88+
)
89+
)
90+
self.empty_the_queue = settings_dict.get(
91+
enums.OptimizerConfig.EMPTY_THE_QUEUE.value, False
92+
)
5193
# update run database at the end of each period
52-
self.db_update_period = int(settings_dict.get(enums.OptimizerConfig.DB_UPDATE_PERIOD.value,
53-
constants.OPTIMIZER_DEFAULT_DB_UPDATE_PERIOD))
94+
self.db_update_period = int(
95+
settings_dict.get(enums.OptimizerConfig.DB_UPDATE_PERIOD.value)
96+
or constants.OPTIMIZER_DEFAULT_DB_UPDATE_PERIOD
97+
)
5498
# AI / genetic
55-
self.max_optimizer_runs = settings_dict.get(enums.OptimizerConfig.MAX_OPTIMIZER_RUNS.value,
56-
constants.OPTIMIZER_DEFAULT_MAX_OPTIMIZER_RUNS)
57-
self.generations_count = settings_dict.get(enums.OptimizerConfig.DEFAULT_GENERATIONS_COUNT.value,
58-
constants.OPTIMIZER_DEFAULT_GENERATIONS_COUNT)
59-
self.initial_generation_count = settings_dict.get(enums.OptimizerConfig.INITIAL_GENERATION_COUNT.value,
60-
constants.OPTIMIZER_DEFAULT_INITIAL_GENERATION_COUNT)
61-
self.run_per_generation = settings_dict.get(enums.OptimizerConfig.DEFAULT_RUN_PER_GENERATION.value,
62-
constants.OPTIMIZER_DEFAULT_RUN_PER_GENERATION)
99+
self.max_optimizer_runs = (
100+
settings_dict.get(enums.OptimizerConfig.MAX_OPTIMIZER_RUNS.value)
101+
or constants.OPTIMIZER_DEFAULT_MAX_OPTIMIZER_RUNS
102+
)
103+
self.generations_count = (
104+
settings_dict.get(enums.OptimizerConfig.DEFAULT_GENERATIONS_COUNT.value)
105+
or constants.OPTIMIZER_DEFAULT_GENERATIONS_COUNT
106+
)
107+
self.initial_generation_count = (
108+
settings_dict.get(enums.OptimizerConfig.INITIAL_GENERATION_COUNT.value)
109+
or constants.OPTIMIZER_DEFAULT_INITIAL_GENERATION_COUNT
110+
)
111+
self.run_per_generation = (
112+
settings_dict.get(enums.OptimizerConfig.DEFAULT_RUN_PER_GENERATION.value)
113+
or constants.OPTIMIZER_DEFAULT_RUN_PER_GENERATION
114+
)
63115
self.fitness_parameters = self.parse_fitness_parameters(
64-
settings_dict.get(enums.OptimizerConfig.DEFAULT_SCORING_PARAMETERS.value,
65-
self.get_default_fitness_parameters())
116+
settings_dict.get(enums.OptimizerConfig.DEFAULT_SCORING_PARAMETERS.value)
117+
or self.get_default_fitness_parameters()
66118
)
119+
67120
self.exclude_filters = self.parse_optimizer_filter(
68-
settings_dict.get(enums.OptimizerConfig.DEFAULT_OPTIMIZER_FILTERS.value,
69-
self.get_default_optimizer_filters())
121+
settings_dict.get(enums.OptimizerConfig.DEFAULT_OPTIMIZER_FILTERS.value)
122+
or self.get_default_optimizer_filters()
70123
)
124+
71125
self.constraints_by_key = self.parse_optimizer_constraint(
72-
settings_dict.get(enums.OptimizerConfig.DEFAULT_OPTIMIZER_CONSTRAINTS.value,
73-
self.get_default_optimizer_constraints())
74-
)
75-
self.mutation_percent = float(settings_dict.get(
76-
enums.OptimizerConfig.DEFAULT_MUTATION_PERCENT.value,
77-
constants.OPTIMIZER_DEFAULT_MUTATION_PERCENT))
78-
self.max_mutation_probability_percent = decimal.Decimal(settings_dict.get(
79-
enums.OptimizerConfig.MAX_MUTATION_PROBABILITY_PERCENT.value,
80-
constants.OPTIMIZER_DEFAULT_MAX_MUTATION_PROBABILITY_PERCENT))
81-
self.min_mutation_probability_percent = decimal.Decimal(settings_dict.get(
82-
enums.OptimizerConfig.MIN_MUTATION_PROBABILITY_PERCENT.value,
83-
constants.OPTIMIZER_DEFAULT_MIN_MUTATION_PROBABILITY_PERCENT))
84-
self.max_mutation_number_multiplier = decimal.Decimal(settings_dict.get(
85-
enums.OptimizerConfig.DEFAULT_MAX_MUTATION_NUMBER_MULTIPLIER.value,
86-
constants.OPTIMIZER_DEFAULT_MAX_MUTATION_NUMBER_MULTIPLIER))
87-
self.target_fitness_score = settings_dict.get(enums.OptimizerConfig.TARGET_FITNESS_SCORE.value)
88-
self.stay_within_boundaries = settings_dict.get(enums.OptimizerConfig.STAY_WITHIN_BOUNDARIES.value,
89-
False)
126+
settings_dict.get(enums.OptimizerConfig.DEFAULT_OPTIMIZER_CONSTRAINTS.value)
127+
or self.get_default_optimizer_constraints()
128+
)
129+
130+
self.mutation_percent = float(
131+
settings_dict.get(enums.OptimizerConfig.DEFAULT_MUTATION_PERCENT.value)
132+
or constants.OPTIMIZER_DEFAULT_MUTATION_PERCENT
133+
)
134+
self.max_mutation_probability_percent = decimal.Decimal(
135+
settings_dict.get(
136+
enums.OptimizerConfig.MAX_MUTATION_PROBABILITY_PERCENT.value
137+
)
138+
or constants.OPTIMIZER_DEFAULT_MAX_MUTATION_PROBABILITY_PERCENT
139+
)
140+
self.min_mutation_probability_percent = decimal.Decimal(
141+
settings_dict.get(
142+
enums.OptimizerConfig.MIN_MUTATION_PROBABILITY_PERCENT.value
143+
)
144+
or constants.OPTIMIZER_DEFAULT_MIN_MUTATION_PROBABILITY_PERCENT
145+
)
146+
self.max_mutation_number_multiplier = decimal.Decimal(
147+
settings_dict.get(
148+
enums.OptimizerConfig.DEFAULT_MAX_MUTATION_NUMBER_MULTIPLIER.value
149+
)
150+
or constants.OPTIMIZER_DEFAULT_MAX_MUTATION_NUMBER_MULTIPLIER
151+
)
152+
self.target_fitness_score = (
153+
settings_dict.get(enums.OptimizerConfig.TARGET_FITNESS_SCORE.value)
154+
or constants.OPTIMIZER_DEFAULT_TARGET_FITNESS_SCORE
155+
)
156+
self.stay_within_boundaries = (
157+
settings_dict.get(enums.OptimizerConfig.STAY_WITHIN_BOUNDARIES.value)
158+
or False
159+
)
90160

91161
def get_constraint(self, constraint_key):
92162
if constraint_key in self.constraints_by_key:
@@ -95,8 +165,7 @@ def get_constraint(self, constraint_key):
95165

96166
def parse_fitness_parameters(self, parameters):
97167
return [
98-
fitness_parameter.FitnessParameter.from_dict(param)
99-
for param in parameters
168+
fitness_parameter.FitnessParameter.from_dict(param) for param in parameters
100169
]
101170

102171
def get_default_fitness_parameters(self):
@@ -107,17 +176,15 @@ def get_default_fitness_parameters(self):
107176
fitness_parameter.FitnessParameter.IS_RATIO_FROM_MAX_KEY: True,
108177
},
109178
{
110-
fitness_parameter.FitnessParameter.NAME_KEY:
111-
commons_enums.BacktestingMetadata.COEFFICIENT_OF_DETERMINATION_MAX_BALANCE.value,
179+
fitness_parameter.FitnessParameter.NAME_KEY: commons_enums.BacktestingMetadata.COEFFICIENT_OF_DETERMINATION_MAX_BALANCE.value,
112180
fitness_parameter.FitnessParameter.WEIGHT_KEY: 0,
113181
fitness_parameter.FitnessParameter.IS_RATIO_FROM_MAX_KEY: False,
114182
},
115183
]
116184

117185
def parse_optimizer_filter(self, filters):
118186
return [
119-
optimizer_filter.OptimizerFilter.from_dict(element)
120-
for element in filters
187+
optimizer_filter.OptimizerFilter.from_dict(element) for element in filters
121188
]
122189

123190
def get_default_optimizer_filters(self):
@@ -130,16 +197,14 @@ def get_default_optimizer_filters(self):
130197
optimizer_filter.OptimizerFilter.OPERATOR_KEY: commons_enums.LogicalOperators.LOWER_THAN.value,
131198
},
132199
{
133-
optimizer_filter.OptimizerFilter.LEFT_OPERAND_KEY_KEY:
134-
commons_enums.BacktestingMetadata.COEFFICIENT_OF_DETERMINATION_MAX_BALANCE.value,
200+
optimizer_filter.OptimizerFilter.LEFT_OPERAND_KEY_KEY: commons_enums.BacktestingMetadata.COEFFICIENT_OF_DETERMINATION_MAX_BALANCE.value,
135201
optimizer_filter.OptimizerFilter.RIGHT_OPERAND_KEY_KEY: None,
136202
optimizer_filter.OptimizerFilter.LEFT_OPERAND_VALUE_KEY: None,
137203
optimizer_filter.OptimizerFilter.RIGHT_OPERAND_VALUE_KEY: 0,
138204
optimizer_filter.OptimizerFilter.OPERATOR_KEY: commons_enums.LogicalOperators.LOWER_THAN.value,
139205
},
140206
{
141-
optimizer_filter.OptimizerFilter.LEFT_OPERAND_KEY_KEY:
142-
commons_enums.BacktestingMetadata.PERCENT_GAINS.value,
207+
optimizer_filter.OptimizerFilter.LEFT_OPERAND_KEY_KEY: commons_enums.BacktestingMetadata.PERCENT_GAINS.value,
143208
optimizer_filter.OptimizerFilter.RIGHT_OPERAND_KEY_KEY: None,
144209
optimizer_filter.OptimizerFilter.LEFT_OPERAND_VALUE_KEY: None,
145210
optimizer_filter.OptimizerFilter.RIGHT_OPERAND_VALUE_KEY: 0,

0 commit comments

Comments
 (0)