nexusquant-cli 0.6.0__tar.gz → 0.6.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.
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/PKG-INFO +1 -1
- nexusquant_cli-0.6.1/nexusquant_cli/__init__.py +1 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/data/signal_codes.json +10 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/roles/investor.py +16 -8
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/signal_explain.py +11 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/pyproject.toml +1 -1
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/tests/test_signal_explain.py +19 -0
- nexusquant_cli-0.6.0/nexusquant_cli/__init__.py +0 -1
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/.gitignore +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/CLAUDE.md +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/README.md +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/docs/investor-operations.md +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/__main__.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/api_client.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/auth_pkce.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/commands/__init__.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/commands/_util.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/commands/auth/__init__.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/commands/auth/cmd.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/commands/policy/__init__.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/commands/policy/cmd.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/commands/strategy/__init__.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/commands/strategy/cmd.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/config.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/embedded.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/identity.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/main.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/nexus.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/roles/__init__.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/roles/_agent.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/roles/admin.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/roles/auth.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/nexusquant_cli/roles/provider.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/scripts/gen_signal_codes.cjs +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/tests/__init__.py +0 -0
- {nexusquant_cli-0.6.0 → nexusquant_cli-0.6.1}/tests/test_nexus_cli.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.6.1"
|
|
@@ -139,6 +139,16 @@
|
|
|
139
139
|
"en": "The broker didn't respond in time, so the order was not sent (not filled). If this keeps happening, please check the broker connection in My strategies."
|
|
140
140
|
}
|
|
141
141
|
},
|
|
142
|
+
"in_doubt_absent": {
|
|
143
|
+
"label": {
|
|
144
|
+
"zh": "未执行 · 券商查无此单",
|
|
145
|
+
"en": "Not executed · Broker has no such order"
|
|
146
|
+
},
|
|
147
|
+
"detail": {
|
|
148
|
+
"zh": "下单结果当时未能确认,之后向券商核实,券商没有这笔订单(未成交)。本次信号不会补下。",
|
|
149
|
+
"en": "The result of this order couldn't be confirmed at the time. The broker later confirmed it has no such order (not filled). This signal will not be placed again."
|
|
150
|
+
}
|
|
151
|
+
},
|
|
142
152
|
"broker_not_connected": {
|
|
143
153
|
"label": {
|
|
144
154
|
"zh": "未执行 · 券商未连接",
|
|
@@ -344,15 +344,11 @@ def list_signals(
|
|
|
344
344
|
for col in ("time", "strategy", "ticker", "side", "price", "status", "your result"):
|
|
345
345
|
table.add_column(col, overflow="fold")
|
|
346
346
|
for s in logs:
|
|
347
|
-
|
|
348
|
-
if
|
|
349
|
-
detail = f"
|
|
350
|
-
elif res.get("skipped"):
|
|
351
|
-
detail = f"[yellow]skipped[/yellow] {res.get('risk_code') or ''}"
|
|
352
|
-
elif res.get("is_filled"):
|
|
353
|
-
detail = "[green]filled[/green]"
|
|
347
|
+
results = s.get("viewer_broker_results") or []
|
|
348
|
+
if len({r.get("account_tail") or "" for r in results}) > 1:
|
|
349
|
+
detail = "\n".join(f"{r.get('account_tail')} {_result_detail(s, r)}" for r in results)
|
|
354
350
|
else:
|
|
355
|
-
detail =
|
|
351
|
+
detail = _result_detail(s, s.get("viewer_broker_result") or {})
|
|
356
352
|
table.add_row(
|
|
357
353
|
str(s.get("signal_time") or s.get("received_at"))[:19],
|
|
358
354
|
str(s.get("strategy_id")), str(s.get("ticker")), str(s.get("direction")),
|
|
@@ -366,6 +362,16 @@ def list_signals(
|
|
|
366
362
|
)
|
|
367
363
|
|
|
368
364
|
|
|
365
|
+
def _result_detail(signal: dict, res: dict) -> str:
|
|
366
|
+
if res.get("error"):
|
|
367
|
+
return f"[red]{res.get('error')}[/red]"
|
|
368
|
+
if res.get("skipped"):
|
|
369
|
+
return f"[yellow]skipped[/yellow] {res.get('risk_code') or ''}"
|
|
370
|
+
if res.get("is_filled"):
|
|
371
|
+
return "[green]filled[/green]"
|
|
372
|
+
return str(signal.get("viewer_no_result_reason") or "")
|
|
373
|
+
|
|
374
|
+
|
|
369
375
|
@read_app.command("signal", help="One signal you received, and why it did or didn't place an order.")
|
|
370
376
|
def read_signal(
|
|
371
377
|
signal_id: Annotated[str, typer.Argument(help="Signal id (from `list signals`).")],
|
|
@@ -397,6 +403,8 @@ def read_signal(
|
|
|
397
403
|
console.print(f"[dim]{key}.{sub}[/dim] {txt.get('zh') if isinstance(txt, dict) else txt}")
|
|
398
404
|
if ex.get("broker_message"):
|
|
399
405
|
console.print(f"[dim]broker[/dim] {ex['broker_message']}")
|
|
406
|
+
for acct in ex.get("accounts", []):
|
|
407
|
+
console.print(f"account {acct['account']} {acct['outcome']}")
|
|
400
408
|
|
|
401
409
|
|
|
402
410
|
@read_app.command("stats", help="Your signal counts and broker success rate.")
|
|
@@ -52,6 +52,17 @@ def _upper_lookup(table: dict[str, Any], code: str | None) -> Any:
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
def explain(row: dict[str, Any]) -> dict[str, Any]:
|
|
55
|
+
"""The account the backend picked (a failed one, if any failed), plus every account when there are several."""
|
|
56
|
+
out = _explain_one(row)
|
|
57
|
+
results = row.get("viewer_broker_results") or []
|
|
58
|
+
if len({r.get("account_tail") or "" for r in results}) > 1:
|
|
59
|
+
out["accounts"] = [
|
|
60
|
+
{"account": r.get("account_tail"), **_explain_one({**row, "viewer_broker_result": r})} for r in results
|
|
61
|
+
]
|
|
62
|
+
return out
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _explain_one(row: dict[str, Any]) -> dict[str, Any]:
|
|
55
66
|
g = glossary()
|
|
56
67
|
vbr = row.get("viewer_broker_result") or None
|
|
57
68
|
listening = row.get("viewer_was_listening_at_signal_time")
|
|
@@ -162,3 +162,22 @@ def test_embedded_runs_admin_read(http: Recorder, monkeypatch, capsys):
|
|
|
162
162
|
config.set_token_override(None)
|
|
163
163
|
out = capsys.readouterr().out
|
|
164
164
|
assert rc == 0 and '"u@x.test"' in out
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_two_accounts_are_listed_separately():
|
|
168
|
+
filled = {"success": True, "is_filled": True, "account_tail": "****0001"}
|
|
169
|
+
failed = {"success": False, "error": "x", "account_tail": "****0002"}
|
|
170
|
+
out = explain(row(viewer_broker_result=failed, viewer_broker_results=[filled, failed]))
|
|
171
|
+
assert out["outcome"] == "failed"
|
|
172
|
+
assert [(a["account"], a["outcome"]) for a in out["accounts"]] == [("****0001", "filled"), ("****0002", "failed")]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_same_account_twice_is_not_listed():
|
|
176
|
+
one = {"success": True, "is_filled": True, "account_tail": "****0001"}
|
|
177
|
+
out = explain(row(viewer_broker_result=one, viewer_broker_results=[one, {**one}]))
|
|
178
|
+
assert "accounts" not in out
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_one_result_has_no_accounts_key():
|
|
182
|
+
out = explain(row(viewer_broker_result={"success": True, "is_filled": True}))
|
|
183
|
+
assert "accounts" not in out
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.6.0"
|
|
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
|