Skip to content

Commit 1a96a3f

Browse files
committed
Add StrategyState
1 parent 3b59b15 commit 1a96a3f

File tree

4 files changed

+164
-40
lines changed

4 files changed

+164
-40
lines changed

investing_bot_framework/core/context/states/bot_state.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABC, abstractmethod
2-
from typing import Type, List
2+
from typing import List
33

44
from investing_bot_framework.core.context.state_validator import StateValidator
55

@@ -33,18 +33,10 @@ def start(self):
3333
def run(self) -> None:
3434
pass
3535

36-
@abstractmethod
37-
def stop(self) -> None:
38-
pass
39-
4036
@property
4137
def context(self):
4238
return self._bot_context
4339

44-
@abstractmethod
45-
def reconfigure(self) -> None:
46-
pass
47-
4840
def validate_state(self) -> bool:
4941
"""
5042
Function that will validate the state

investing_bot_framework/core/context/states/data_provider_state.py renamed to investing_bot_framework/core/context/states/data_providing_state.py

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,39 @@
88
from investing_bot_framework.core.exceptions import OperationalException
99
from investing_bot_framework.core.context.states import BotState
1010
from investing_bot_framework.core.executors import ExecutionScheduler
11+
from investing_bot_framework.core.workers import Worker
1112
from investing_bot_framework.core.data_providers import DataProvider
12-
from investing_bot_framework.core.executors.data_provider_executor import DataProviderExecutor
13+
from investing_bot_framework.core.executors import Executor
1314
from investing_bot_framework.core.configuration.config_constants import DEFAULT_MAX_WORKERS, SETTINGS_MAX_WORKERS
1415

1516
logger = logging.getLogger(__name__)
1617

1718

19+
class DataProviderExecutor(Executor):
20+
"""
21+
Class DataProviderExecutor: is an executor for DataProvider instances.
22+
"""
23+
24+
def __init__(self, data_providers: List[DataProvider] = None, max_workers: int = DEFAULT_MAX_WORKERS):
25+
super(DataProviderExecutor, self).__init__(max_workers=max_workers)
26+
27+
self._registered_data_providers: List[DataProvider] = []
28+
29+
if data_providers is not None and len(data_providers) > 0:
30+
self._registered_data_providers = data_providers
31+
32+
def create_workers(self) -> List[Worker]:
33+
return self._registered_data_providers
34+
35+
@property
36+
def registered_data_providers(self) -> List[DataProvider]:
37+
return self._registered_data_providers
38+
39+
@property
40+
def configured(self):
41+
return self._registered_data_providers is not None and len(self._registered_data_providers) > 0
42+
43+
1844
class DataProviderScheduler(ExecutionScheduler):
1945
"""
2046
Data Provider scheduler that will function as a scheduler to make sure it keeps it state across multiple states,
@@ -42,7 +68,7 @@ def configured(self) -> bool:
4268
return self._configured
4369

4470

