Skip to content

Commit de8f94f

Browse files
author
investingbots
authored
Merge pull request #6 from investingbots/feature_base_strategy
Feature base strategy
2 parents e25275c + f50be6c commit de8f94f

File tree

15 files changed

+236
-123
lines changed

15 files changed

+236
-123
lines changed

bot/context/analyzing_state.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
1+
import logging
2+
13
from bot.context.bot_state import BotState
4+
from bot.context.bot_context import BotContext
5+
6+
logger = logging.getLogger(__name__)
27

38

49
class AnalyzingState(BotState):
510

6-
def run(self):
7-
pass
11+
def __init__(self):
12+
logger.info("Initializing analyzing state ...")
13+
14+
def run(self) -> None:
15+
logger.info("Analyzing state started ...")
16+
17+
from bot.context.data_providing_state import DataProvidingState
18+
context = BotContext()
19+
context.transition_to(DataProvidingState)
20+
context.run()
821

9-
def stop(self):
22+
def stop(self) -> None:
1023
pass
1124

12-
def reconfigure(self):
25+
def reconfigure(self) -> None:
1326
pass

bot/context/bot_context.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import logging
2-
from typing import Dict, Type, Any
32
from pandas import DataFrame
3+
from typing import Dict, Type, Any, List
44

5-
from bot.utils import Singleton
65
from bot import OperationalException
76
from bot.data import DataProviderExecutor
87
from bot.context.bot_state import BotState
98
from bot.strategies import StrategyExecutor
9+
from bot.utils import Singleton, DataSource
10+
1011

1112
logger = logging.getLogger(__name__)
1213

