dijkies 0.0.3__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dijkies/data_pipeline.py CHANGED
@@ -3,6 +3,8 @@ from abc import ABC, abstractmethod
3
3
  import pandas as pd
4
4
  from pandas.core.frame import DataFrame as PandasDataFrame
5
5
 
6
+ from dijkies.exchange_market_api import ExchangeMarketAPI
7
+
6
8
 
7
9
  class DataPipeline(ABC):
8
10
  @abstractmethod
@@ -10,6 +12,35 @@ class DataPipeline(ABC):
10
12
  pass
11
13
 
12
14
 
13
- class NoDataPipeline(ABC):
15
+ class NoDataPipeline(DataPipeline):
16
+ def run(self) -> PandasDataFrame:
17
+ return pd.DataFrame([{}], columns=["close"])
18
+
19
+
20
+ class CurrentValueDataPipeline(DataPipeline):
21
+ def __init__(self, base: str, exchange_market_api: ExchangeMarketAPI) -> None:
22
+ self.exchange_market_api = exchange_market_api
23
+ self.base = base
24
+
25
+ def run(self) -> PandasDataFrame:
26
+ current_price = self.exchange_market_api.get_price(self.base)
27
+ return pd.DataFrame([{"close": current_price}])
28
+
29
+
30
+ class OHLCVDataPipeline(DataPipeline):
31
+ def __init__(
32
+ self,
33
+ exchange_market_api: ExchangeMarketAPI,
34
+ base: str,
35
+ candle_interval_in_minutes: int,
36
+ lookback_in_minutes: int,
37
+ ) -> None:
38
+ self.exchange_market_api = exchange_market_api
39
+ self.base = base
40
+ self.candle_interval_in_minutes = candle_interval_in_minutes
41
+ self.lookback_in_minutes = lookback_in_minutes
42
+
14
43
  def run(self) -> PandasDataFrame:
15
- return pd.DataFrame({})
44
+ return self.exchange_market_api.get_candles(
45
+ self.base, self.candle_interval_in_minutes, self.lookback_in_minutes
46
+ )
dijkies/deployment.py CHANGED
@@ -1,94 +1,140 @@
1
1
  import os
2
- import json
3
2
  import pickle
4
-
3
+ import shutil
5
4
  from abc import ABC, abstractmethod
6
-
7
- from dijkies.strategy import Strategy
8
- from dijkies.executors import State
9
- from dijkies.credentials import Credentials
10
- from dijkies.executors import BitvavoExchangeAssetClient
5
+ from pathlib import Path
6
+ from typing import Literal
7
+
8
+ from dijkies.executors import (
9
+ SUPPORTED_EXCHANGES,
10
+ BacktestExchangeAssetClient,
11
+ BitvavoExchangeAssetClient,
12
+ )
11
13
  from dijkies.logger import get_logger
14
+ from dijkies.strategy import Strategy
12
15
 
16
+ BOT_STATUS = Literal["active", "paused", "stopped"]
17
+ ASSET_HANDLING = Literal["quote_only", "base_only", "ignore"]
13
18
 
14
- class StrategyRepository(ABC):
15
- @abstractmethod
16
- def store(self, strategy: Strategy, id: str) -> None:
17
- pass
18
19
 
20
+ class StrategyRepository(ABC):
19
21
  @abstractmethod
20
- def store_state(self, state: State, id: str) -> None:
22
+ def store(
23
+ self,
24
+ strategy: Strategy,
25
+ person_id: str,
26
+ exchange: SUPPORTED_EXCHANGES,
27
+ bot_id: str,
28
+ status: BOT_STATUS,
29
+ ) -> None:
21
30
  pass
22
31
 
23
32
  @abstractmethod
24
- def read(self, id: str) -> Strategy:
33
+ def read(
34
+ self,
35
+ person_id: str,
36
+ exchange: SUPPORTED_EXCHANGES,
37
+ bot_id: str,
38
+ status: BOT_STATUS,
39
+ ) -> Strategy:
25
40
  pass
26
41
 
27
42
  @abstractmethod
28
- def read_state(self, id: str) -> State:
43
+ def change_status(
44
+ self,
45
+ person_id: str,
46
+ exchange: SUPPORTED_EXCHANGES,
47
+ bot_id: str,
48
+ from_status: BOT_STATUS,
49
+ to_status: BOT_STATUS,
50
+ ) -> None:
29
51
  pass
30
52
 
31
53
 
32
54
  class LocalStrategyRepository(StrategyRepository):
33
- def __init__(self, root_directory: str) -> None:
55
+ def __init__(self, root_directory: Path) -> None:
34
56
  self.root_directory = root_directory
35
57
 
36
- def store(self, strategy: Strategy, id: str):
37
- path = os.path.join(self.root_directory, id + ".pkl")
58
+ def store(
59
+ self,
60
+ strategy: Strategy,
61
+ person_id: str,
62
+ exchange: SUPPORTED_EXCHANGES,
63
+ bot_id: str,
64
+ status: BOT_STATUS,
65
+ ) -> None:
66
+ (self.root_directory / person_id / exchange / status).mkdir(
67
+ parents=True, exist_ok=True
68
+ )
69
+ path = os.path.join(
70
+ self.root_directory, person_id, exchange, status, bot_id + ".pkl"
71
+ )
38
72
  with open(path, "wb") as file:
