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 +536 -0
- dijkies-0.1.6/README.md +518 -0
- dijkies-0.1.6/pyproject.toml +34 -0
- dijkies-0.1.6/src/dijkies/__init__.py +0 -0
- dijkies-0.1.6/src/dijkies/constants.py +5 -0
- dijkies-0.1.6/src/dijkies/data_pipeline.py +40 -0
- dijkies-0.1.6/src/dijkies/deployment.py +165 -0
- dijkies-0.1.6/src/dijkies/entities.py +149 -0
- dijkies-0.1.6/src/dijkies/exceptions.py +95 -0
- dijkies-0.1.6/src/dijkies/exchange_market_api.py +217 -0
- dijkies-0.1.6/src/dijkies/executors.py +548 -0
- dijkies-0.1.6/src/dijkies/interfaces.py +258 -0
- dijkies-0.1.6/src/dijkies/performance.py +153 -0
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
|
+

|
|
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.
|