@@ -23,8 +24,17 @@ class BotContext(metaclass=Singleton):
2324
"""
2425
_state: BotState = None
2526

27+
"""
28+
List of all the buying and selling orders.
29+
"""
30+
_buy_orders: DataFrame = None
31+
_sell_orders: DataFrame = None
32+
33+
"""
34+
The data sources for the bot
35+
"""
2636
_analyzed_data: DataFrame = None
27-
_raw_data_sources: Dict[str, DataFrame] = None
37+
_data_sources: List[DataSource] = None
2838

2939
_strategy_executor = None
3040
_data_provider_executor: DataProviderExecutor = None
@@ -68,12 +78,12 @@ def analyzed_data(self, data_frame: DataFrame) -> None:
6878
self._analyzed_data = data_frame
6979

7080
@property
71-
def raw_data(self) -> Dict[str, DataFrame]:
72-
return self._raw_data_sources
81+
def data_sources(self) -> List[DataSource]:
82+
return self._data_sources
7383

74-
@raw_data.setter
75-
def raw_data(self, raw_data_sources: Dict[str, DataFrame]) -> None:
76-
self._raw_data_sources = raw_data_sources
84+
@data_sources.setter
85+
def data_sources(self, data_sources: List[DataSource]) -> None:
86+
self._data_sources = data_sources
7787

7888
@property
7989
def data_provider_executor(self) -> DataProviderExecutor:

bot/context/data_providing_state.py

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,74 @@
11
import logging
22
from time import sleep
3+
from typing import List
34
from wrapt import synchronized
45

6+
from bot.utils import DataSource
57
from bot import OperationalException
68
from bot.events.observer import Observer
79
from bot.context.bot_state import BotState
810
from bot.context.bot_context import BotContext
9-
from bot.context.analyzing_state import AnalyzingState
1011
from bot.data.data_provider_executor import DataProviderExecutor
1112

1213
logger = logging.getLogger(__name__)
1314

1415

1516
class DataProvidingState(BotState, Observer):
1617

17-
def __init__(self):
18+
def __init__(self) -> None:
1819
super(DataProvidingState, self).__init__()
1920

2021
logger.info("Initializing data providing state ...")
2122

2223
# Initialize the manager
2324
context = BotContext()
2425

25-
self._executor: DataProviderExecutor = context.data_provider_executor
26-
self._executor.add_observer(self)
26+
self._data_provider_executor: DataProviderExecutor = context.data_provider_executor
27+
self._data_provider_executor.add_observer(self)
2728

2829
self._updated = False
2930
self._started = False
3031

31-
def _clean_up(self):
32+
def _clean_up(self) -> None:
3233

33-
if self._executor.processing:
34-
self._executor.stop()
34+
if self._data_provider_executor.processing:
35+
self._data_provider_executor.stop()
3536

3637
self._updated = False
3738
self._started = False
3839

39-
def run(self):
40+
def run(self) -> None:
4041
logger.info("Data providing state started ...")
4142

42-
# if not self._started:
43-
# self._executor.start()
44-
#
45-
# # Sleep till updated
46-
# while not self._updated:
47-
# sleep(1)
48-
#
49-
# if self._updated:
50-
#
51-
# # Collect all data from the data providers
52-
# data_entries = {}
53-
#
54-
# for data_provider in self._executor.registered_data_providers:
55-
# data_entries[data_provider.get_id()] = data_provider.data
56-
#
57-
# context = BotContext()
58-
# context.raw_data = data_entries
59-
# context.transition_to(AnalyzingState)
60-
# else:
61-
# raise OperationalException("Abruptly ended out of run state")
62-
63-
def stop(self):
43+
data_sources: List[DataSource] = []
44+
45+
if not self._started:
46+
self._data_provider_executor.start()
47+
48+
# Sleep till updated
49+
while not self._updated:
50+
sleep(1)
51+
52+
if self._updated:
53+
54+
# Collect all data from the data providers
55+
for data_provider in self._data_provider_executor.registered_data_providers:
56+
data_sources.append(DataSource(data_provider.get_id(), data_provider.data))
57+
58+
context = BotContext()
59+
context.data_sources = data_sources
60+
61+
# Transitioning to another state
62+
from bot.context.analyzing_state import AnalyzingState
63+
context.transition_to(AnalyzingState)
64+
context.run()
65+
else:
66+
raise OperationalException("Abruptly ended out of run state")
67+
68+
def stop(self) -> None:
6469
pass
6570

66-
def reconfigure(self):
71+
def reconfigure(self) -> None:
6772
self._clean_up()
6873

6974
@synchronized

bot/data/data_provider/data_provider.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from abc import ABC, abstractmethod
2+
from abc import abstractmethod
33
from pandas import DataFrame
44

55
from bot.events.observable import Observable
@@ -18,7 +18,7 @@ def __init__(self, message: str) -> None:
1818
super().__init__(self)
1919
self.message = message
2020

21-
def __str__(self):
21+
def __str__(self) -> str:
2222
return self.message
2323

2424
def __json__(self):
@@ -29,11 +29,11 @@ def __json__(self):
2929

3030
class DataProvider(Observable):
3131

32-
def __init__(self):
32+
def __init__(self) -> None:
3333
super(DataProvider, self).__init__()
3434
self._data: DataFrame = None
3535

36-
def start(self):
36+
def start(self) -> None:
3737
self._data = self.provide_data()
3838
self.notify_observers()
3939

@@ -48,16 +48,17 @@ def provide_data(self) -> DataFrame:
4848
pass
4949

5050
@property
51-
def data(self):
51+
def data(self) -> DataFrame:
5252

5353
if self._data is None:
54-
55-
raise DataProviderException("Could not provide data")
56-
54+
raise DataProviderException("Could not provide data, data is not set")
5755
else:
58-
return self._data
56+
data = self._data
57+
self.clean_up()
58+
return data
5959

60-
def clear(self):
60+
@abstractmethod
61+
def clean_up(self) -> None:
6162
self._data = None
6263

6364
@abstractmethod

bot/data/data_provider_executor.py

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import logging
2-
from typing import List, Dict, Tuple
2+
from typing import List, Tuple
33

44
from bot.utils import StoppableThread
55
from bot.executors import WorkerExecutor
66
from bot.events.observable import Observable
7-
from bot.data.data_provider.data_provider import DataProvider, DataProviderException
7+
from bot.data.data_provider.data_provider import DataProvider
88

99
logger = logging.getLogger(__name__)
1010

@@ -31,31 +31,9 @@ def create_jobs(self) -> List[Tuple[Observable, StoppableThread]]:
3131

3232
return jobs
3333

34+
def start(self) -> None:
35+
super(DataProviderExecutor, self).start()
36+
3437
@property
3538
def registered_data_providers(self) -> List[DataProvider]:
3639
return self._registered_data_providers
37-
38-
39-
class ConfigDataProviderExecutor(DataProviderExecutor):
40-
41-
def __init__(self, config: Dict[str, any], data_providers: List[DataProvider] = None):
42-
43-
data_providers: List[DataProvider] = []
44-
45-
# Enable fmp data provider
46-
if config.get('data_provider', {}).get('fmp', {}).get('enabled', False):
47-
logger.info('Enabling data_provider.fmp ...')
48-
49-
try:
50-
logging.info("Initializing FMP data provider")
51-
from bot.data.data_provider.template.fmp_data_provider import FMPDataProvider
52-
data_providers.append(FMPDataProvider())
53-
except Exception as e:
54-
raise DataProviderException(str(e))
55-
56-
if data_providers is not None:
57-
58-
for data_provider in data_providers:
59-
data_providers.append(data_provider)
60-
61-
super(ConfigDataProviderExecutor, self).__init__(data_providers)

bot/executors/executor.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from abc import abstractmethod
2+
3+
from bot.events.observable import Observable
4+
5+
6+
class Executor(Observable):
7+
"""
8+
Executor class: functions as an interface that will handle the executions of functions.
9+
"""
10+
11+
@abstractmethod
12+
def start(self) -> None:
13+
pass
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from abc import abstractmethod
2+
3+
from bot.executors.executor import Executor
4+
5+
6+
class SingleExecutor(Executor):
7+
"""
8+
SingleExecutor class: functions as an abstract class that will handle the executions of a single function.
9+
"""
10+
11+
def __init__(self) -> None:
12+
super(SingleExecutor, self).__init__()
13+
14+
def start(self) -> None:
15+
self.run_job()
16+
self.notify_observers()
17+
18+
@abstractmethod
19+
def run_job(self) -> None:
20+
pass

bot/executors/worker_executor.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1+
import logging
12
from queue import Queue
23
from abc import abstractmethod
34
from wrapt import synchronized
4-
from typing import Dict, List, Tuple, Any
5+
from typing import Dict, List, Tuple
56

67
from bot import OperationalException
78
from bot.utils import StoppableThread
89
from bot.events.observer import Observer
10+
from bot.executors.executor import Executor
911
from bot.events.observable import Observable
1012
from bot.constants import DEFAULT_MAX_WORKERS
1113

1214

13-
class WorkerExecutor(Observable, Observer):
15+
logger = logging.getLogger(__name__)
16+
17+
18+
class WorkerExecutor(Executor, Observer):
1419
"""
15-
WorkerExecutor class: Abstract class that will schedule, execute and manage workers.
20+
WorkerExecutor class: functions as an abstract class that will schedule, execute and manage workers.
1621
"""
1722

1823
def __init__(self, max_workers: int = DEFAULT_MAX_WORKERS) -> None:

bot/remote_loaders/data_provider_remote_loader.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ def load_data_provider(self, data_provider_class_name: str) -> DataProvider:
2222
modules = self.locate_python_modules(data_providers_dir)
2323
location = self.locate_class(modules, data_provider_class_name)
2424
generator = self.create_class_generators(location, data_provider_class_name, DataProvider)
25-
data_provider: DataProvider = next(generator, None)
25+
instance: DataProvider = next(generator, None)()
2626

27-
if data_provider and issubclass(data_provider, DataProvider):
28-
return data_provider
27+
if instance and isinstance(instance, DataProvider):
28+
return instance
2929

3030
raise OperationalException(
31-
f"Impossible to load Strategy '{data_provider_class_name}'. This strategy does not exist "
31+
f"Impossible to load Strategy '{data_provider_class_name}'. This data provider does not exist "
3232
"or contains Python code errors."
3333
)

bot/remote_loaders/strategy_remote_loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ def load_strategy(self, strategy_class_name: str) -> Strategy:
2222
modules = self.locate_python_modules(strategies_dir)
2323
location = self.locate_class(modules, strategy_class_name)
2424
generator = self.create_class_generators(location, strategy_class_name, Strategy)
25-
strategy: Strategy = next(generator, None)
25+
strategy: Strategy = next(generator, None)()
2626

27-
if strategy and issubclass(strategy, Strategy):
27+
if strategy and isinstance(strategy, Strategy):
2828
return strategy
2929

3030
raise OperationalException(

0 commit comments

Comments
 (0)