39
73
  pickle.dump(strategy, file)
40
74
 
41
- def store_state(self, state: State, id: str) -> None:
42
- path = os.path.join(self.root_directory, "state_" + id, ".json")
43
- with open(path, "w") as file:
44
- json.dump(state, file)
45
-
46
- def read(self, id: str) -> Strategy:
47
- path = os.path.join(self.root_directory, id + ".pkl")
75
+ def read(
76
+ self,
77
+ person_id: str,
78
+ exchange: SUPPORTED_EXCHANGES,
79
+ bot_id: str,
80
+ status: BOT_STATUS,
81
+ ) -> Strategy:
82
+ path = os.path.join(
83
+ self.root_directory, person_id, exchange, status, bot_id + ".pkl"
84
+ )
48
85
  with open(path, "rb") as file:
49
86
  strategy = pickle.load(file)
50
87
  return strategy
51
88
 
52
- def read_state(self, id: str) -> State:
53
- path = os.path.join(self.root_directory, "state_" + id, ".json")
54
- with open(path, "r") as file:
55
- state = json.load(file)
56
- return state
89
+ def change_status(
90
+ self,
91
+ person_id: str,
92
+ exchange: SUPPORTED_EXCHANGES,
93
+ bot_id: str,
94
+ from_status: BOT_STATUS,
95
+ to_status: BOT_STATUS,
96
+ ) -> None:
97
+ if from_status == to_status:
98
+ return
99
+ src = (
100
+ Path(f"{self.root_directory}/{person_id}/{exchange}/{from_status}")
101
+ / f"{bot_id}.pkl"
102
+ )
103
+ dest_folder = Path(f"{self.root_directory}/{person_id}/{exchange}/{to_status}")
104
+
105
+ dest_folder.mkdir(parents=True, exist_ok=True)
106
+ shutil.move(src, dest_folder / src.name)
57
107
 
58
108
 
59
109
  class CredentialsRepository(ABC):
60
110
  @abstractmethod
61
- def get_api_key(self, id: str) -> str:
111
+ def get_api_key(self, person_id: str, exchange: str) -> str:
62
112
  pass
63
113
 
64
114
  @abstractmethod
65
- def store_api_key(self, id: str, api_key: str) -> None:
115
+ def store_api_key(self, person_id: str, exchange: str, api_key: str) -> None:
66
116
  pass
67
117
 
68
118
  @abstractmethod
69
- def get_api_secret_key(self, id: str) -> str:
119
+ def get_api_secret_key(self, person_id: str, exchange: str) -> str:
70
120
  pass
71
121
 
72
122
  @abstractmethod
73
- def store_api_secret_key(self, id: str, api_secret_key: str) -> None:
123
+ def store_api_secret_key(
124
+ self, person_id: str, exchange: str, api_secret_key: str
125
+ ) -> None:
74
126
  pass
75
127
 
76
- def get_credentials(self, id: str) -> Credentials:
77
- return Credentials(
78
- api_key=self.get_api_key(id),
79
- api_secret_key=self.get_api_secret_key(id)
80
- )
81
-
82
128
 
83
- class LocalCredentialRepository(CredentialsRepository):
84
- def get_api_key(self, id: str) -> str:
85
- return os.environ.get(f"{id}_api_key")
129
+ class LocalCredentialsRepository(CredentialsRepository):
130
+ def get_api_key(self, person_id: str, exchange: str) -> str:
131
+ return os.environ.get(f"{person_id}_{exchange}_api_key")
86
132
 
87
- def store_api_key(self, id: str, api_key: str) -> None:
133
+ def store_api_key(self, person_id: str, exchange: str, api_key: str) -> None:
88
134
  pass
89
135
 
90
- def get_api_secret_key(self, id: str) -> str:
91
- return os.environ.get(f"{id}_api_secret_key")
136
+ def get_api_secret_key(self, person_id: str, exchange: str) -> str:
137
+ return os.environ.get(f"{person_id}_{exchange}_api_secret_key")
92
138
 
93
139
  def store_api_secret_key(self, id: str, api_secret_key: str) -> None:
94
140
  pass
@@ -103,25 +149,92 @@ class Bot:
103
149
  self.strategy_repository = strategy_repository
104
150
  self.credential_repository = credential_repository
105
151
 
