dijkies 0.0.3__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/__init__.py +4 -0
- dijkies/backtest.py +98 -0
- dijkies/credentials.py +6 -0
- dijkies/data_pipeline.py +15 -0
- dijkies/deployment.py +127 -0
- dijkies/evaluate.py +235 -0
- dijkies/exceptions.py +60 -0
- dijkies/exchange_market_api.py +235 -0
- dijkies/executors.py +665 -0
- dijkies/logger.py +16 -0
- dijkies/performance.py +166 -0
- dijkies/strategy.py +68 -0
- dijkies-0.0.3.dist-info/METADATA +191 -0
- dijkies-0.0.3.dist-info/RECORD +15 -0
- dijkies-0.0.3.dist-info/WHEEL +4 -0
dijkies/performance.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from pandas.core.series import Series as PandasSeries
|
|
5
|
+
|
|
6
|
+
import uuid
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
from pandas.core.series import Series
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
from dijkies.executors import State
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PerformanceInformationRow(BaseModel):
|
|
16
|
+
id: str = str(uuid.uuid4())
|
|
17
|
+
candle_time: datetime
|
|
18
|
+
candle_open: float
|
|
19
|
+
candle_high: float
|
|
20
|
+
candle_low: float
|
|
21
|
+
candle_close: float
|
|
22
|
+
buy_orders: list = []
|
|
23
|
+
sell_orders: list = []
|
|
24
|
+
balance_total_base: float
|
|
25
|
+
balance_total_quote: float
|
|
26
|
+
balance_available_base: float
|
|
27
|
+
balance_available_quote: float
|
|
28
|
+
balance_base_on_hold: float
|
|
29
|
+
balance_quote_on_hold: float
|
|
30
|
+
total_fee_paid: float
|
|
31
|
+
total_value_strategy: float
|
|
32
|
+
roi_strategy: float
|
|
33
|
+
roi_strategy_per_month: float
|
|
34
|
+
total_value_hodl: float
|
|
35
|
+
roi_hodl: float
|
|
36
|
+
roi_hodl_per_month: float
|
|
37
|
+
number_of_transactions: int
|
|
38
|
+
absolute_profit: float
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_objects(
|
|
42
|
+
cls,
|
|
43
|
+
candle: Series,
|
|
44
|
+
start_candle: Series,
|
|
45
|
+
state: State,
|
|
46
|
+
initialization_value_in_quote: float,
|
|
47
|
+
) -> "PerformanceInformationRow":
|
|
48
|
+
strategy_value = state.total_value_in_quote(candle.close)
|
|
49
|
+
hodl_value = (candle.close / start_candle.open) * initialization_value_in_quote
|
|
50
|
+
|
|
51
|
+
roi_strategy = ((strategy_value / initialization_value_in_quote) - 1) * 100
|
|
52
|
+
roi_hodl = ((candle.close / start_candle.open) - 1) * 100
|
|
53
|
+
|
|
54
|
+
duration_in_months = (candle.time - start_candle.time).total_seconds() / (
|
|
55
|
+
60 * 60 * 24 * 30
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
strategy_per_month = (roi_strategy / 100 + 1) ** (
|
|
59
|
+
1 / max(duration_in_months, 0.01)
|
|
60
|
+
)
|
|
61
|
+
hodl_per_month = (roi_hodl / 100 + 1) ** (1 / max(duration_in_months, 0.01))
|
|
62
|
+
|
|
63
|
+
return cls(
|
|
64
|
+
candle_time=candle.time,
|
|
65
|
+
candle_open=candle.open,
|
|
66
|
+
candle_high=candle.high,
|
|
67
|
+
candle_low=candle.low,
|
|
68
|
+
candle_close=candle.close,
|
|
69
|
+
buy_orders=state.buy_orders,
|
|
70
|
+
sell_orders=state.sell_orders,
|
|
71
|
+
balance_total_base=state.total_base,
|
|
72
|
+
balance_total_quote=state.total_quote,
|
|
73
|
+
balance_available_base=state.base_available,
|
|
74
|
+
balance_available_quote=state.quote_available,
|
|
75
|
+
balance_base_on_hold=state.base_on_hold,
|
|
76
|
+
balance_quote_on_hold=state.quote_on_hold,
|
|
77
|
+
total_fee_paid=state.total_fee_paid,
|
|
78
|
+
total_value_strategy=strategy_value,
|
|
79
|
+
roi_strategy=roi_strategy,
|
|
80
|
+
roi_strategy_per_month=strategy_per_month,
|
|
81
|
+
total_value_hodl=hodl_value,
|
|
82
|
+
roi_hodl=roi_hodl,
|
|
83
|
+
roi_hodl_per_month=hodl_per_month,
|
|
84
|
+
number_of_transactions=state.number_of_transactions,
|
|
85
|
+
absolute_profit=strategy_value - initialization_value_in_quote,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Metric(ABC):
|
|
90
|
+
@property
|
|
91
|
+
@abstractmethod
|
|
92
|
+
def metric_name(self) -> str:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
@abstractmethod
|
|
96
|
+
def calculate(self, time_series: PandasSeries) -> float:
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class DrawDown(Metric):
|
|
101
|
+
@property
|
|
102
|
+
def metric_name(self) -> str:
|
|
103
|
+
return "draw_down"
|
|
104
|
+
|
|
105
|
+
def calculate(self, time_series: PandasSeries) -> float:
|
|
106
|
+
returns = time_series.pct_change()
|
|
107
|
+
returns.fillna(0.0, inplace=True)
|
|
108
|
+
|
|
109
|
+
cumulative = (returns + 1).cumprod()
|
|
110
|
+
running_max = np.maximum.accumulate(cumulative)
|
|
111
|
+
|
|
112
|
+
result = ((cumulative - running_max) / running_max).min() * 100
|
|
113
|
+
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class ReturnOnInvestment(Metric):
|
|
118
|
+
@property
|
|
119
|
+
def metric_name(self) -> str:
|
|
120
|
+
return "roi"
|
|
121
|
+
|
|
122
|
+
def calculate(self, time_series: PandasSeries) -> float:
|
|
123
|
+
return ((time_series.iloc[-1] / time_series.iloc[0]) - 1) * 100
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class NormalizedReturnOnInvestment(Metric):
|
|
127
|
+
def __init__(self, candle_interval_in_minutes: int) -> None:
|
|
128
|
+
self.candle_interval_in_minutes = candle_interval_in_minutes
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def metric_name(self) -> str:
|
|
132
|
+
return "normalized_roi"
|
|
133
|
+
|
|
134
|
+
def calculate(self, time_series: PandasSeries) -> float:
|
|
135
|
+
"""
|
|
136
|
+
returns the average return per month.
|
|
137
|
+
"""
|
|
138
|
+
total_time_in_minutes = len(time_series) * self.candle_interval_in_minutes
|
|
139
|
+
total_time_in_months = total_time_in_minutes / (60 * 24 * 30)
|
|
140
|
+
|
|
141
|
+
total_return = time_series.iloc[-1] / time_series.iloc[0]
|
|
142
|
+
return_per_month = total_return ** (min(1 / total_time_in_months, 1))
|
|
143
|
+
|
|
144
|
+
return (return_per_month - 1) * 100
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SharpeRatio(Metric):
|
|
148
|
+
def __init__(
|
|
149
|
+
self, risk_free_rate_per_year: float, candle_interval_in_minutes: int
|
|
150
|
+
) -> None:
|
|
151
|
+
self.risk_free_rate_per_year = risk_free_rate_per_year
|
|
152
|
+
self.candle_interval_in_minutes = candle_interval_in_minutes
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def metric_name(self) -> str:
|
|
156
|
+
return "sharpe_ratio"
|
|
157
|
+
|
|
158
|
+
def calculate(self, time_series: PandasSeries) -> float:
|
|
159
|
+
risk_free_rate_per_day = (1 + self.risk_free_rate_per_year) ** (1 / 365) - 1
|
|
160
|
+
measurements_per_day = 60 * 24 / self.candle_interval_in_minutes
|
|
161
|
+
normalized_std_return = risk_free_rate_per_day / max(measurements_per_day, 1)
|
|
162
|
+
returns = time_series.pct_change()
|
|
163
|
+
excess_return = returns.mean() - normalized_std_return
|
|
164
|
+
volatility = max(returns.std(), 0.00001)
|
|
165
|
+
sharpe_ratio = excess_return / volatility
|
|
166
|
+
return sharpe_ratio * np.sqrt(measurements_per_day)
|
dijkies/strategy.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
from pandas.core.frame import DataFrame as PandasDataFrame
|
|
5
|
+
|
|
6
|
+
from dijkies.executors import ExchangeAssetClient
|
|
7
|
+
from dijkies.data_pipeline import DataPipeline
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Strategy(ABC):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
executor: ExchangeAssetClient,
|
|
14
|
+
) -> None:
|
|
15
|
+
self.executor = executor
|
|
16
|
+
self.state = self.executor.state
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def execute(self, data: PandasDataFrame) -> None:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def run(self, data: PandasDataFrame) -> None:
|
|
23
|
+
self.executor.update_state()
|
|
24
|
+
self.execute(data)
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def _get_strategy_params(cls) -> list[str]:
|
|
28
|
+
subclass_sig = inspect.signature(cls.__init__)
|
|
29
|
+
base_sig = inspect.signature(Strategy.__init__)
|
|
30
|
+
|
|
31
|
+
subclass_params = {
|
|
32
|
+
name: p for name, p in subclass_sig.parameters.items() if name != "self"
|
|
33
|
+
}
|
|
34
|
+
base_params = {
|
|
35
|
+
name: p for name, p in base_sig.parameters.items() if name != "self"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
unique_params = {
|
|
39
|
+
name: p for name, p in subclass_params.items() if name not in base_params
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return list(unique_params.keys())
|
|
43
|
+
|
|
44
|
+
def params_to_json(self):
|
|
45
|
+
params = self._get_strategy_params()
|
|
46
|
+
return {p: getattr(self, p) for p in params}
|
|
47
|
+
|
|
48
|
+
def __getstate__(self):
|
|
49
|
+
state = self.__dict__.copy()
|
|
50
|
+
state["executor"] = None
|
|
51
|
+
state["data_pipeline"] = None
|
|
52
|
+
return state
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def analysis_dataframe_size_in_minutes(self) -> int:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def exchange(self) -> str:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
def get_data_pipeline(self) -> DataPipeline:
|
|
65
|
+
"""
|
|
66
|
+
implement this method for deployement
|
|
67
|
+
"""
|
|
68
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: dijkies
|
|
3
|
+
Version: 0.0.3
|
|
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: mlflow>=3.7.0
|
|
9
|
+
Requires-Dist: pandas>=2.3.3
|
|
10
|
+
Requires-Dist: pre-commit>=4.5.0
|
|
11
|
+
Requires-Dist: pydantic>=2.12.5
|
|
12
|
+
Requires-Dist: pytest>=9.0.2
|
|
13
|
+
Requires-Dist: python-binance>=1.0.33
|
|
14
|
+
Requires-Dist: python-bitvavo-api>=1.4.3
|
|
15
|
+
Requires-Dist: ta>=0.11.0
|
|
16
|
+
Requires-Dist: tenacity>=9.1.2
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Dijkies
|
|
21
|
+
|
|
22
|
+
**Dijkies** is a Python framework for creating, testing, and deploying algorithmic trading strategies in a clean, modular, and exchange-agnostic way.
|
|
23
|
+
|
|
24
|
+
The core idea behind Dijkies is to **separate trading logic from execution and infrastructure**, allowing the same strategy code to be reused for:
|
|
25
|
+
|
|
26
|
+
- Historical backtesting
|
|
27
|
+
- Paper trading
|
|
28
|
+
- Live trading
|
|
29
|
+
|
|
30
|
+
## Philosophy
|
|
31
|
+
|
|
32
|
+
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.
|
|
33
|
+
|
|
34
|
+
This separation ensures that strategies remain:
|
|
35
|
+
|
|
36
|
+
- Easy to reason about
|
|
37
|
+
- Easy to test
|
|
38
|
+
- Easy to reuse across environments
|
|
39
|
+
|
|
40
|
+
A strategy written once can be backtested on historical data and later deployed to a real exchange without modification.
|
|
41
|
+
|
|
42
|
+
## How It Works
|
|
43
|
+
|
|
44
|
+
At a high level, Dijkies operates as follows:
|
|
45
|
+
|
|
46
|
+
1. Market data (candles) is fetched from an exchange or data provider
|
|
47
|
+
2. A rolling window of historical data is passed to a strategy
|
|
48
|
+
3. The strategy analyzes the data and generates buy/sell signals
|
|
49
|
+
4. Orders are placed through a standardized execution interface
|
|
50
|
+
5. Account state is updated accordingly
|
|
51
|
+
6. Results are collected (during backtesting) or executed live
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Key Design Principles
|
|
55
|
+
|
|
56
|
+
- **Strategy–Executor separation**
|
|
57
|
+
Trading logic is completely decoupled from execution logic.
|
|
58
|
+
|
|
59
|
+
- **Single interface for backtesting and live trading**
|
|
60
|
+
Switching between backtesting and live trading requires no strategy changes.
|
|
61
|
+
|
|
62
|
+
- **Explicit state management**
|
|
63
|
+
All balances and positions are tracked in a transparent `State` object.
|
|
64
|
+
|
|
65
|
+
- **Minimal assumptions**
|
|
66
|
+
Dijkies does not enforce indicators, timeframes, or asset types.
|
|
67
|
+
|
|
68
|
+
- **Composable and extensible**
|
|
69
|
+
New exchanges, execution models, and risk layers can be added easily.
|
|
70
|
+
|
|
71
|
+
## Who Is This For?
|
|
72
|
+
|
|
73
|
+
Dijkies is designed for:
|
|
74
|
+
|
|
75
|
+
- Developers building algorithmic trading systems
|
|
76
|
+
- Quantitative traders who want full control over strategy logic
|
|
77
|
+
- Anyone who wants to move from backtesting to production without rewriting code
|
|
78
|
+
|
|
79
|
+
## What Dijkies Is Not
|
|
80
|
+
|
|
81
|
+
- A no-code trading bot
|
|
82
|
+
- A black-box strategy optimizer
|
|
83
|
+
- A fully managed trading platform
|
|
84
|
+
|
|
85
|
+
Dijkies provides the **building blocks**, not the trading edge.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
This quick start shows how to define a strategy, fetch market data, and run a backtest in just a few steps.
|
|
92
|
+
|
|
93
|
+
### 1. Define a Strategy
|
|
94
|
+
|
|
95
|
+
A strategy is a class that inherits from `Strategy` and implements the `execute` method.
|
|
96
|
+
It receives a rolling dataframe of candles and decides when to place orders.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from dijkies.strategy import Strategy
|
|
100
|
+
from dijkies.executors import ExchangeAssetClient
|
|
101
|
+
from ta.momentum import RSIIndicator
|
|
102
|
+
import pandas as pd
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class RSIStrategy(Strategy):
|
|
106
|
+
# Amount of historical data passed into execute()
|
|
107
|
+
analysis_dataframe_size_in_minutes = 60 * 24 * 30 # 30 days
|
|
108
|
+
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
executor: ExchangeAssetClient,
|
|
112
|
+
lower_threshold: float,
|
|
113
|
+
higher_threshold: float,
|
|
114
|
+
) -> None:
|
|
115
|
+
self.lower_threshold = lower_threshold
|
|
116
|
+
self.higher_threshold = higher_threshold
|
|
117
|
+
super().__init__(executor)
|
|
118
|
+
|
|
119
|
+
def execute(self, candle_df: pd.DataFrame) -> None:
|
|
120
|
+
candle_df["rsi"] = RSIIndicator(candle_df.close).rsi()
|
|
121
|
+
|
|
122
|
+
previous = candle_df.iloc[-2]
|
|
123
|
+
current = candle_df.iloc[-1]
|
|
124
|
+
|
|
125
|
+
# Buy when RSI crosses below lower threshold
|
|
126
|
+
if previous.rsi > self.lower_threshold and current.rsi < self.lower_threshold:
|
|
127
|
+
self.executor.place_market_buy_order(
|
|
128
|
+
self.executor.state.base,
|
|
129
|
+
self.executor.state.quote_available,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Sell when RSI crosses above higher threshold
|
|
133
|
+
if previous.rsi < self.higher_threshold and current.rsi > self.higher_threshold:
|
|
134
|
+
self.executor.place_market_sell_order(
|
|
135
|
+
self.executor.state.base,
|
|
136
|
+
self.executor.state.base_available,
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 2. fetch data for your backtest
|
|
141
|
+
Market data is provided as a pandas DataFrame containing OHLCV candles.
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from dijkies.exchange_market_api import BitvavoMarketAPI
|
|
145
|
+
|
|
146
|
+
market_api = BitvavoMarketAPI()
|
|
147
|
+
candle_df = market_api.get_candles(base="XRP", lookback_in_minutest=60*24*365)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 3. Set Up State and BacktestingExecutor
|
|
151
|
+
Market data is provided as a pandas DataFrame containing OHLCV candles.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from dijkies.executors import BacktestExchangeAssetClient, State
|
|
155
|
+
|
|
156
|
+
state = State(
|
|
157
|
+
base="XRP",
|
|
158
|
+
total_base=0,
|
|
159
|
+
total_quote=1000,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
executor = BacktestExchangeAssetClient(
|
|
163
|
+
state=state,
|
|
164
|
+
fee_limit_order=0.0015,
|
|
165
|
+
fee_market_order=0.0025,
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 4. Run the Backtest
|
|
170
|
+
|
|
171
|
+
Use the Backtester to run the strategy over historical data.
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from dijkies.backtest import Backtester
|
|
175
|
+
|
|
176
|
+
strategy = RSIStrategy(
|
|
177
|
+
executor=executor,
|
|
178
|
+
lower_threshold=35,
|
|
179
|
+
higher_threshold=65,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
backtester = Backtester()
|
|
183
|
+
|
|
184
|
+
results = backtester.run(
|
|
185
|
+
candle_df=candle_df,
|
|
186
|
+
strategy=strategy,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
results.total_value_strategy.plot()
|
|
190
|
+
results.total_value_hodl.plot()
|
|
191
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
dijkies/__init__.py,sha256=FmmEOqu9R1gE1wBWWIrAIFTF1rI5sEjpsCdrw4RC5Z8,167
|
|
2
|
+
dijkies/backtest.py,sha256=Z3U8pijdNwGh_h4dlPtORk3znIOBlTKOaGR-IK-swys,3206
|
|
3
|
+
dijkies/credentials.py,sha256=pwhRsTFnaDOinnRwnplnxAIDS778dPd2awFDmjo3dpY,104
|
|
4
|
+
dijkies/data_pipeline.py,sha256=SK5NCpI5okhwiPOAPTzoDVLgqPH6SGPsT4khNXcf7jI,313
|
|
5
|
+
dijkies/deployment.py,sha256=N8xtF2YTU1wA0RAcwVmJEaXKZnNH94XjutIs7aOFyYo,3638
|
|
6
|
+
dijkies/evaluate.py,sha256=52H87f4E7-1eTDN2CLSKkswtydTIeDSMZdCoXo56woA,8064
|
|
7
|
+
dijkies/exceptions.py,sha256=BLqK0-rmpqx3wtZ8abu19u8ClnsAjkcpz4B22p-YYt0,1907
|
|
8
|
+
dijkies/exchange_market_api.py,sha256=ZH2gD_Z7PkX3KLT1GtG20Xr-bacj4B3IGsJNP7Hz5AE,7606
|
|
9
|
+
dijkies/executors.py,sha256=7GJznhlv2WMP6UjqQeE6s9VQsvzD0Y0okXdJd4rmMdk,20896
|
|
10
|
+
dijkies/logger.py,sha256=yrxNyzriXO_WITRsIEWkVQeqLRavHVklFainDO18p7Y,432
|
|
11
|
+
dijkies/performance.py,sha256=1EoGx0_riiAyOP8G7UQcYwmjt8b8hw4c3UzAgsbpOuE,5416
|
|
12
|
+
dijkies/strategy.py,sha256=4UZpuZcDvtol6RbarUw-K846bZritSDqDirz9w8ycPw,1804
|
|
13
|
+
dijkies-0.0.3.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
|
|
14
|
+
dijkies-0.0.3.dist-info/METADATA,sha256=zgh4Zr4w3-yFTLg0le_rKDHMIsMpltd68ZT9AAkTx1w,5717
|
|
15
|
+
dijkies-0.0.3.dist-info/RECORD,,
|