45-
class DataProviderState(BotState, Observer):
71+
class DataProvidingState(BotState, Observer):
4672
"""
4773
Represent the data_providers state of a bot. This state will load all the defined data_providers providers and will
4874
run them.
@@ -58,19 +84,19 @@ class DataProviderState(BotState, Observer):
5884
data_provider_scheduler: DataProviderScheduler = None
5985

6086
def __init__(self, context: BotContext) -> None:
61-
super(DataProviderState, self).__init__(context)
87+
super(DataProvidingState, self).__init__(context)
6288
self._updated = False
6389
self.data_provider_executor = None
6490

6591
def _schedule_data_providers(self) -> List[DataProvider]:
6692

67-
if not DataProviderState.data_provider_scheduler:
68-
DataProviderState.data_provider_scheduler = DataProviderScheduler()
93+
if not DataProvidingState.data_provider_scheduler:
94+
DataProvidingState.data_provider_scheduler = DataProviderScheduler()
6995

70-
if not DataProviderState.data_provider_scheduler.configured:
71-
DataProviderState.data_provider_scheduler.configure(self.registered_data_providers)
96+
if not DataProvidingState.data_provider_scheduler.configured:
97+
DataProvidingState.data_provider_scheduler.configure(self.registered_data_providers)
7298

73-
planning = DataProviderState.data_provider_scheduler.schedule_executions()
99+
planning = DataProvidingState.data_provider_scheduler.schedule_executions()
74100
planned_data_providers = []
75101

76102
for data_provider in self.registered_data_providers:
@@ -113,22 +139,13 @@ def run(self) -> None:
113139
for data_provider in self.data_provider_executor.registered_data_providers:
114140
logger.info("Data provider: {} finished running".format(data_provider.get_id()))
115141

116-
def stop(self) -> None:
117-
"""
118-
Stop all data_providers providers
119-
"""
120-
pass
121-
122142
@synchronized
123143
def update(self, observable, **kwargs) -> None:
124144
self._updated = True
125145

126146
@staticmethod
127147
def register_data_providers(data_providers: List) -> None:
128-
DataProviderState.registered_data_providers = data_providers
129-
130-
def reconfigure(self) -> None:
131-
pass
148+
DataProvidingState.registered_data_providers = data_providers
132149

133150

134151

investing_bot_framework/core/context/states/setup_state.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
class SetupState(BotState):
66

7-
from investing_bot_framework.core.context.states.data_provider_state import DataProviderState
8-
transition_state_class = DataProviderState
7+
from investing_bot_framework.core.context.states.data_providing_state import DataProvidingState
8+
transition_state_class = DataProvidingState
99

1010
def __init__(self, context):
1111
super(SetupState, self).__init__(context)
Lines changed: 126 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,136 @@
1-
from typing import Type
1+
import logging
2+
import time
3+
from typing import List
4+
25
from investing_bot_framework.core.context.states import BotState
6+
from investing_bot_framework.core.executors import ExecutionScheduler
7+
from investing_bot_framework.core.exceptions import OperationalException
8+
from investing_bot_framework.core.workers import Worker
9+
from investing_bot_framework.core.executors import Executor
10+
from investing_bot_framework.core.events import Observer
11+
from investing_bot_framework.core.configuration.config_constants import DEFAULT_MAX_WORKERS, SETTINGS_MAX_WORKERS
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class StrategyExecutor(Executor):
17+
"""
18+
Class StrategyExecutor: is an executor for Strategy instances.
19+
"""
20+
21+
def __init__(self, strategies: List = None, max_workers: int = DEFAULT_MAX_WORKERS):
22+
super(StrategyExecutor, self).__init__(max_workers=max_workers)
23+
24+
self._registered_strategies: List = []
25+
26+
if strategies is not None and len(strategies) > 0:
27+
self._registered_strategies = strategies
28+
29+
def create_workers(self) -> List[Worker]:
30+
return self._registered_strategies
31+
32+
@property
33+
def registered_strategies(self) -> List:
34+
return self._registered_strategies
35+
36+
@property
37+
def configured(self):
38+
return self._registered_strategies is not None and len(self._registered_strategies) > 0
39+
40+
41+
class StrategyScheduler(ExecutionScheduler):
42+
"""
43+
Strategy scheduler that will function as a scheduler.
44+
"""
45+
46+
def __init__(self):
47+
self._configured = False
48+
super(StrategyScheduler, self).__init__()
49+
50+
def configure(self, strategies: List) -> None:
51+
self._planning = {}
52+
53+
for strategy in strategies:
54+
self.add_execution_task(
55+
execution_id=strategy.get_id(),
56+
time_unit=strategy.get_time_unit(),
57+
interval=strategy.get_time_interval()
58+
)
59+
60+
self._configured = True
61+
62+
@property
63+
def configured(self) -> bool:
64+
return self._configured
365

466

5-
class StrategyState(BotState):
67+
class StrategyState(BotState, Observer):
68+
69+
from investing_bot_framework.core.context.states.data_providing_state import DataProvidingState
70+
transition_state_class = DataProvidingState
71+
72+
registered_strategies: List = None
73+
strategy_scheduler: StrategyScheduler = None
74+
75+
def __init__(self, context):
76+
super(StrategyState, self).__init__(context)
77+
self._updated = False
78+
self.strategy_executor = None
79+
80+
def _schedule_strategies(self) -> List:
81+
82+
if not StrategyState.strategy_scheduler:
83+
StrategyState.strategy_scheduler = StrategyScheduler()
84+
85+
if not StrategyState.strategy_scheduler.configured:
86+
StrategyState.strategy_scheduler.configure(self.registered_strategies)
87+
88+
planning = StrategyState.strategy_scheduler.schedule_executions()
89+
planned_strategies = []
90+
91+
for strategy in self.registered_strategies:
92+
93+
if strategy.get_id() in planning:
94+
planned_strategies.append(strategy)
95+
96+
return planned_strategies
97+
98+
def _start_strategies(self, strategies: List) -> None:
99+
100+
self.strategy_executor = StrategyExecutor(
101+
strategies=strategies,
102+
max_workers=self.context.settings.get(SETTINGS_MAX_WORKERS, DEFAULT_MAX_WORKERS)
103+
)
104+
105+
if self.strategy_executor.configured:
106+
self.strategy_executor.add_observer(self)
107+
self.strategy_executor.start()
108+
else:
109+
# Skip the execution
110+
self._updated = True
6111

7112
def run(self) -> None:
8-
pass
9113

10-
def stop(self) -> None:
11-
pass
114+
if self.registered_strategies is None:
115+
raise OperationalException("Data providing state has not any data providers configured")
116+
117+
# Schedule the strategies providers
118+
planned_strategies = self._schedule_strategies()
119+
120+
# Execute all the strategies providers
121+
self._start_strategies(planned_strategies)
122+
123+
# Sleep till updated
124+
while not self._updated:
125+
time.sleep(1)
12126

13-
def reconfigure(self) -> None:
14-
pass
127+
# Collect all strategies from the strategies providers
128+
for strategies in self.strategy_executor.registered_strategies:
129+
logger.info("Data provider: {} finished running".format(strategies.get_id()))
15130

16131
def update(self, observable, **kwargs) -> None:
17-
pass
132+
self._updated = True
18133

19-
def get_transition_state_class(self) -> Type:
20-
from investing_bot_framework.core.context.states.data_provider_state import DataProviderState
21-
return DataProviderState
134+
@staticmethod
135+
def register_strategies(strategies: List) -> None:
136+
StrategyState.registered_strategies = strategies

0 commit comments

Comments
 (0)