106
- def set_executor(self, strategy: Strategy):
107
- api_key = self.credential_repository.get_api_key(id)
108
- api_secret_key = self.credential_repository.get_api_secret_key(id)
109
-
110
- if strategy.exchange == "bitvavo":
152
+ def set_executor(
153
+ self,
154
+ strategy: Strategy,
155
+ person_id: str,
156
+ exchange: SUPPORTED_EXCHANGES,
157
+ ) -> None:
158
+ if exchange == "bitvavo":
159
+ api_key = self.credential_repository.get_api_key(person_id, exchange)
160
+ api_secret_key = self.credential_repository.get_api_secret_key(
161
+ person_id, exchange
162
+ )
111
163
  strategy.executor = BitvavoExchangeAssetClient(
112
- strategy.state,
113
- api_key,
114
- api_secret_key,
115
- 1,
116
- get_logger()
164
+ strategy.state, api_key, api_secret_key, 1, get_logger()
165
+ )
166
+ elif exchange == "backtest":
167
+ strategy.executor = BacktestExchangeAssetClient(
168
+ strategy.state, 0.0025, 0.0015
117
169
  )
170
+ else:
171
+ raise Exception("exchange not defined")
172
+
173
+ def run(
174
+ self,
175
+ person_id: str,
176
+ exchange: SUPPORTED_EXCHANGES,
177
+ bot_id: str,
178
+ status: BOT_STATUS,
179
+ ) -> None:
118
180
 
119
- def run(self, id: str) -> None:
120
- strategy = self.strategy_repository.read(id)
121
- self.set_executor(strategy)
181
+ strategy = self.strategy_repository.read(person_id, exchange, bot_id, status)
182
+ self.set_executor(strategy, person_id, exchange)
122
183
 
123
184
  data_pipeline = strategy.get_data_pipeline()
124
185
  data = data_pipeline.run()
125
- strategy.run(data)
126
186
 
127
- self.strategy_repository.store(strategy)
187
+ try:
188
+ strategy.run(data)
189
+ self.strategy_repository.store(
190
+ strategy, person_id, exchange, bot_id, status
191
+ )
192
+ except Exception as e:
193
+ self.strategy_repository.store(
194
+ strategy, person_id, exchange, bot_id, status
195
+ )
196
+ self.strategy_repository.change_status(
197
+ person_id, exchange, bot_id, status, "paused"
198
+ )
199
+ raise Exception(e)
200
+
201
+ def stop(
202
+ self,
203
+ person_id: str,
204
+ exchange: SUPPORTED_EXCHANGES,
205
+ bot_id: str,
206
+ status: BOT_STATUS,
207
+ asset_handling: ASSET_HANDLING,
208
+ ) -> None:
209
+ if status == "stopped":
210
+ return
211
+
212
+ strategy = self.strategy_repository.read(person_id, exchange, bot_id, status)
213
+ self.set_executor(strategy, person_id, exchange)
214
+
215
+ try:
216
+ for open_order in strategy.state.open_orders:
217
+ _ = strategy.executor.cancel_order(open_order)
218
+ if asset_handling == "base_only":
219
+ _ = strategy.executor.place_market_buy_order(
220
+ strategy.state.base, strategy.state.quote_available
221
+ )
222
+ elif asset_handling == "quote_only":
223
+ _ = strategy.executor.place_market_sell_order(
224
+ strategy.state.base, strategy.state.base_available
225
+ )
226
+ self.strategy_repository.store(
227
+ strategy, person_id, exchange, bot_id, status
228
+ )
229
+ self.strategy_repository.change_status(
230
+ person_id, exchange, bot_id, status, "stopped"
231
+ )
232
+
233
+ except Exception as e:
234
+ self.strategy_repository.store(
235
+ strategy, person_id, exchange, bot_id, status
236
+ )
237
+ self.strategy_repository.change_status(
238
+ person_id, exchange, bot_id, status, "paused"
239
+ )
240
+ raise Exception(e)
dijkies/exceptions.py CHANGED
@@ -58,3 +58,26 @@ class CancelOrderError(Exception):
58
58
  class MethodNotDefinedError(Exception):
59
59
  def __init__(self):
60
60
  super().__init__("method not implemented...")
61
+
62
+
63
+ class InsufficientBalanceError(Exception):
64
+ def __init__(
65
+ self,
66
+ balance: dict[str, float],
67
+ requested: float,
68
+ ):
69
+ super().__init__(
70
+ f"""
71
+ not enough balance:\n
72
+ available: {balance["available"]}, requested: {requested}\n
73
+ """
74
+ )
75
+
76
+
77
+ class InsufficientOrderValueError(Exception):
78
+ def __init__(self):
79
+ super().__init__(
80
+ """
81
+ order value should be at least 5 euro:
82
+ """
83
+ )
@@ -1,17 +1,13 @@
1
- from abc import ABC, abstractmethod
2
-
3
- from pandas.core.frame import DataFrame as PandasDataFrame
4
-
5
1
  import logging
6
-
7
- import pandas as pd
8
- import requests
9
- from binance.client import Client
10
-
11
2
  import threading
12
3
  import time
4
+ from abc import ABC, abstractmethod
13
5
  from concurrent.futures import ThreadPoolExecutor, as_completed
14
6
 
7
+ import pandas as pd
8
+ import requests
9
+ from binance.client import Client
10
+ from pandas.core.frame import DataFrame as PandasDataFrame
15
11
  from python_bitvavo_api.bitvavo import Bitvavo
16
12
 
17
13
  from dijkies.logger import get_logger