talis-cli 0.3.0__tar.gz → 0.3.1__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.
- {talis_cli-0.3.0 → talis_cli-0.3.1}/PKG-INFO +1 -1
- {talis_cli-0.3.0 → talis_cli-0.3.1}/pyproject.toml +1 -1
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/api.py +12 -6
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/fund.py +6 -1
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis_cli.egg-info/PKG-INFO +1 -1
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis_cli.egg-info/SOURCES.txt +1 -0
- talis_cli-0.3.1/tests/test_fund_deposit.py +46 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_mcp_backtest.py +13 -1
- {talis_cli-0.3.0 → talis_cli-0.3.1}/README.md +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/setup.cfg +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/__init__.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/_eip55.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/cli.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/__init__.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/_shared.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/auth.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/brief.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/chat.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/guide.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/history.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/mcp_server.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/notifications.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/outcome.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/portfolio.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/research.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/snapshot.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/strategies.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/test_tenant.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/trade.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/wait.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/commands/withdraw.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/config.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/output.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/paper.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/spot.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis/symbols.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis_cli.egg-info/dependency_links.txt +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis_cli.egg-info/entry_points.txt +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis_cli.egg-info/requires.txt +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/talis_cli.egg-info/top_level.txt +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_api.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_api_key_auth.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_brief.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_chat.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_commands.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_config.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_confirm_prompt.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_eip55.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_guide.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_history.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_login_flow.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_loopback_login.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_mcp.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_mcp_balance_contract.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_mcp_docstring_contract.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_mcp_mode_contract.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_mcp_sdk_compat.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_mcp_session_expiry.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_mcp_strategy_tools.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_non_interactive_auth.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_notifications.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_outcome.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_paper_mode.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_research.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_session_autorenew.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_signup.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_snapshot_diff.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_spot.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_spot_gateway_first.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_strategies_create.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_test_tenant.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_version_sync.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_wait.py +0 -0
- {talis_cli-0.3.0 → talis_cli-0.3.1}/tests/test_withdraw.py +0 -0
|
@@ -27,6 +27,9 @@ import httpx
|
|
|
27
27
|
|
|
28
28
|
from . import config
|
|
29
29
|
|
|
30
|
+
#: Full path of the control plane's candle route (router prefix included).
|
|
31
|
+
CANDLES_PATH = "/market/candles"
|
|
32
|
+
|
|
30
33
|
DEFAULT_TIMEOUT = httpx.Timeout(connect=10.0, read=30.0, write=30.0, pool=10.0)
|
|
31
34
|
|
|
32
35
|
REQUEST_ID_HEADER = "X-Request-Id"
|
|
@@ -493,16 +496,19 @@ class Client:
|
|
|
493
496
|
|
|
494
497
|
def get_candles(self, *, symbol: str, interval: str = "1h", limit: int = 500,
|
|
495
498
|
exchange: str | None = None) -> dict:
|
|
496
|
-
"""Historical candles from the control plane (``GET /candles``).
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
499
|
+
"""Historical candles from the control plane (``GET /market/candles``).
|
|
500
|
+
|
|
501
|
+
The market router is mounted under ``/market`` (engine/api/routes/
|
|
502
|
+
market.py ``APIRouter(prefix="/market")``); 0.3.0 called ``/candles``
|
|
503
|
+
and 404'd on production — pinned by ``test_candles_path_matches_the_
|
|
504
|
+
engine_router``. Returns ``{"candles": [{t,o,h,l,c,v}, ...], "symbol",
|
|
505
|
+
"interval"}`` — abbreviated OHLCV keys, newest last. The backtester
|
|
506
|
+
wants the full names; ``backtest_candles`` in the MCP layer maps them.
|
|
501
507
|
"""
|
|
502
508
|
params: dict[str, Any] = {"symbol": symbol, "interval": interval, "limit": limit}
|
|
503
509
|
if exchange:
|
|
504
510
|
params["exchange"] = exchange
|
|
505
|
-
return self._request("GET",
|
|
511
|
+
return self._request("GET", CANDLES_PATH, params=params)
|
|
506
512
|
|
|
507
513
|
def create_backtest(self, *, body: dict) -> dict:
|
|
508
514
|
"""Submit a backtest (``POST /backtests``). Returns ``{backtest_id, status}``."""
|
|
@@ -46,7 +46,12 @@ def deposit(
|
|
|
46
46
|
output.error(f"could not get deposit addresses: {exc.detail}")
|
|
47
47
|
raise typer.Exit(code=1)
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
# The control plane names the EVM Unified Deposit Address `deposit_addr`
|
|
50
|
+
# (verified live 2026-09-06: {"deposit_addr", "solana_addr",
|
|
51
|
+
# "btc_addr_segwit", ...}); the older keys stay as fallbacks. Without
|
|
52
|
+
# `deposit_addr` the EVM row — the rail most users fund from — was blank.
|
|
53
|
+
evm = (uda.get("deposit_addr") or uda.get("evm_addr")
|
|
54
|
+
or uda.get("evm_address") or "")
|
|
50
55
|
sol = uda.get("solana_addr") or ""
|
|
51
56
|
btc = uda.get("btc_addr_segwit") or uda.get("btc_addr") or ""
|
|
52
57
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""`talis deposit` must show the EVM address the control plane actually returns.
|
|
2
|
+
|
|
3
|
+
Live shape of ``POST /deposits/uda`` (2026-09-06): ``deposit_addr`` (EVM),
|
|
4
|
+
``solana_addr``, ``btc_addr_segwit``, ``recipient_addr``, ``to_chain_id``,
|
|
5
|
+
``to_token_address``. 0.3.0 looked for ``evm_addr``/``evm_address`` only and
|
|
6
|
+
printed an empty EVM row — on the rail most users fund from.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
import respx
|
|
14
|
+
from typer.testing import CliRunner
|
|
15
|
+
|
|
16
|
+
from talis.cli import app
|
|
17
|
+
|
|
18
|
+
BASE = "http://api.test"
|
|
19
|
+
LIVE_SHAPE = {
|
|
20
|
+
"deposit_addr": "0xaFCD8Ebc0000000000000000000000000000dEaD",
|
|
21
|
+
"solana_addr": "2LNv8tS1c7000000000000000000000000000000000",
|
|
22
|
+
"btc_addr_segwit": "bc1q0ma0xp000000000000000000000000000000",
|
|
23
|
+
"recipient_addr": "0x62ec8F9100000000000000000000000000000000",
|
|
24
|
+
"to_chain_id": "1337",
|
|
25
|
+
"to_token_address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@respx.mock
|
|
30
|
+
def test_deposit_json_includes_the_evm_address_from_deposit_addr(stored_creds):
|
|
31
|
+
respx.post(f"{BASE}/deposits/uda").mock(return_value=httpx.Response(200, json=LIVE_SHAPE))
|
|
32
|
+
result = CliRunner().invoke(app, ["deposit", "--json"])
|
|
33
|
+
assert result.exit_code == 0, result.output
|
|
34
|
+
out = json.loads(result.stdout)
|
|
35
|
+
assert out["evm"] == LIVE_SHAPE["deposit_addr"]
|
|
36
|
+
assert out["solana"] == LIVE_SHAPE["solana_addr"]
|
|
37
|
+
assert out["bitcoin"] == LIVE_SHAPE["btc_addr_segwit"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@respx.mock
|
|
41
|
+
def test_deposit_still_accepts_the_older_evm_key(stored_creds):
|
|
42
|
+
respx.post(f"{BASE}/deposits/uda").mock(
|
|
43
|
+
return_value=httpx.Response(200, json={"evm_addr": "0x1111111111111111111111111111111111111111"}))
|
|
44
|
+
result = CliRunner().invoke(app, ["deposit", "--json"])
|
|
45
|
+
assert result.exit_code == 0, result.output
|
|
46
|
+
assert json.loads(result.stdout)["evm"] == "0x1111111111111111111111111111111111111111"
|
|
@@ -40,7 +40,7 @@ def _routes(valid=True, statuses=("RUNNING", "COMPLETED")):
|
|
|
40
40
|
respx.post(f"{BASE}/strategies/validate").mock(
|
|
41
41
|
return_value=httpx.Response(200, json={"valid": valid, "errors": [] if valid else ["bad trigger"],
|
|
42
42
|
"human_readable_summary": "x"}))
|
|
43
|
-
respx.get(f"{BASE}/candles").mock(
|
|
43
|
+
respx.get(f"{BASE}/market/candles").mock(
|
|
44
44
|
return_value=httpx.Response(200, json={"candles": CANDLES, "symbol": "X", "interval": "1h"}))
|
|
45
45
|
submit = respx.post(f"{BASE}/backtests").mock(
|
|
46
46
|
return_value=httpx.Response(200, json={"backtest_id": "bt_1", "status": "RUNNING"}))
|
|
@@ -107,3 +107,15 @@ def test_backtest_is_registered_and_read_only():
|
|
|
107
107
|
import inspect
|
|
108
108
|
assert "paper" not in inspect.signature(mcp_server.backtest).parameters
|
|
109
109
|
assert "confirm_real_money" not in inspect.signature(mcp_server.backtest).parameters
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_candles_path_matches_the_engine_router():
|
|
113
|
+
"""Non-tautological pin: the CLI's candle path must equal the ENGINE's real
|
|
114
|
+
mounted path (router prefix + route path). 0.3.0 shipped "/candles" and
|
|
115
|
+
404'd on production because the respx tests only mirrored the CLI's own
|
|
116
|
+
assumption. Skips where the engine package is not importable (CLI-only CI)."""
|
|
117
|
+
market = pytest.importorskip("engine.api.routes.market")
|
|
118
|
+
from talis import api as talis_api
|
|
119
|
+
# APIRoute.path already carries the router prefix ("/market/candles").
|
|
120
|
+
real = {r.path for r in market.router.routes if getattr(r, "path", None)}
|
|
121
|
+
assert talis_api.CANDLES_PATH in real, (talis_api.CANDLES_PATH, sorted(real))
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|