dijkies 0.1.6__tar.gz

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-0.1.6/PKG-INFO ADDED
@@ -0,0 +1,536 @@
1
+ Metadata-Version: 2.3
2
+ Name: dijkies
3
+ Version: 0.1.6
4
+ Summary: A python framework that can be used to create, test and deploy trading algorithms.
5
+ Author: Arnold Dijk
6
+ Author-email: Arnold Dijk <arnold.dijk@teamrockstars.nl>
7
+ License: MIT
8
+ Requires-Dist: pandas>=2.3.3
9
+ Requires-Dist: pydantic>=2.12.5
10
+ Requires-Dist: python-binance>=1.0.33
11
+ Requires-Dist: python-bitvavo-api>=1.4.3
12
+ Requires-Python: >=3.11
13
+ Project-URL: Homepage, https://github.com/ArnoldDijk/dijkies
14
+ Project-URL: Source, https://github.com/ArnoldDijk/dijkies
15
+ Project-URL: Issues, https://github.com/ArnoldDijk/dijkies/issues
16
+ Project-URL: Documentation, https://github.com/ArnoldDijk/dijkies/blob/dev/README.md
17
+ Description-Content-Type: text/markdown
18
+
19
+ ![Alt text](image.png)
20
+
21
+ # Dijkies
22
+
23
+ **Dijkies** is a Python framework for creating, testing, and deploying algorithmic trading strategies in a clean, modular, and exchange-agnostic way.
24
+
25
+ The core idea behind Dijkies is to **separate trading logic from execution and infrastructure**, allowing the same strategy code to be reused for:
26
+
27
+ - Historical backtesting
28
+ - Paper trading
29
+ - Live trading
30
+
31
+ ## Philosophy
32
+
33
+ In Dijkies, a strategy is responsible only for **making decisions** — when to buy, when to sell, and how much. Everything else, such as order execution, fee calculation, balance management, and exchange communication, is handled by dedicated components.
34
+
35
+ This separation ensures that strategies remain:
36
+
37
+ - Easy to reason about
38
+ - Easy to test
39
+ - Easy to reuse across environments
40
+
41
+ A strategy written once can be backtested on historical data and later deployed to a real exchange without modification.
42
+
43
+ ## Key Design Principles
44
+
45
+ - **Strategy–Executor separation**
46
+ Trading logic is completely decoupled from execution logic.
47
+
48
+ - **Single interface for backtesting and live trading**
49
+ Switching between backtesting and live trading requires no strategy changes.
50
+
51
+ - **Explicit state management**
52
+ All balances and positions are tracked in a transparent `State` object.
53
+
54
+ - **Minimal assumptions**
55
+ Dijkies does not enforce indicators, timeframes, or asset types.
56
+
57
+ - **Composable and extensible**
58
+ New exchanges, execution models, and risk layers can be added easily.
59
+
60
+ ## Who Is This For?
61
+
62
+ Dijkies is designed for:
63
+
64
+ - Data Scientists building algorithmic trading systems
65
+ - Quantitative traders who want full control over strategy logic
66
+ - Anyone who wants to move from backtesting to production without focussing on
67
+
68
+ ## What Dijkies Is Not
69
+
70
+ - A no-code trading bot
71
+ - A black-box strategy optimizer
72
+ - A fully managed trading platform
73
+
74
+ Dijkies provides the **building blocks**, not the trading edge.
75
+
76
+ ---
77
+
78
+ ## Quick Start
79
+
80
+ This quick start shows how to define a strategy, fetch market data, and run a backtest in just a few steps.
81
+
82
+ ### 1. Define a Strategy
83
+
84
+ A strategy is a class that inherits from `Strategy` and implements the `execute` and `get_data_pipeline` method.
85
+ the execute method receives a pandas dataframe that should, at least, contain `open`, `high`, `low`, `close`, `volume` and `candle_time` columns.
86
+ more can be added, and used within your trading algorithm. The engineering of these columns falls outside of dijkies scope.
87
+
88
+ This data is then used to define and execute actions. The following actions are available (see docstrings in dijkies.interfaces.ExchangeAssetClient for more info):
89
+
90
+ - `place limit buy order`
91
+ - `place limit sell order`
92
+ - `place market buy order`
93
+ - `place market sell order`
94
+ - `cancel (limit) order`
95
+ - `get order information`
96
+ - `get account balance`
97
+
98
+ Below is an example implementation of an RSI strategy:
99
+
100
+ ```python
101
+ from dijkies.executors import BacktestExchangeAssetClient
102
+ from dijkies.exchange_market_api import BitvavoMarketAPI
103
+ from dijkies.interfaces import (
104
+ Strategy,
105
+ DataPipeline,
106
+ ExchangeAssetClient
107
+ )
108
+ from dijkies.entities import State
109
+ from dijkies.data_pipeline import OHLCVDataPipeline
110
+
111
+ from ta.momentum import RSIIndicator
112
+ from pandas.core.frame import DataFrame as PandasDataFrame
113
+
114
+
115
+ class RSIStrategy(Strategy):
116
+ analysis_dataframe_size_in_minutes = 60*24*30
117
+ min_order_amount = 10
118
+
119
+ def __init__(
120
+ self,
121
+ executor: ExchangeAssetClient,
122
+ lower_threshold: float,
123
+ higher_threshold: float,
124
+ ) -> None:
125
+ self.lower_threshold = lower_threshold
126
+ self.higher_threshold = higher_threshold
127
+ super().__init__(executor)
128
+
129
+ def execute(self, candle_df: PandasDataFrame) -> None:
130
+ candle_df["momentum_rsi"] = RSIIndicator(candle_df.close).rsi()
131
+
132
+ previous_candle = candle_df.iloc[-2]
133
+ current_candle = candle_df.iloc[-1]
134
+
135
+ is_buy_signal = (
136
+ previous_candle.momentum_rsi > self.lower_threshold
137
+ and current_candle.momentum_rsi < self.lower_threshold
138
+ )
139
+
140
+ if (
141
+ is_buy_signal and
142
+ self.state.quote_available > self.min_order_amount
143
+ ):
144
+ self.executor.place_market_buy_order(self.state.quote_available)
145
+
146
+ is_sell_signal = (
147
+ previous_candle.momentum_rsi < self.higher_threshold
148
+ and current_candle.momentum_rsi > self.higher_threshold
149
+ )
150
+
151
+ if (
152
+ is_sell_signal and
153
+ self.state.base_available * candle_df.iloc[-1].close > self.min_order_amount
154
+ ):
155
+ self.executor.place_market_sell_order(self.state.base_available)
156
+
157
+ def get_data_pipeline(self) -> DataPipeline:
158
+ return OHLCVDataPipeline(
159
+ BitvavoMarketAPI(),
160
+ self.state.base,
161
+ 60,
162
+ 60*24*7
163
+ )
164
+ ```
165
+
166
+ ### 2. fetch data for your backtest
167
+ Market data is provided as a pandas DataFrame containing OHLCV candles.
168
+
169
+ ```python
170
+ from dijkies.exchange_market_api import BitvavoMarketAPI
171
+
172
+ bitvavo_market_api = BitvavoMarketAPI()
173
+
174
+ candle_df = bitvavo_market_api.get_candles()
175
+ ```
176
+
177
+ ### 3. Set Up State and BacktestingExecutor
178
+ final steps involve initializing a state and backtest-executor. The state keeps track of the assets that the strategy is managing.
179
+ This is in sync with the real state of the account at the exchange and is used as information source in decision making.
180
+ The backtest executor is a Mock for the execution of actions. This backtest executor is replaced by a real exchange executor in live trading. The backtest method returns a Pandas dataframe containing all important information about the backtest. For instance, a timeseries of the amount of assets, which buy orders are open, total amount of transactions made so far. the full list can be found in the performance module.
181
+
182
+
183
+ ```python
184
+ # do backtest
185
+
186
+ fee_limit_order = 0.0015
187
+ fee_market_order = 0.0025
188
+
189
+ start_investment_base = 0
190
+ start_investment_quote = 1000
191
+
192
+ state = State(
193
+ base="XRP",
194
+ total_base=start_investment_base,
195
+ total_quote=start_investment_quote
196
+ )
197
+
198
+ executor = BacktestExchangeAssetClient(
199
+ state,
200
+ fee_limit_order=fee_limit_order,
201
+ fee_market_order=fee_market_order
202
+ )
203
+
204
+ strategy = RSIStrategy(
205
+ executor,
206
+ 35,
207
+ 65,
208
+ )
209
+
210
+ results = strategy.backtest(candle_df)
211
+ ```
212
+
213
+ ## Deployment & Live Trading
214
+
215
+ Dijkies supports deploying strategies to live trading environments using the **same strategy code** that is used for backtesting. Deployment is built around a small set of composable components that handle persistence, credentials, execution switching, and bot lifecycle management.
216
+
217
+ At a high level, deployment works by:
218
+
219
+ 1. Persisting a configured strategy
220
+ 2. Attaching a live exchange executor
221
+ 3. Running the strategy via a `Bot`
222
+ 4. Managing lifecycle states such as *active*, *paused*, and *stopped*
223
+
224
+ ---
225
+
226
+ ## Core Deployment Concepts
227
+
228
+ ### Strategy Persistence
229
+
230
+ Strategies are **serialized and stored** so they can be resumed, paused, or stopped without losing state.
231
+
232
+ This includes:
233
+ - Strategy parameters
234
+ - Internal indicators or buffers
235
+ - Account state (balances, open orders, etc.)
236
+
237
+ Persistence is handled through a `StrategyRepository`.
238
+
239
+ ---
240
+
241
+ ### Strategy Status
242
+
243
+ Each deployed strategy (bot) exists in one of the following states:
244
+
245
+ - **active** — strategy is running normally
246
+ - **paused** — strategy execution stopped due to an error
247
+ - **stopped** — strategy has been intentionally stopped
248
+
249
+ Status transitions are managed automatically by the deployment system.
250
+
251
+ ---
252
+
253
+ ### Executor Switching
254
+
255
+ One of Dijkies’ key design goals is that **strategies do not know whether they are backtesting or live trading**.
256
+
257
+ At deployment time, the executor is injected dynamically:
258
+
259
+ - `BacktestExchangeAssetClient` for backtesting
260
+ - `BitvavoExchangeAssetClient` for live trading
261
+
262
+ No strategy code changes are required.
263
+
264
+ ---
265
+
266
+ ## Strategy Repository
267
+
268
+ The `StrategyRepository` abstraction defines how strategies are stored and retrieved.
269
+
270
+ ```python
271
+ class StrategyRepository(ABC):
272
+ def store(...)
273
+ def read(...)
274
+ def change_status(...)
275
+ ```
276
+
277
+ ### LocalStrategyRepository
278
+
279
+ The provided implementation stores strategies locally using pickle.
280
+
281
+ #### Directory Structure
282
+
283
+ root/
284
+ └── person_id/
285
+ └── exchange/
286
+ └── status/
287
+ └── bot_id.pkl
288
+
289
+ ```python
290
+ from pathlib import Path
291
+ from dijkies.deployment import LocalStrategyRepository
292
+
293
+ repo = LocalStrategyRepository(Path("./strategies"))
294
+
295
+ # read
296
+
297
+ strategy = repo.read(
298
+ person_id="ArnoldDijk",
299
+ exchange="bitvavo",
300
+ bot_id="berend_botje",
301
+ status="active"
302
+ )
303
+
304
+ # store
305
+
306
+ repo.store(
307
+ strategy=strategy,
308
+ person_id="ArnoldDijk",
309
+ exchange="bitvavo",
310
+ bot_id="berend_botje",
311
+ status="active"
312
+ )
313
+
314
+ # change status
315
+
316
+ repo.change_status(
317
+ person_id="ArnoldDijk",
318
+ exchange="bitvavo",
319
+ bot_id="berend_botje",
320
+ from_status="active",
321
+ to_status="stopped",
322
+ )
323
+ ```
324
+
325
+ This makes it easy to:
326
+
327
+ - Resume bots after restarts
328
+ - Inspect stored strategies
329
+ - Build higher-level orchestration around the filesystem
330
+
331
+ ## Credentials Management
332
+
333
+ Live trading requires exchange credentials. These are abstracted behind a CredentialsRepository.
334
+
335
+ ```python
336
+ class CredentialsRepository(ABC):
337
+ def get_api_key(...)
338
+ def get_api_secret_key(...)
339
+ ```
340
+
341
+ The local implementation retrieves credentials from environment variables:
342
+
343
+ ```bash
344
+ export ArnoldDijk_bitvavo_api_key="..."
345
+ export ArnoldDijk_bitvavo_api_secret_key="..."
346
+ ```
347
+
348
+ ```python
349
+ from dijkies.deployment import LocalCredentialsRepository
350
+
351
+ credentials_repository = LocalCredentialsRepository()
352
+ bitvavo_api_key = credentials_repository.get_api_key(
353
+ person_id="ArnoldDijk",
354
+ exchange="bitvavo"
355
+ )
356
+ ```
357
+
358
+ This keeps secrets out of source code and allows standard deployment practices (Docker, CI/CD, etc.).
359
+
360
+ ## The Bot
361
+
362
+ The Bot class is the runtime orchestrator responsible for:
363
+
364
+ - Loading a stored strategy
365
+ - Injecting the correct executor
366
+ - Running or stopping the strategy
367
+ - Handling failures and state transitions
368
+
369
+ ### running the bot
370
+
371
+ ```python
372
+ bot.run(
373
+ person_id="ArnoldDijk",
374
+ exchange="bitvavo",
375
+ bot_id="berend_botje",
376
+ status="active",
377
+ )
378
+ ```
379
+
380
+ What happens internally:
381
+
382
+ 1. The state of the strategy is loaded from the repository
383
+ 2. The executor is replaced with a live exchange client
384
+ 3. The strategy’s data pipeline is executed
385
+ 4. strategy.run() is called
386
+ 5. The new state of the strategy is persisted
387
+
388
+ If an exception occurs:
389
+ 1. The strategy is stored
390
+ 2. The bot is automatically moved to paused
391
+
392
+ ### Stopping a Bot
393
+
394
+ Bots can be stopped gracefully using the stop method.
395
+
396
+ ```python
397
+ bot.stop(
398
+ person_id="ArnoldDijk",
399
+ exchange="bitvavo",
400
+ bot_id="berend_botje",
401
+ status="active",
402
+ asset_handling="quote_only",
403
+ )
404
+ ```
405
+
406
+ #### Asset Handling Options
407
+
408
+ When stopping a bot, you must specify how assets should be handled:
409
+
410
+ `quote_only`
411
+ Sell all base assets and remain in quote currency
412
+
413
+ `base_only`
414
+ Buy base assets using all available quote currency
415
+
416
+ `ignore`
417
+ Leave balances unchanged
418
+
419
+ Before stopping, the bot:
420
+
421
+ 1. Cancels all open orders
422
+ 2. Handles assets according to the selected mode
423
+ 3. Persists the final state
424
+ 4. Moves the bot to stopped
425
+
426
+ If anything fails, the bot is moved to paused.
427
+
428
+ ## Deployment locally Quickstart
429
+
430
+ In this example, we will continue from the earlier defined rsi strategy.
431
+ we ended at the moment we executed the backtest. Now suppose we decide to use this algorithm with real money.
432
+ Then we have to deploy the strategy. In this example we will deploy locally.
433
+
434
+ ### Step 1: Prepare the Strategy for Deployment -> Create a Strategy Repository and store your strategy
435
+
436
+
437
+ ```python
438
+ from pathlib import Path
439
+ from dijkies.deployment import LocalStrategyRepository
440
+
441
+ strategy_repository = LocalStrategyRepository(
442
+ root_directory=Path("./strategies")
443
+ )
444
+
445
+ # adjust state to what you want to invest.
446
+
447
+ strategy.state = State(
448
+ base="BTC",
449
+ total_base=0,
450
+ total_quote=13 # let's invest 13 euros initially
451
+ )
452
+
453
+ strategy_repository.store(
454
+ strategy=strategy,
455
+ person_id="ArnoldDijk",
456
+ exchange="bitvavo",
457
+ bot_id="berend_botje",
458
+ status="active",
459
+ )
460
+ ```
461
+
462
+ This serializes the strategy and its state so it can be resumed later.
463
+
464
+ ### Step 2: Configure Exchange Credentials
465
+
466
+ Set your exchange credentials as environment variables:
467
+
468
+ ```bash
469
+ export ArnoldDijk_bitvavo_api_key="foo"
470
+ export ArnoldDijk_bitvavo_api_secret_key="bar"
471
+ ```
472
+
473
+ ### Step 3: Create the Bot Runtime
474
+
475
+ The Bot orchestrates loading, execution, and lifecycle management.
476
+
477
+ ```python
478
+ from dijkies.deployment import Bot, LocalCredentialsRepository
479
+
480
+ credentials_repository = LocalCredentialsRepository()
481
+
482
+ bot = Bot(
483
+ strategy_repository=strategy_repository,
484
+ credential_repository=credentials_repository,
485
+ )
486
+ ```
487
+
488
+ ### Step 4: Run the Strategy Live
489
+
490
+ start the live trading bot
491
+
492
+ ```python
493
+ bot.run(
494
+ person_id="ArnoldDijk",
495
+ exchange="bitvavo",
496
+ bot_id="berend_botje",
497
+ status="active",
498
+ )
499
+ ```
500
+
501
+ What Happens Under the Hood:
502
+ 1. The strategy is loaded from disk
503
+ 2. The backtest executor is replaced with BitvavoExchangeAssetClient where API credentials are injected
504
+ 3. The strategy’s data pipeline fetches live market data
505
+ 4. strategy.run() executes decision logic. Here, orders are executed on the exchange and state is modified accordingly
506
+ 5. strategy is persisted, executor and credentials not included.
507
+
508
+ If an exception occurs, the bot is automatically moved to paused.
509
+
510
+ the strategy should be run repeatedly every, say, 60 minutes. There are plenty of ways to accomplish this, and below is a very
511
+ basic example:
512
+
513
+ ```python
514
+ import time
515
+ from datetime import datetime, timezone
516
+
517
+ while True:
518
+ try:
519
+ print("running bot cycle at ", datetime.now(tz=timezone.utc))
520
+ bot.run(
521
+ person_id="ArnoldDijk",
522
+ exchange="bitvavo",
523
+ bot_id="berend_botje",
524
+ status="active",
525
+ )
526
+ print("bot cycle finished")
527
+ except Exception as e:
528
+ print("an error occured: ", e)
529
+
530
+ t = datetime.now(tz=timezone.utc)
531
+ minutes_left = 60 - t.minute
532
+ time.sleep((minutes_left - 1) * 60 + (60 - t.second))
533
+
534
+ ```
535
+
536
+ However, it is much better to use orchestration tools like Apache Airflow. Many bots can be run in parallel using the fan-in/fan-out principle.