forex-data-aggregator 0.1.5__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Nicola Fiorato
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,537 @@
1
+ Metadata-Version: 2.4
2
+ Name: forex_data_aggregator
3
+ Version: 0.1.5
4
+ Summary: Data aggregation for forex market data
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Nicola Fiorato
8
+ Author-email: fiorato.nicola@gmail.com
9
+ Requires-Python: >=3.12,<3.13
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: adbc-driver-sqlite (>=1.6.0,<2.0.0)
14
+ Requires-Dist: alpha-vantage (>=2.3.1,<3.0.0)
15
+ Requires-Dist: bs4 (>=0.0.2,<0.0.3)
16
+ Requires-Dist: connectorx (>=0.4.3,<0.5.0)
17
+ Requires-Dist: debugpy (>=1.8.5,<2.0.0)
18
+ Requires-Dist: dotty-dict (>=1.3.1,<2.0.0)
19
+ Requires-Dist: duckdb (>=1.3.1,<2.0.0)
20
+ Requires-Dist: flake8 (>=7.1.1,<8.0.0)
21
+ Requires-Dist: holidays (>=0.89,<0.90)
22
+ Requires-Dist: iteration-utilities (>=0.12.1,<0.13.0)
23
+ Requires-Dist: loguru (>=0.7.2,<0.8.0)
24
+ Requires-Dist: matplotlib (>=3.9.2)
25
+ Requires-Dist: mpl-finance (>=0.10.1,<0.11.0)
26
+ Requires-Dist: mplfinance (>=0.12.9b7,<0.13.0)
27
+ Requires-Dist: mypy (>=1.14.1,<2.0.0)
28
+ Requires-Dist: numpy (<2.0.0)
29
+ Requires-Dist: pandas (>=2.0.0)
30
+ Requires-Dist: pandas-stubs (>=2.2.3.241126,<3.0.0.0)
31
+ Requires-Dist: polars (>=0.20.7)
32
+ Requires-Dist: polygon-api-client (>=1.5.0,<2.0.0)
33
+ Requires-Dist: pyaml (>=21.10.1,<22.0.0)
34
+ Requires-Dist: pyarrow (>=14.0.2)
35
+ Requires-Dist: pyinstrument (>=5.1.2,<6.0.0)
36
+ Requires-Dist: pytest (>=8.1.1,<9.0.0)
37
+ Requires-Dist: pytest-flake8 (>=1.3.0,<2.0.0)
38
+ Requires-Dist: pytest-mypy (>=0.10.3,<0.11.0)
39
+ Requires-Dist: requests (>=2.28.1,<3.0.0)
40
+ Requires-Dist: scipy (>=1.11.4,<2.0.0)
41
+ Requires-Dist: setuptools (>=69.0.2,<70.0.0)
42
+ Description-Content-Type: text/markdown
43
+
44
+ # <span style="font-size:1.5em;">FOREX DATA</span>
45
+
46
+ [![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue?style=for-the-badge&logo=read-the-docs)](https://nikfio.github.io/forex_data/)
47
+ [![CI Status](https://img.shields.io/circleci/build/github/nikfio/forex_data/master?style=for-the-badge&logo=circleci)](https://circleci.com/gh/nikfio/forex_data)
48
+ [![PyPI version](https://img.shields.io/pypi/v/forex-data-aggregator?style=for-the-badge&logo=pypi)](https://pypi.org/project/forex-data-aggregator/)
49
+ [![Python Version](https://img.shields.io/badge/python-3.12-blue?style=for-the-badge&logo=python)](https://www.python.org/)
50
+ [![Poetry](https://img.shields.io/badge/Poetry-Package%20Manager-blue?style=for-the-badge&logo=poetry)](https://python-poetry.org/)
51
+
52
+ > 📚 **[View Full Documentation](https://nikfio.github.io/forex_data/)** | 🚀 **[Quick Start](#installation)** | 💡 **[Examples](#examples)**
53
+
54
+ The forex_data package offers ways to aggregate data from the Forex market into a dataframe having the the essential OHLC information, so the ouput will always have the columns:
55
+
56
+ * timestamp
57
+ * open
58
+ * high
59
+ * low
60
+ * close
61
+
62
+ The purpose is to aggregate data in OHLC format from multiple sources, optimize data caching and provide an interface to easily access and use the data: the main outcome from the interface is a dataframe.
63
+
64
+ At the moment, sources are divided in **historical sources** and **real-time sources**.
65
+
66
+ ## SOURCES
67
+
68
+ ### HISTORICAL SOURCE
69
+
70
+ A historical source provides data tipically from the first years of 2000s and the free tier is fine for the purposes of the package. The update rate is slow but data can be retrived with a low resolution like 1-minute timeframe.
71
+
72
+ The historical source used in the package is [histdata.com](http://www.histdata.com/), which work is really genuine and a lot appreciated.
73
+
74
+ Summarizing, a historical source can provide tons of data even from many years ago and with no limits at the downside of a slow update rate. For example, *histdata* updates data on a montly basis.
75
+
76
+ ### REAL-TIME SOURCE
77
+
78
+ A real-time source is what is more tipically known as a source for forex market or stock market data. It offers APIs in determined clients or even just a minimal documentation to establish the API call in HTTP request format.
79
+ A minimal free or trial offering is proposed, but they rely on premium subscriptions offers based on:
80
+
81
+ * real time performance
82
+ * size of tickers list available
83
+ * how much history of a ticker
84
+ * and many other parameters ...
85
+
86
+ As of now, just [alpha-vantage](https://www.alphavantage.co/documentation/) and [polygon-io](https://polygon.io/docs/forex/getting-started) are managed. The intention is to make the most out of them and their free tier access to data.
87
+ [Twelve data](https://twelvedata.com/) interface is under development.
88
+
89
+ ## INSTALLATION
90
+
91
+ ### From PyPI (Recommended)
92
+
93
+ The easiest way to install forex_data is via pip:
94
+
95
+ ```bash
96
+ pip install forex-data-aggregator
97
+ ```
98
+
99
+ Or with Poetry:
100
+
101
+ ```bash
102
+ poetry add forex-data-aggregator
103
+ ```
104
+
105
+ ### From Source
106
+
107
+ If you want to install from source or contribute to development:
108
+
109
+ 1. Ensure you have [Poetry](https://python-poetry.org/docs/) installed
110
+ 2. Clone the repository:
111
+ ```bash
112
+ git clone https://github.com/nikfio/forex_data.git -b master forex-data
113
+ cd forex-data
114
+ ```
115
+ 3. Install dependencies:
116
+ ```bash
117
+ poetry install
118
+ ```
119
+ 4. Run tests to verify installation:
120
+ ```bash
121
+ poetry run pytest
122
+ ```
123
+
124
+ ## DOCUMENTATION
125
+
126
+ 📖 **Comprehensive documentation is available at [nikfio.github.io/forex_data](https://nikfio.github.io/forex_data/)**
127
+
128
+ The full documentation includes:
129
+
130
+ - **[Installation Guide](https://nikfio.github.io/forex_data/installation.html)** - Detailed setup instructions
131
+ - **[Quick Start Tutorial](https://nikfio.github.io/forex_data/quickstart.html)** - Get started in minutes
132
+ - **[Configuration Reference](https://nikfio.github.io/forex_data/configuration.html)** - All configuration options explained
133
+ - **[API Reference](https://nikfio.github.io/forex_data/forex_data.html)** - Complete API documentation with type hints
134
+ - **[Code Examples](https://nikfio.github.io/forex_data/examples.html)** - 15+ comprehensive examples
135
+ - **[Contributing Guide](https://nikfio.github.io/forex_data/contributing.html)** - How to contribute to the project
136
+ - **[Changelog](https://nikfio.github.io/forex_data/changelog.html)** - Version history and updates
137
+
138
+ ## CONFIGURATION FILE
139
+
140
+ A configuration file can be passed in order to group fixed parameters values.
141
+ In repository folder clone, look for [appconfig folder](appconfig) to see the [example template file](appconfig/appconfig_template.yaml).
142
+
143
+ In data managers instantiation, you can pass directly the absolute path to the YAML file or also a folder.
144
+ In the second case, it will look for the configuration file ending with `data_config.yaml` in the specified folder.
145
+ Furthermore, any parameter value can be overridden by explicit assignment in object instantion.
146
+ The feature will be more clear following the [examples section](#examples).
147
+
148
+ #### ENGINE
149
+
150
+ Available options:
151
+
152
+ * pandas
153
+ * pyarrow
154
+ * polars
155
+ * polars_lazy
156
+
157
+ #### DATA_FILETYPE
158
+
159
+ Available options:
160
+
161
+ * csv
162
+ * parquet
163
+
164
+ *parquet* filetype is strongly suggested for read/write speed and disk space occupation.
165
+ Meanwhile, if you have any analysis application outside the Python environment, it would more likely accept csv files over parquet: so *csv* filetype could be a better choice for its broader acceptance.
166
+
167
+ #### PROVIDERS_KEY
168
+
169
+ To use real-time sources you need to provide an API key.
170
+
171
+ Look here to register and create a key from Alpha-Vantage provider
172
+ [Alpha-Vantage free API registration](https://www.alphavantage.co/support/#api-key)
173
+
174
+ Look here to register and create a key from Polygon-IO provider
175
+ [Polygon-IO home page](https://polygon.io/)
176
+
177
+ ## LOGGING
178
+
179
+ Logging feature is added via loguru library.
180
+ By construction log is dumped in a file which location is determined by pathlib.
181
+ A generic usage folder for the package named `.database` is created at the current user home folder.
182
+ Here log is dumped in a file called `forexdata.log`, the complete location of the log file will be:
183
+
184
+ `~/.database/forexdata.log`
185
+
186
+ ## EXAMPLES
187
+
188
+ You can find complete working examples in the [examples folder](examples/) showing the various modules and functionalities the package offers.
189
+
190
+ To run the examples:
191
+
192
+ ```bash
193
+ # Historical data example
194
+ poetry run python examples/histdata_db_manager.py
195
+
196
+ # Real-time data example (requires API keys as environment variables)
197
+ export ALPHA_VANTAGE_API_KEY="your_key_here"
198
+ export POLYGON_IO_API_KEY="your_key_here"
199
+ poetry run python examples/realtime_data_manager.py
200
+ ```
201
+
202
+ #### Historical data
203
+
204
+ Let's walk through the [example for historical data source](examples/histdata_db_manager.py):
205
+
206
+ 1. **Configuration setup**
207
+ ```python
208
+ # Use a runtime defined config yaml file
209
+ test_config_yaml = '''
210
+ DATA_FILETYPE: 'parquet'
211
+
212
+ ENGINE: 'polars_lazy'
213
+ '''
214
+ ```
215
+ You can define configuration inline or use a file. The configuration can override specific settings.
216
+ <br>
217
+
218
+ 2. **Data manager instance**
219
+ ```python
220
+ from forex_data import HistoricalManagerDB
221
+
222
+ histmanager = HistoricalManagerDB(
223
+ config=test_config_yaml
224
+ )
225
+ ```
226
+ Create an instance of the historical data manager with your configuration.
227
+ <br>
228
+
229
+ 3. **Get data**
230
+ ```python
231
+ ex_ticker = 'EURUSD'
232
+ ex_timeframe = '1d'
233
+ ex_start_date = '2018-10-03 10:00:00'
234
+ ex_end_date = '2018-12-03 10:00:00'
235
+
236
+ yeardata = histmanager.get_data(
237
+ ticker=ex_ticker,
238
+ timeframe=ex_timeframe,
239
+ start=ex_start_date,
240
+ end=ex_end_date
241
+ )
242
+ ```
243
+ The call returns a dataframe with data having the timeframe, start, and end specified by the inputs.
244
+ The output dataframe type depends on the engine selected (polars_lazy, polars, pandas, pyarrow).
245
+
246
+ With `polars_lazy` as ENGINE option, the output dataframe:
247
+ ```
248
+ ┌─────────────────────┬─────────┬─────────┬─────────┬─────────┐
249
+ │ timestamp ┆ open ┆ high ┆ low ┆ close │
250
+ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
251
+ │ datetime[ms] ┆ f32 ┆ f32 ┆ f32 ┆ f32 │
252
+ ╞═════════════════════╪═════════╪═════════╪═════════╪═════════╡
253
+ │ 2018-10-03 21:00:00 ┆ 1.1523 ┆ 1.1528 ┆ 1.1512 ┆ 1.1516 │
254
+ │ 2018-10-04 21:00:00 ┆ 1.1516 ┆ 1.1539 ┆ 1.1485 ┆ 1.1498 │
255
+ │ 2018-10-05 21:00:00 ┆ 1.1498 ┆ 1.1534 ┆ 1.1486 ┆ 1.1514 │
256
+ │ ... ┆ ... ┆ ... ┆ ... ┆ ... │
257
+ └─────────────────────┴─────────┴─────────┴─────────┴─────────┘
258
+ ```
259
+ <br>
260
+
261
+ 4. **Add a timeframe**
262
+ ```python
263
+ histmanager.add_timeframe('1W')
264
+ ```
265
+ Add a new timeframe. The data manager will create and cache the new timeframe data if not already present.
266
+ <br>
267
+
268
+ 5. **Plot data**
269
+ ```python
270
+ histmanager.plot(
271
+ ticker=ex_ticker,
272
+ timeframe='1D',
273
+ start_date='2016-02-02 18:00:00',
274
+ end_date='2016-06-23 23:00:00'
275
+ )
276
+ ```
277
+ Generate a candlestick chart for the specified ticker and date range.
278
+
279
+ <br>
280
+
281
+ ![output chart](doc/imgs/histdata_test_nzdusd.png)
282
+
283
+ <br>
284
+
285
+ 6. **Conditional Data Retrieval**
286
+
287
+ You can filter data directly during retrieval using SQL-like conditions.
288
+
289
+ ```python
290
+ from forex_data import (
291
+ HistoricalManagerDB,
292
+ BASE_DATA_COLUMN_NAME,
293
+ SQL_COMPARISON_OPERATORS
294
+ )
295
+
296
+ # 1. Simple condition: OPEN < 1.13
297
+ data = histmanager.get_data(
298
+ ticker='EURUSD',
299
+ timeframe='1D',
300
+ start='2018-01-01',
301
+ end='2018-12-31',
302
+ comparison_column_name=BASE_DATA_COLUMN_NAME.OPEN,
303
+ check_level=1.13,
304
+ comparison_operator=SQL_COMPARISON_OPERATORS.LESS_THAN
305
+ )
306
+
307
+ # 2. Multiple conditions (OR): HIGH > 1.145 OR LOW < 1.12
308
+ from forex_data import SQL_CONDITION_AGGREGATION_MODES
309
+
310
+ data = histmanager.get_data(
311
+ ticker='EURUSD',
312
+ timeframe='1D',
313
+ start='2019-01-01',
314
+ end='2019-12-31',
315
+ comparison_column_name=[
316
+ BASE_DATA_COLUMN_NAME.HIGH,
317
+ BASE_DATA_COLUMN_NAME.LOW
318
+ ],
319
+ check_level=[1.145, 1.12],
320
+ comparison_operator=[
321
+ SQL_COMPARISON_OPERATORS.GREATER_THAN,
322
+ SQL_COMPARISON_OPERATORS.LESS_THAN
323
+ ],
324
+ aggregation_mode=SQL_CONDITION_AGGREGATION_MODES.OR
325
+ )
326
+ ```
327
+
328
+ <br>
329
+
330
+ #### Real-Time data
331
+
332
+ Let's walk through the [example for real-time data source](examples/realtime_data_manager.py):
333
+
334
+ **Important:** This example requires API keys set as environment variables:
335
+ ```bash
336
+ export ALPHA_VANTAGE_API_KEY="your_alphavantage_key"
337
+ export POLYGON_IO_API_KEY="your_polygon_io_key"
338
+ ```
339
+
340
+ 1. **Configuration with API keys**
341
+ ```python
342
+ from os import getenv
343
+
344
+ alpha_vantage_key = getenv('ALPHA_VANTAGE_API_KEY')
345
+ polygon_io_key = getenv('POLYGON_IO_API_KEY')
346
+
347
+ test_config_yaml = f'''
348
+ DATA_FILETYPE: 'parquet'
349
+
350
+ ENGINE: 'polars_lazy'
351
+
352
+ PROVIDERS_KEY:
353
+ ALPHA_VANTAGE_API_KEY : {alpha_vantage_key},
354
+ POLYGON_IO_API_KEY : {polygon_io_key}
355
+ '''
356
+ ```
357
+ Configuration includes API keys for real-time data providers.
358
+ <br>
359
+
360
+ 2. **Data manager instance**
361
+ ```python
362
+ from forex_data import RealtimeManager
363
+
364
+ realtimedata_manager = RealtimeManager(
365
+ config=test_config_yaml
366
+ )
367
+ ```
368
+ <br>
369
+
370
+ 3. **Get last daily close**
371
+ ```python
372
+ ex_ticker = 'EURCAD'
373
+
374
+ dayclose_quote = realtimedata_manager.get_daily_close(
375
+ ticker=ex_ticker,
376
+ last_close=True
377
+ )
378
+ ```
379
+
380
+ Output:
381
+ ```
382
+ ┌─────────────────────┬─────────┬─────────┬─────────┬────────┐
383
+ │ timestamp ┆ open ┆ high ┆ low ┆ close │
384
+ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
385
+ │ datetime[ms] ┆ f32 ┆ f32 ┆ f32 ┆ f32 │
386
+ ╞═════════════════════╪═════════╪═════════╪═════════╪════════╡
387
+ │ 2025-01-23 00:00:00 ┆ 1.4123 ┆ 1.4156 ┆ 1.4098 ┆ 1.4125 │
388
+ └─────────────────────┴─────────┴─────────┴─────────┴────────┘
389
+ ```
390
+
391
+ 4. **Get daily close for last N days**
392
+ ```python
393
+ ex_n_days = 13
394
+
395
+ window_daily_ohlc = realtimedata_manager.get_daily_close(
396
+ ticker=ex_ticker,
397
+ recent_days_window=ex_n_days
398
+ )
399
+ ```
400
+ Returns the last 13 days of daily OHLC data.
401
+
402
+ 5. **Get daily close for specific date range**
403
+ ```python
404
+ ex_start_date = '2025-01-15'
405
+ ex_end_date = '2025-01-23'
406
+
407
+ window_limits_daily_ohlc = realtimedata_manager.get_daily_close(
408
+ ticker=ex_ticker,
409
+ day_start=ex_start_date,
410
+ day_end=ex_end_date
411
+ )
412
+ ```
413
+
414
+ Output:
415
+ ```
416
+ ┌─────────────────────┬────────┬────────┬────────┬────────┐
417
+ │ timestamp ┆ open ┆ high ┆ low ┆ close │
418
+ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
419
+ │ datetime[ms] ┆ f32 ┆ f32 ┆ f32 ┆ f32 │
420
+ ╞═════════════════════╪════════╪════════╪════════╪════════╡
421
+ │ 2025-01-23 00:00:00 ┆ 1.4125 ┆ 1.4156 ┆ 1.4098 ┆ 1.4132 │
422
+ │ 2025-01-22 00:00:00 ┆ 1.4089 ┆ 1.4147 ┆ 1.4072 ┆ 1.4125 │
423
+ │ 2025-01-21 00:00:00 ┆ 1.4112 ┆ 1.4134 ┆ 1.4063 ┆ 1.4089 │
424
+ │ ... ┆ ... ┆ ... ┆ ... ┆ ... │
425
+ └─────────────────────┴────────┴────────┴────────┴────────┘
426
+ ```
427
+
428
+ 6. **Get OHLC data with custom timeframe**
429
+ ```python
430
+ ex_start_date = '2024-04-10'
431
+ ex_end_date = '2024-04-15'
432
+ ex_timeframe = '1h'
433
+
434
+ window_data_ohlc = realtimedata_manager.get_data(
435
+ ticker=ex_ticker,
436
+ start=ex_start_date,
437
+ end=ex_end_date,
438
+ timeframe=ex_timeframe
439
+ )
440
+ ```
441
+
442
+ Output:
443
+ ```
444
+ Real time 1h window data: shape: (72, 5)
445
+ ┌─────────────────────┬─────────┬─────────┬─────────┬─────────┐
446
+ │ timestamp ┆ open ┆ high ┆ low ┆ close │
447
+ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
448
+ │ datetime[ms] ┆ f32 ┆ f32 ┆ f32 ┆ f32 │
449
+ ╞═════════════════════╪═════════╪═════════╪═════════╪═════════╡
450
+ │ 2024-04-10 00:00:00 ┆ 1.4765 ┆ 1.4768 ┆ 1.4752 ┆ 1.4761 │
451
+ │ 2024-04-10 01:00:00 ┆ 1.4761 ┆ 1.4768 ┆ 1.4755 ┆ 1.4762 │
452
+ │ 2024-04-10 02:00:00 ┆ 1.4762 ┆ 1.4778 ┆ 1.4751 ┆ 1.4771 │
453
+ │ ... ┆ ... ┆ ... ┆ ... ┆ ... │
454
+ └─────────────────────┴─────────┴─────────┴─────────┴─────────┘
455
+ ```
456
+
457
+ 7. **Intraday data with dynamic dates**
458
+ ```python
459
+ from pandas import Timestamp, Timedelta
460
+
461
+ ex_start_date = Timestamp.now() - Timedelta('10D')
462
+ ex_end_date = Timestamp.now() - Timedelta('8D')
463
+ ex_timeframe = '5m'
464
+
465
+ window_data_ohlc = realtimedata_manager.get_data(
466
+ ticker='EURUSD',
467
+ start=ex_start_date,
468
+ end=ex_end_date,
469
+ timeframe=ex_timeframe
470
+ )
471
+ ```
472
+ Get 5-minute data for recent days using dynamic date calculations.
473
+
474
+
475
+ ## PYTEST and pipeline implementation
476
+
477
+ The project uses **pytest** for testing and **CircleCI** for continuous integration. The pipeline automatically runs on every commit to ensure code quality and functionality.
478
+
479
+ ### Testing with Pytest
480
+
481
+ To run tests locally:
482
+
483
+ ```bash
484
+ # Run all tests
485
+ poetry run pytest
486
+
487
+ # Run tests with flake8 linting (same as CI)
488
+ poetry run pytest --flake8
489
+
490
+ # Run tests with verbose output
491
+ poetry run pytest -v
492
+
493
+ # Run specific test file
494
+ poetry run pytest tests/test_file.py
495
+ ```
496
+
497
+ ### CircleCI Pipeline
498
+
499
+ The CI/CD pipeline is configured via `.circleci/config.yml` and automatically runs on every push to the repository.
500
+
501
+ #### Pipeline Configuration
502
+
503
+ **Version:** CircleCI 2.1
504
+
505
+ **Docker Image:** `cimg/python:3.12.12`
506
+
507
+ **Workflow:** `unit-tests`
508
+
509
+ #### Pipeline Steps
510
+
511
+ The pipeline executes the following steps for Python 3.12:
512
+
513
+ 1. **Checkout**: Clone the repository code
514
+ 2. **Install Poetry**: Install the Poetry package manager (`pip install poetry`)
515
+ 3. **Restore Cache**: Restore dependencies from cache if available (cache key based on `poetry.lock` checksum)
516
+ 4. **Install Dependencies**: Install project dependencies using `poetry install`
517
+ 5. **Save Cache**: Cache the installed dependencies for faster future builds
518
+ 6. **Run Tests**: Execute tests with flake8 linting using `poetry run pytest --flake8`
519
+
520
+ #### Environment Variables
521
+
522
+ The pipeline supports the following environment variables (configured in CircleCI project settings):
523
+
524
+ - `DATABASE_URL`: Database connection string (if needed)
525
+ - `API_KEY`: API keys for external services (if needed for integration tests)
526
+
527
+ #### Jobs
528
+
529
+ - **py312**: Runs the complete test suite on Python 3.12
530
+
531
+ #### Workflow
532
+
533
+ The `unit-tests` workflow triggers on every commit and runs the `py312` job to validate:
534
+ - Code functionality through pytest
535
+ - Code quality and style through flake8 integration
536
+ ```
537
+