hamuna-quant-cli 0.1.8__tar.gz → 0.1.10__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.
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/PKG-INFO +1 -1
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/__init__.py +1 -1
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/__main__.py +59 -2
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/live/runner.py +64 -2
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/runtime/server_client.py +7 -2
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli.egg-info/PKG-INFO +1 -1
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/pyproject.toml +1 -1
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/MANIFEST.in +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/README.md +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/README.md +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/_market_fallback.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/_metrics_15.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/_test_akquant_parity.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/akquant_data_adapter.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/akquant_runner.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/akquant_schema_adapter.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/base_strategy.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/cross_sectional_helpers.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/live/__init__.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/live/loader.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/live/qmt_broker.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/live/qmt_market.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/prebuilt_downloader.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/prebuilt_resolver.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/qmt_translator.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/runtime/__init__.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/runtime/backtest.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/runtime/cache.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/runtime/discipline.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/runtime/http_client.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/runtime/s3client.py +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/scripts/server.json +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli.egg-info/SOURCES.txt +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli.egg-info/dependency_links.txt +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli.egg-info/entry_points.txt +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli.egg-info/requires.txt +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli.egg-info/top_level.txt +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/setup.cfg +0 -0
- {hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/tests/test_live_safety.py +0 -0
|
@@ -331,9 +331,14 @@ def cmd_commit(args) -> int:
|
|
|
331
331
|
bridge_server 扫描执行; hamuna-quant-cli 下单走 bridge_server HTTP.
|
|
332
332
|
bundle = 4 文件 (strategy + result + config + params). multipart params 字段
|
|
333
333
|
= 整个 params.json 内容 (ADR-0025, desktop 实盘配置源).
|
|
334
|
+
|
|
335
|
+
Round 18.62 P0-1: 新增 step 1.5 replay diff gate — `--replay-result` 必填,
|
|
336
|
+
与 `--result` 算 sharpe diff, 超阈值 (HAMUNA_REPLAY_GATE_PCT 缺省 0.20) → exit 5.
|
|
337
|
+
server `UpdateStrategyResult` handler 二次校验防绕过.
|
|
334
338
|
"""
|
|
335
339
|
from . import runtime
|
|
336
340
|
import json
|
|
341
|
+
import os
|
|
337
342
|
import tarfile
|
|
338
343
|
|
|
339
344
|
sid = args.strategy_id
|
|
@@ -354,6 +359,16 @@ def cmd_commit(args) -> int:
|
|
|
354
359
|
if params_path and not params_path.exists():
|
|
355
360
|
print(f"params.json 不存在: {params_path} (--params 可选)", file=sys.stderr)
|
|
356
361
|
return 2
|
|
362
|
+
# Round 18.62 P0-1: replay_result.json 必填 (除非 --skip-replay-gate qa 通道).
|
|
363
|
+
replay_path = Path(args.replay_result) if args.replay_result else None
|
|
364
|
+
if not args.skip_replay_gate:
|
|
365
|
+
if replay_path is None:
|
|
366
|
+
print("ERROR: P0-1: 必须传 --replay-result, 先跑 live run --broker replay 拿 metrics "
|
|
367
|
+
"(或 --skip-replay-gate qa 通道)", file=sys.stderr)
|
|
368
|
+
return 2
|
|
369
|
+
if not replay_path.exists():
|
|
370
|
+
print(f"replay_result.json 不存在: {args.replay_result}", file=sys.stderr)
|
|
371
|
+
return 2
|
|
357
372
|
|
|
358
373
|
bundle_out = Path(args.bundle_out) if args.bundle_out else Path(f"runs/{sid}/bundle.tar.gz")
|
|
359
374
|
bundle_out.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -367,16 +382,52 @@ def cmd_commit(args) -> int:
|
|
|
367
382
|
tar.add(cfg_path, arcname=cfg_path.name)
|
|
368
383
|
if params_path:
|
|
369
384
|
tar.add(params_path, arcname=params_path.name)
|
|
385
|
+
# Round 18.62 P0-1: replay_result.json 也打进去, 方便回溯 / 审计.
|
|
386
|
+
if replay_path:
|
|
387
|
+
tar.add(replay_path, arcname=replay_path.name)
|
|
370
388
|
print(f" ✓ bundle = {bundle_out} ({bundle_out.stat().st_size} bytes)", file=sys.stderr)
|
|
371
389
|
|
|
372
|
-
|
|
390
|
+
# ----- Round 18.62 P0-1 step 1.5: replay diff gate -----
|
|
391
|
+
# 必在 step 2 (PUT) 之前 — gate fail 后 step 2-3 都不跑, 不留半上传状态.
|
|
392
|
+
replay_metrics: dict | None = None
|
|
393
|
+
if not args.skip_replay_gate:
|
|
394
|
+
try:
|
|
395
|
+
bt = json.loads(result_path.read_text(encoding="utf-8"))
|
|
396
|
+
rp = json.loads(replay_path.read_text(encoding="utf-8")) # type: ignore[union-attr]
|
|
397
|
+
except json.JSONDecodeError as e:
|
|
398
|
+
print(f" ✗ result/replay JSON 解析失败: {e}", file=sys.stderr)
|
|
399
|
+
return 2
|
|
400
|
+
bt_sharpe = float(bt.get("metrics", {}).get("sharpe", 0.0))
|
|
401
|
+
rp_sharpe = float(rp.get("metrics", {}).get("sharpe", 0.0))
|
|
402
|
+
diff_pct = abs(rp_sharpe - bt_sharpe) / max(abs(bt_sharpe), 0.01)
|
|
403
|
+
try:
|
|
404
|
+
threshold = float(os.environ.get("HAMUNA_REPLAY_GATE_PCT", "0.20"))
|
|
405
|
+
except ValueError:
|
|
406
|
+
threshold = 0.20
|
|
407
|
+
if diff_pct > threshold:
|
|
408
|
+
print(f"ERROR: replay gate diff={diff_pct:.2%} > {threshold:.0%} "
|
|
409
|
+
f"(backtest.sharpe={bt_sharpe:.4f} vs replay.sharpe={rp_sharpe:.4f})",
|
|
410
|
+
file=sys.stderr)
|
|
411
|
+
return 5
|
|
412
|
+
replay_metrics = {
|
|
413
|
+
"sharpe": rp_sharpe,
|
|
414
|
+
"total_return": float(rp.get("metrics", {}).get("total_return", 0.0)),
|
|
415
|
+
"win_rate": float(rp.get("metrics", {}).get("win_rate", 0.0)),
|
|
416
|
+
"trades": len(rp.get("trades", [])),
|
|
417
|
+
"diff_pct": diff_pct,
|
|
418
|
+
"run_at": (rp.get("period") or {}).get("end"),
|
|
419
|
+
}
|
|
420
|
+
print(f" ✓ replay gate pass (diff={diff_pct:.2%} <= {threshold:.0%}, "
|
|
421
|
+
f"replay.sharpe={rp_sharpe:.4f})", file=sys.stderr)
|
|
422
|
+
|
|
423
|
+
print("=== Step 2/3: PUT /strategies/:id/result (metrics + replay_metrics) ===", file=sys.stderr)
|
|
373
424
|
try:
|
|
374
425
|
result = json.loads(result_path.read_text(encoding="utf-8"))
|
|
375
426
|
except json.JSONDecodeError as e:
|
|
376
427
|
print(f" ✗ result.json 解析失败: {e}", file=sys.stderr)
|
|
377
428
|
return 2
|
|
378
429
|
try:
|
|
379
|
-
resp = runtime.server_client.upload_backtest_result(sid, result)
|
|
430
|
+
resp = runtime.server_client.upload_backtest_result(sid, result, replay_metrics=replay_metrics)
|
|
380
431
|
except runtime.server_client.ServerError as e:
|
|
381
432
|
print(f" ✗ 上传 metrics 失败: {e}", file=sys.stderr)
|
|
382
433
|
return 4
|
|
@@ -943,6 +994,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
943
994
|
cm.add_argument("strategy_id", help="server 端 strategy_id")
|
|
944
995
|
cm.add_argument("--strategy", help="akquant strategy .py 路径 (默认 <id>.py)")
|
|
945
996
|
cm.add_argument("--result", required=True, help="metrics result.json 路径")
|
|
997
|
+
# Round 18.62 P0-1: replay gate. --replay-result 必填 (除非 --skip-replay-gate).
|
|
998
|
+
cm.add_argument("--replay-result", default=None,
|
|
999
|
+
help="replay_result.json 路径 (P0-1 必填; 先跑 live run --broker replay 拿 metrics, "
|
|
1000
|
+
"或 --skip-replay-gate 跳过)")
|
|
1001
|
+
cm.add_argument("--skip-replay-gate", action="store_true",
|
|
1002
|
+
help="[qa 临时] 跳过 replay diff gate (非 dev 默认)")
|
|
946
1003
|
cm.add_argument("--config", help="config.json 路径")
|
|
947
1004
|
cm.add_argument("--params", default=None,
|
|
948
1005
|
help="params.json 路径 (固定 schema; 整个内容作 multipart params 字段, desktop 实盘配置源)")
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import sys
|
|
12
|
+
import json # Round 18.62 P0-1: replay metrics 落盘
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
from typing import Any
|
|
14
15
|
|
|
@@ -216,19 +217,39 @@ def _build_instruments(symbols: list[str] | None, normalize: bool = False) -> li
|
|
|
216
217
|
|
|
217
218
|
normalize=True: 裸码 → 带后缀 (replay 数据来自 prebuilt, stockCode 是 '600000.SH'
|
|
218
219
|
形态, instrument symbol 必须一致才匹配 ReplayMarketGateway 的订阅过滤).
|
|
220
|
+
|
|
221
|
+
Round 18.59: 按 6 位代码前缀推断 AssetType — akquant 0.3.x AssetType 枚举没 ETF 也没 Bond,
|
|
222
|
+
510/511/512/515/159 (沪/深 ETF) → Fund; 113/127/128 (可转债) → Fund; 其他 6 位 → Stock.
|
|
223
|
+
之前硬写 AssetType.Stock 让 351 个 ETF 被 broker 内部按 asset_type 分流时丢弃 → universe=[].
|
|
224
|
+
ponytail: akquant 0.4.x 若新增 AssetType.ETF / AssetType.Bond 则此映射需重审.
|
|
219
225
|
"""
|
|
220
226
|
if not symbols:
|
|
221
227
|
return None
|
|
222
228
|
# 延迟 import akquant — 让 loader 在 akquant 未装环境也能跑通
|
|
223
229
|
from akquant import AssetType, Instrument
|
|
224
230
|
|
|
231
|
+
_ETF_PREFIXES = ("510", "511", "512", "515", "159")
|
|
232
|
+
_BOND_PREFIXES = ("113", "127", "128") # 可转债 (沪 113 / 深 127 / 沪 128)
|
|
233
|
+
|
|
225
234
|
def _sym(s: str) -> str:
|
|
226
235
|
if normalize:
|
|
227
236
|
from ..akquant_schema_adapter import normalize_symbol
|
|
228
237
|
return normalize_symbol(s)
|
|
229
238
|
return s
|
|
230
239
|
|
|
231
|
-
|
|
240
|
+
def _asset_type(bare: str) -> Any:
|
|
241
|
+
# _sym 后可能是 6 位裸码或 600000.SH 形态 — 都按前 3 位判
|
|
242
|
+
code6 = bare.split(".")[0][-6:] if "." in bare else bare[-6:]
|
|
243
|
+
if not (len(code6) == 6 and code6.isdigit()):
|
|
244
|
+
return AssetType.Stock # 非法 6 位 fallback, 不阻塞启动
|
|
245
|
+
pfx3 = code6[:3]
|
|
246
|
+
if pfx3.startswith(_ETF_PREFIXES):
|
|
247
|
+
return AssetType.Fund
|
|
248
|
+
if pfx3.startswith(_BOND_PREFIXES):
|
|
249
|
+
return AssetType.Fund
|
|
250
|
+
return AssetType.Stock
|
|
251
|
+
|
|
252
|
+
return [Instrument(symbol=_sym(s), asset_type=_asset_type(_sym(s))) for s in symbols if s.strip()]
|
|
232
253
|
|
|
233
254
|
|
|
234
255
|
def _recent_real_bars(symbols: list[str], days: int = 30) -> tuple[Any, str]:
|
|
@@ -770,4 +791,45 @@ def run_live(
|
|
|
770
791
|
f"market_broker={market_broker or '<single>'} "
|
|
771
792
|
f"symbols={symbols or '<all>'} duration={duration}", flush=True)
|
|
772
793
|
print(f"[info] gateway_options={gateway_options}", flush=True)
|
|
773
|
-
_akquant_run_live(**kwargs)
|
|
794
|
+
_akquant_run_live(**kwargs)
|
|
795
|
+
|
|
796
|
+
# ===== Round 18.62 P0-1: replay metrics 产出 =====
|
|
797
|
+
# live run 跑完后, 拿同一份 bars + strategy_cls 跑一次 mini-backtest 拿 metrics,
|
|
798
|
+
# 落 runs/{strategy_id}/replay_result.json (跟 cmd_run 同 13-key schema, to_hamuna_result).
|
|
799
|
+
# 用途: commiter cmd_commit --replay-result 拿此文件 vs 回测 result.json 算 sharpe diff.
|
|
800
|
+
# 仅 broker=replay 时跑 (其他 broker 真盘无意义, 也没 bars 数据).
|
|
801
|
+
if broker == "replay" and spec.mode == "class" and spec.strategy_cls is not None:
|
|
802
|
+
try:
|
|
803
|
+
from akquant import run_backtest as _akquant_run_backtest # noqa: F401
|
|
804
|
+
from ..akquant_schema_adapter import to_hamuna_result
|
|
805
|
+
from datetime import datetime as _dt
|
|
806
|
+
# strategy_id 来自 run_live kwargs 顶层 (Round 18.42+ commiter 必传);
|
|
807
|
+
# 若没有 (裸 live run 调) 退到 /tmp/replay_result.json, 不污染 runs/.
|
|
808
|
+
strategy_id = kwargs.get("strategy_id") or "adhoc"
|
|
809
|
+
out_dir = Path("runs") / strategy_id
|
|
810
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
811
|
+
out_path = out_dir / "replay_result.json"
|
|
812
|
+
# 用 replay_bars_df + strategy_cls 跑 mini-backtest. 同一份数据 → 可比.
|
|
813
|
+
mini_kwargs = dict(kwargs)
|
|
814
|
+
mini_kwargs.pop("live", None) # 防混入 live flag
|
|
815
|
+
# to_hamuna_result 需 akquant BacktestResult 对象; mini-backtest 走同一 strategy_cls
|
|
816
|
+
# + 同一份 bars, 拿 metrics 对象 → to_hamuna_result 落 13-key schema.
|
|
817
|
+
bt_result = _akquant_run_backtest(
|
|
818
|
+
strategy_cls=spec.strategy_cls,
|
|
819
|
+
bars=replay_bars_df,
|
|
820
|
+
initial_cash=initial_cash or 100000.0,
|
|
821
|
+
)
|
|
822
|
+
cfg = {"period": {"start": str(replay_bars_df['date'].min())[:10] if hasattr(replay_bars_df, 'columns') else "",
|
|
823
|
+
"end": str(_dt.now().date())}}
|
|
824
|
+
result_dict = to_hamuna_result(bt_result, cfg)
|
|
825
|
+
out_path.write_text(json.dumps(result_dict, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
826
|
+
m = result_dict.get("metrics", {})
|
|
827
|
+
print(f"[info] replay metrics: sharpe={m.get('sharpe'):.3f} "
|
|
828
|
+
f"total_return={m.get('total_return', 0):.2%} trades={len(result_dict.get('trades', []))} "
|
|
829
|
+
f"→ {out_path}", flush=True)
|
|
830
|
+
except ImportError:
|
|
831
|
+
# akquant 未装 / to_hamuna_result 不可用 — 跳过, 不阻塞 live run
|
|
832
|
+
print("[warn] P0-1: akquant / to_hamuna_result 未装, 跳过 replay metrics 产出", flush=True)
|
|
833
|
+
except Exception as e:
|
|
834
|
+
# mini-backtest 失败不致命 — live run 已成功, 只 warn
|
|
835
|
+
print(f"[warn] P0-1: replay metrics 产出失败 ({type(e).__name__}: {e}), 跳过", flush=True)
|
{hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/runtime/server_client.py
RENAMED
|
@@ -72,13 +72,18 @@ def _api_call(method: str, path: str, body: dict | None = None) -> dict:
|
|
|
72
72
|
return envelope
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def upload_backtest_result(strategy_id: str, result: dict) -> dict:
|
|
76
|
-
"""PUT /api/v1/strategies/{id}/result → 上传 BacktestResult-shape dict.
|
|
75
|
+
def upload_backtest_result(strategy_id: str, result: dict, replay_metrics: dict | None = None) -> dict:
|
|
76
|
+
"""PUT /api/v1/strategies/{id}/result → 上传 BacktestResult-shape dict + 可选 replay_metrics.
|
|
77
77
|
|
|
78
78
|
兼容 akquant_runner.run_akquant_backtest() 输出 (13 顶层 key + metrics 子 dict 15 key + ...),
|
|
79
79
|
先做轻清洗: NaN/Inf → None (json 标准不允许), 后传给 server.
|
|
80
|
+
|
|
81
|
+
Round 18.62 P0-1: replay_metrics (Sharpe/TotalReturn/WinRate/Trades/DiffPct/RunAt) —
|
|
82
|
+
跟 result 平级, server handler 二次校验 diff 防绕过. None 时不传该 key (老调用方不破).
|
|
80
83
|
"""
|
|
81
84
|
payload = _sanitize_for_json(result)
|
|
85
|
+
if replay_metrics is not None:
|
|
86
|
+
payload = {"result": payload, "replay_metrics": _sanitize_for_json(replay_metrics)}
|
|
82
87
|
return _api_call('PUT', f'/api/v1/strategies/{strategy_id}/result', payload)
|
|
83
88
|
|
|
84
89
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hamuna-quant-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.10"
|
|
8
8
|
description = "Hamuna A 股回测 + 实盘统一 CLI (akquant 0.3.x). 替代 v1 自建 driver + QMT-style 策略规范."
|
|
9
9
|
readme = "hamuna_quant_cli/README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/akquant_schema_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli/cross_sectional_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{hamuna_quant_cli-0.1.8 → hamuna_quant_cli-0.1.10}/hamuna_quant_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|