tradekart-backtester-data 0.1.0__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.
Files changed (25) hide show
  1. tradekart_backtester_data-0.1.0/PKG-INFO +46 -0
  2. tradekart_backtester_data-0.1.0/README.md +29 -0
  3. tradekart_backtester_data-0.1.0/data/__init__.py +0 -0
  4. tradekart_backtester_data-0.1.0/data/backfill.py +33 -0
  5. tradekart_backtester_data-0.1.0/data/constants.py +2 -0
  6. tradekart_backtester_data-0.1.0/data/fetch.py +18 -0
  7. tradekart_backtester_data-0.1.0/data/interfaces.py +21 -0
  8. tradekart_backtester_data-0.1.0/data/lambda_handler.py +11 -0
  9. tradekart_backtester_data-0.1.0/data/loader.py +19 -0
  10. tradekart_backtester_data-0.1.0/data/pipeline.py +40 -0
  11. tradekart_backtester_data-0.1.0/data/storage.py +44 -0
  12. tradekart_backtester_data-0.1.0/data/symbol_cache.py +57 -0
  13. tradekart_backtester_data-0.1.0/data/symbols.py +14 -0
  14. tradekart_backtester_data-0.1.0/data/version.py +4 -0
  15. tradekart_backtester_data-0.1.0/pyproject.toml +40 -0
  16. tradekart_backtester_data-0.1.0/setup.cfg +4 -0
  17. tradekart_backtester_data-0.1.0/tests/test_loader.py +31 -0
  18. tradekart_backtester_data-0.1.0/tests/test_pipeline.py +31 -0
  19. tradekart_backtester_data-0.1.0/tests/test_storage.py +15 -0
  20. tradekart_backtester_data-0.1.0/tests/test_symbol_cache.py +51 -0
  21. tradekart_backtester_data-0.1.0/tradekart_backtester_data.egg-info/PKG-INFO +46 -0
  22. tradekart_backtester_data-0.1.0/tradekart_backtester_data.egg-info/SOURCES.txt +23 -0
  23. tradekart_backtester_data-0.1.0/tradekart_backtester_data.egg-info/dependency_links.txt +1 -0
  24. tradekart_backtester_data-0.1.0/tradekart_backtester_data.egg-info/requires.txt +10 -0
  25. tradekart_backtester_data-0.1.0/tradekart_backtester_data.egg-info/top_level.txt +1 -0
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: tradekart-backtester-data
3
+ Version: 0.1.0
4
+ Summary: Daily NSE price data fetcher and storage for TradeKart's backtester
5
+ Author-email: Vikalp Varshney <vikalp@tradekart.gmail.com>
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: tradekart-cloud
9
+ Requires-Dist: yfinance>=0.2.40
10
+ Requires-Dist: pandas>=2.2.0
11
+ Requires-Dist: pyarrow>=16.0.0
12
+ Requires-Dist: requests>=2.31.0
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
15
+ Requires-Dist: moto[s3]>=5.0.0; extra == "dev"
16
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
17
+
18
+ # README #
19
+
20
+ This README would normally document whatever steps are necessary to get your application up and running.
21
+
22
+ ### What is this repository for? ###
23
+
24
+ * Quick summary
25
+ * Version
26
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
27
+
28
+ ### How do I get set up? ###
29
+
30
+ * Summary of set up
31
+ * Configuration
32
+ * Dependencies
33
+ * Database configuration
34
+ * How to run tests
35
+ * Deployment instructions
36
+
37
+ ### Contribution guidelines ###
38
+
39
+ * Writing tests
40
+ * Code review
41
+ * Other guidelines
42
+
43
+ ### Who do I talk to? ###
44
+
45
+ * Repo owner or admin
46
+ * Other community or team contact
@@ -0,0 +1,29 @@
1
+ # README #
2
+
3
+ This README would normally document whatever steps are necessary to get your application up and running.
4
+
5
+ ### What is this repository for? ###
6
+
7
+ * Quick summary
8
+ * Version
9
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
10
+
11
+ ### How do I get set up? ###
12
+
13
+ * Summary of set up
14
+ * Configuration
15
+ * Dependencies
16
+ * Database configuration
17
+ * How to run tests
18
+ * Deployment instructions
19
+
20
+ ### Contribution guidelines ###
21
+
22
+ * Writing tests
23
+ * Code review
24
+ * Other guidelines
25
+
26
+ ### Who do I talk to? ###
27
+
28
+ * Repo owner or admin
29
+ * Other community or team contact
File without changes
@@ -0,0 +1,33 @@
1
+ from datetime import date, timedelta
2
+
3
+ import yfinance as yf
4
+ from cloud import AWS
5
+
6
+ from .storage import S3ParquetStore
7
+ from .symbol_cache import get_nifty100_symbols
8
+
9
+ YEARS_OF_HISTORY = 3
10
+
11
+
12
+ def backfill():
13
+ aws = AWS()
14
+ store = S3ParquetStore(aws=aws)
15
+ symbols = get_nifty100_symbols(aws)
16
+
17
+ start = date.today() - timedelta(days=365 * YEARS_OF_HISTORY)
18
+ print(f"Backfilling {len(symbols)} symbols from {start} to today...")
19
+
20
+ for symbol in symbols:
21
+ data = yf.download(symbol, start=start, end=date.today(), progress=False)
22
+ if data.empty:
23
+ print(f" {symbol}: no data, skipping")
24
+ continue
25
+ data.index.name = "date"
26
+ store.save(symbol, data[["Open", "High", "Low", "Close", "Volume"]])
27
+ print(f" {symbol}: saved {len(data)} rows")
28
+
29
+ print("Backfill complete.")
30
+
31
+
32
+ if __name__ == "__main__":
33
+ backfill()
@@ -0,0 +1,2 @@
1
+ NIFTY100_URL = "https://archives.nseindia.com/content/indices/ind_nifty100list.csv"
2
+ BUCKET = "tradekart-strategies-data"
@@ -0,0 +1,18 @@
1
+ from datetime import date, timedelta
2
+
3
+ import pandas as pd
4
+ import yfinance as yf
5
+
6
+
7
+ class YahooFinanceSource:
8
+ def fetch_latest_candle(self, symbol: str, for_date: date) -> pd.DataFrame | None:
9
+ data = yf.download(
10
+ symbol,
11
+ start=for_date,
12
+ end=for_date + timedelta(days=1),
13
+ progress=False,
14
+ )
15
+ if data.empty:
16
+ return None
17
+ data.index.name = "date"
18
+ return data[["Open", "High", "Low", "Close", "Volume"]]
@@ -0,0 +1,21 @@
1
+ from datetime import date
2
+ from typing import Protocol
3
+
4
+ import pandas as pd
5
+
6
+
7
+ class PriceStore(Protocol):
8
+ def load(self, symbol: str) -> pd.DataFrame | None:
9
+ """
10
+ Return the symbol's full stored history, or None if nothing stored yet.
11
+ """
12
+
13
+ def save(self, symbol: str, prices: pd.DataFrame) -> None:
14
+ """
15
+ Overwrite the symbol's stored history with this DataFrame
16
+ """
17
+
18
+
19
+ class DataSource(Protocol):
20
+ def fetch_latest_candle(self, symbol: str, for_date: date) -> pd.DataFrame | None:
21
+ """Return one day's OHLCV for this symbol, or None if unavailable."""
@@ -0,0 +1,11 @@
1
+ import json
2
+
3
+ from .pipeline import run
4
+
5
+
6
+ def handler(event, context):
7
+ result = run()
8
+ return {
9
+ "statusCode": 200,
10
+ "body": json.dumps(result),
11
+ }
@@ -0,0 +1,19 @@
1
+ import pandas as pd
2
+ from cloud import AWS
3
+
4
+ from .interfaces import PriceStore
5
+ from .storage import S3ParquetStore
6
+ from .symbol_cache import get_nifty100_symbols
7
+
8
+
9
+ def load_all_prices(store: PriceStore | None = None, aws: AWS | None = None):
10
+ aws = aws or AWS()
11
+ store = store or S3ParquetStore(aws)
12
+ symbols = get_nifty100_symbols(aws)
13
+
14
+ closes: dict[str, pd.Series] = {}
15
+ for symbol in symbols:
16
+ data = store.load(symbol)
17
+ if data is not None:
18
+ closes[symbol] = data["Close"]
19
+ return pd.DataFrame(closes).sort_index()
@@ -0,0 +1,40 @@
1
+ from datetime import date, timedelta
2
+
3
+ import pandas as pd
4
+ from cloud import AWS
5
+
6
+ from .fetch import YahooFinanceSource
7
+ from .interfaces import DataSource, PriceStore
8
+ from .storage import S3ParquetStore
9
+ from .symbol_cache import get_nifty100_symbols
10
+
11
+
12
+ def run(
13
+ store: PriceStore | None = None,
14
+ data_source: DataSource | None = None,
15
+ aws: AWS | None = None,
16
+ for_date: date | None = None,
17
+ ) -> dict:
18
+ aws = aws or AWS()
19
+ store = store or S3ParquetStore(aws)
20
+ data_source = data_source or YahooFinanceSource()
21
+ target_date = for_date or date.today() - timedelta(days=1)
22
+ symbols = get_nifty100_symbols(aws)
23
+ fetched, skipped = 0, 0
24
+
25
+ for symbol in symbols:
26
+ candle = data_source.fetch_latest_candle(symbol, target_date)
27
+ if candle is None:
28
+ skipped += 1
29
+ continue
30
+ existing = store.load(symbol)
31
+ combined = pd.concat([existing, candle]) if existing is not None else candle
32
+ combined = combined[~combined.index.duplicated(keep="last")].sort_index()
33
+ store.save(symbol, combined)
34
+ fetched += 1
35
+ return {
36
+ "fetched": fetched,
37
+ "skipped": skipped,
38
+ "total": len(symbols),
39
+ "date": str(target_date),
40
+ }
@@ -0,0 +1,44 @@
1
+ import io
2
+ from pathlib import Path
3
+
4
+ import pandas as pd
5
+ from cloud import AWS
6
+ from cloud.exceptions import TradeKartCloudError
7
+ from cloud.storage.s3 import DownloadFileRequest, UploadBytesRequest
8
+
9
+ from .constants import BUCKET
10
+
11
+
12
+ class S3ParquetStore:
13
+ def __init__(self, aws: AWS | None = None, bucket: str = BUCKET):
14
+ self._aws = aws or AWS()
15
+ self._bucket = bucket
16
+
17
+ def _object_key(self, symbol: str) -> str:
18
+ return f"prices/{symbol}.parquet"
19
+
20
+ def load(self, symbol: str) -> pd.DataFrame | None:
21
+ tmp_path = Path(f"/tmp/{symbol}.parquet")
22
+ try:
23
+ self._aws.s3.download_file(
24
+ DownloadFileRequest(
25
+ bucket_name=self._bucket,
26
+ object_key=self._object_key(symbol),
27
+ destination_path=tmp_path,
28
+ )
29
+ )
30
+ except TradeKartCloudError:
31
+ return None
32
+ return pd.read_parquet(tmp_path)
33
+
34
+ def save(self, symbol: str, prices: pd.DataFrame) -> None:
35
+ buffer = io.BytesIO()
36
+ prices.to_parquet(buffer)
37
+ self._aws.s3.upload_bytes(
38
+ UploadBytesRequest(
39
+ bucket_name=self._bucket,
40
+ object_key=self._object_key(symbol),
41
+ body=buffer.getvalue(),
42
+ content_type="application/octet-stream",
43
+ )
44
+ )
@@ -0,0 +1,57 @@
1
+ import json
2
+ from datetime import date, timedelta
3
+ from pathlib import Path
4
+
5
+ from cloud import AWS
6
+ from cloud.exceptions import TradeKartCloudError
7
+ from cloud.storage.s3 import DownloadFileRequest, UploadBytesRequest
8
+
9
+ from .constants import BUCKET
10
+ from .symbols import fetch_nifty100_symbols
11
+
12
+ CACHE_KEY = "symbols/nifty100.json"
13
+ MAX_AGE_DAYS = 180
14
+
15
+
16
+ def _load_cache(aws: AWS) -> dict | None:
17
+ tmp_path = Path("/tmp/nifty100_cache.json")
18
+ try:
19
+ aws.s3.download_file(
20
+ DownloadFileRequest(
21
+ bucket_name=BUCKET, object_key=CACHE_KEY, destination_path=tmp_path
22
+ )
23
+ )
24
+ except TradeKartCloudError:
25
+ return None
26
+ return json.loads(tmp_path.read_text())
27
+
28
+
29
+ def _save_cache(aws: AWS, symbols: list[str]) -> None:
30
+ payload = json.dumps(
31
+ {"fetched_on": date.today().isoformat(), "symbols": symbols}
32
+ ).encode()
33
+ aws.s3.upload_bytes(
34
+ UploadBytesRequest(
35
+ bucket_name=BUCKET,
36
+ object_key=CACHE_KEY,
37
+ body=payload,
38
+ content_type="application/json",
39
+ )
40
+ )
41
+
42
+
43
+ def get_nifty100_symbols(aws: AWS) -> list[str]:
44
+ cached = _load_cache(aws)
45
+ if cached and date.today() - date.fromisoformat(cached["fetched_on"]) < timedelta(
46
+ days=MAX_AGE_DAYS
47
+ ):
48
+ return cached["symbols"]
49
+
50
+ try:
51
+ symbols = fetch_nifty100_symbols()
52
+ except Exception:
53
+ if cached:
54
+ return cached["symbols"]
55
+ raise
56
+ _save_cache(aws, symbols)
57
+ return symbols
@@ -0,0 +1,14 @@
1
+ import csv
2
+ import io
3
+
4
+ import requests
5
+
6
+ from data.constants import NIFTY100_URL
7
+
8
+
9
+ def fetch_nifty100_symbols() -> list[str]:
10
+ headers = {"User-Agent": "Mozilla/5.0"}
11
+ response = requests.get(NIFTY100_URL, headers=headers, timeout=10)
12
+ response.raise_for_status()
13
+ reader = csv.DictReader(io.StringIO(response.text))
14
+ return [f"{row['Symbol'].strip()}.NS" for row in reader]
@@ -0,0 +1,4 @@
1
+ """Version info."""
2
+
3
+ __version_info__ = (0, 1, 0)
4
+ __version__ = ".".join(str(v) for v in __version_info__)
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tradekart-backtester-data"
7
+ dynamic = ["version"]
8
+ authors = [{ name = "Vikalp Varshney", email = "vikalp@tradekart.gmail.com" }]
9
+ description = "Daily NSE price data fetcher and storage for TradeKart's backtester"
10
+ readme = "README.md"
11
+ requires-python = ">=3.11"
12
+ dependencies = [
13
+ "tradekart-cloud",
14
+ "yfinance>=0.2.40",
15
+ "pandas>=2.2.0",
16
+ "pyarrow>=16.0.0",
17
+ "requests>=2.31.0",
18
+ ]
19
+
20
+ [tool.setuptools]
21
+ include-package-data = true
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["."]
25
+ include = ["data*"]
26
+ namespaces = false
27
+
28
+ [tool.setuptools.dynamic]
29
+ version = { attr = "data.version.__version__" }
30
+
31
+ [project.optional-dependencies]
32
+ dev = ["pytest>=8.0.0", "moto[s3]>=5.0.0", "ruff>=0.6.0"]
33
+
34
+ [tool.pytest.ini_options]
35
+ testpaths = ["tests"]
36
+
37
+ [tool.ruff.lint]
38
+ ignore = [
39
+ "DTZ011", # date.today() is fine for day-level cache/batch checks - no precise moment-in-time matters here
40
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,31 @@
1
+ import pandas as pd
2
+
3
+ from data.loader import load_all_prices
4
+
5
+
6
+ class FakeStore:
7
+ def __init__(self, data: dict[str, pd.DataFrame]):
8
+ self._data = data
9
+
10
+ def load(self, symbol):
11
+ return self._data.get(symbol)
12
+
13
+ def save(self, symbol, prices):
14
+ self._data[symbol] = prices
15
+
16
+
17
+ def test_load_all_prices_combines_symbols(monkeypatch):
18
+ fake_data = {
19
+ "RELIANCE.NS": pd.DataFrame(
20
+ {"Close": [100.0]}, index=pd.to_datetime(["2024-01-01"])
21
+ ),
22
+ "TCS.NS": pd.DataFrame(
23
+ {"Close": [200.0]}, index=pd.to_datetime(["2024-01-01"])
24
+ ),
25
+ }
26
+ monkeypatch.setattr(
27
+ "data.loader.get_nifty100_symbols", lambda aws: list(fake_data.keys())
28
+ )
29
+ wide = load_all_prices(store=FakeStore(fake_data))
30
+ assert list(wide.columns) == list(fake_data.keys())
31
+ assert wide.loc["2024-01-01", "RELIANCE.NS"] == 100.0
@@ -0,0 +1,31 @@
1
+ from datetime import date
2
+
3
+ import pandas as pd
4
+
5
+ from data.pipeline import run
6
+
7
+
8
+ class FakeStore:
9
+ def __init__(self):
10
+ self._data: dict[str, pd.DataFrame] = {}
11
+
12
+ def load(self, symbol):
13
+ return self._data.get(symbol)
14
+
15
+ def save(self, symbol, prices):
16
+ self._data[symbol] = prices
17
+
18
+
19
+ class FakeSource:
20
+ def fetch_latest_candle(self, symbol, for_date):
21
+ return pd.DataFrame({"Close": [100.0]}, index=pd.to_datetime([for_date]))
22
+
23
+
24
+ def test_run_fetches_and_saves_each_symbol(monkeypatch):
25
+ monkeypatch.setattr(
26
+ "data.pipeline.get_nifty100_symbols", lambda aws: ["RELIANCE.NS", "TCS.NS"]
27
+ )
28
+ store = FakeStore()
29
+ result = run(store=store, data_source=FakeSource(), for_date=date(2024, 1, 2))
30
+ assert result["fetched"] == 2
31
+ assert result["skipped"] == 0
@@ -0,0 +1,15 @@
1
+ import pandas as pd
2
+
3
+
4
+ def test_save_then_load_round_trip(s3_store):
5
+ prices = pd.DataFrame(
6
+ {"Close": [100.0, 101.5]}, index=pd.date_range(start="2026-10-07", periods=2)
7
+ )
8
+ s3_store.save("RELIANCE.NS", prices)
9
+ loaded = s3_store.load("RELIANCE.NS")
10
+ assert loaded is not None
11
+ assert loaded["Close"].tolist() == [100.0, 101.5]
12
+
13
+
14
+ def test_load_missing_symbol_returns_none(s3_store):
15
+ assert s3_store.load("DOES_NOT_EXIST.NS") is None
@@ -0,0 +1,51 @@
1
+ import pytest
2
+
3
+ from data import symbol_cache
4
+
5
+
6
+ def test_fetches_live_when_no_cache(monkeypatch, aws, bucket):
7
+ monkeypatch.setattr(symbol_cache, "BUCKET", bucket)
8
+ monkeypatch.setattr(
9
+ symbol_cache,
10
+ "fetch_nifty100_symbols",
11
+ lambda: ["RELIANCE.NS", "TCS.NS", "INFY.NS"],
12
+ )
13
+ assert symbol_cache.get_nifty100_symbols(aws) == [
14
+ "RELIANCE.NS",
15
+ "TCS.NS",
16
+ "INFY.NS",
17
+ ]
18
+
19
+
20
+ def test_uses_cache_when_fresh(monkeypatch, aws, bucket):
21
+ monkeypatch.setattr(symbol_cache, "BUCKET", bucket)
22
+ symbol_cache._save_cache(aws, ["CACHED.NS"])
23
+
24
+ def fail_if_called():
25
+ raise AssertionError("fetch_nifty100_symbols should not be called")
26
+
27
+ monkeypatch.setattr(symbol_cache, "fetch_nifty100_symbols", fail_if_called)
28
+ assert symbol_cache.get_nifty100_symbols(aws) == ["CACHED.NS"]
29
+
30
+
31
+ def test_falls_back_to_stale_cache_if_nse_fails(monkeypatch, aws, bucket):
32
+ monkeypatch.setattr(symbol_cache, "BUCKET", bucket)
33
+ symbol_cache._save_cache(aws, ["STALE.NS"])
34
+ monkeypatch.setattr(symbol_cache, "MAX_AGE_DAYS", -1)
35
+ monkeypatch.setattr(
36
+ symbol_cache,
37
+ "fetch_nifty100_symbols",
38
+ lambda: (_ for _ in ()).throw(RuntimeError("NSE down")),
39
+ )
40
+ assert symbol_cache.get_nifty100_symbols(aws) == ["STALE.NS"]
41
+
42
+
43
+ def test_raises_if_no_cache_and_nse_fails(monkeypatch, aws, bucket):
44
+ monkeypatch.setattr(symbol_cache, "BUCKET", bucket)
45
+ monkeypatch.setattr(
46
+ symbol_cache,
47
+ "fetch_nifty100_symbols",
48
+ lambda: (_ for _ in ()).throw(RuntimeError("NSE down")),
49
+ )
50
+ with pytest.raises(RuntimeError):
51
+ symbol_cache.get_nifty100_symbols(aws)
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: tradekart-backtester-data
3
+ Version: 0.1.0
4
+ Summary: Daily NSE price data fetcher and storage for TradeKart's backtester
5
+ Author-email: Vikalp Varshney <vikalp@tradekart.gmail.com>
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: tradekart-cloud
9
+ Requires-Dist: yfinance>=0.2.40
10
+ Requires-Dist: pandas>=2.2.0
11
+ Requires-Dist: pyarrow>=16.0.0
12
+ Requires-Dist: requests>=2.31.0
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
15
+ Requires-Dist: moto[s3]>=5.0.0; extra == "dev"
16
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
17
+
18
+ # README #
19
+
20
+ This README would normally document whatever steps are necessary to get your application up and running.
21
+
22
+ ### What is this repository for? ###
23
+
24
+ * Quick summary
25
+ * Version
26
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
27
+
28
+ ### How do I get set up? ###
29
+
30
+ * Summary of set up
31
+ * Configuration
32
+ * Dependencies
33
+ * Database configuration
34
+ * How to run tests
35
+ * Deployment instructions
36
+
37
+ ### Contribution guidelines ###
38
+
39
+ * Writing tests
40
+ * Code review
41
+ * Other guidelines
42
+
43
+ ### Who do I talk to? ###
44
+
45
+ * Repo owner or admin
46
+ * Other community or team contact
@@ -0,0 +1,23 @@
1
+ README.md
2
+ pyproject.toml
3
+ data/__init__.py
4
+ data/backfill.py
5
+ data/constants.py
6
+ data/fetch.py
7
+ data/interfaces.py
8
+ data/lambda_handler.py
9
+ data/loader.py
10
+ data/pipeline.py
11
+ data/storage.py
12
+ data/symbol_cache.py
13
+ data/symbols.py
14
+ data/version.py
15
+ tests/test_loader.py
16
+ tests/test_pipeline.py
17
+ tests/test_storage.py
18
+ tests/test_symbol_cache.py
19
+ tradekart_backtester_data.egg-info/PKG-INFO
20
+ tradekart_backtester_data.egg-info/SOURCES.txt
21
+ tradekart_backtester_data.egg-info/dependency_links.txt
22
+ tradekart_backtester_data.egg-info/requires.txt
23
+ tradekart_backtester_data.egg-info/top_level.txt
@@ -0,0 +1,10 @@
1
+ tradekart-cloud
2
+ yfinance>=0.2.40
3
+ pandas>=2.2.0
4
+ pyarrow>=16.0.0
5
+ requests>=2.31.0
6
+
7
+ [dev]
8
+ pytest>=8.0.0
9
+ moto[s3]>=5.0.0
10
+ ruff>=0.6.0