fixtureqa 0.4.11__tar.gz → 0.4.13__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.
- {fixtureqa-0.4.11/fixtureqa.egg-info → fixtureqa-0.4.13}/PKG-INFO +1 -1
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/perf_payload.py +24 -4
- {fixtureqa-0.4.11 → fixtureqa-0.4.13/fixtureqa.egg-info}/PKG-INFO +1 -1
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/pyproject.toml +1 -1
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_perf_payload.py +53 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/LICENSE +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/README.md +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/__init__.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/__main__.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/__init__.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/app.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/connection_manager.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/deps.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/__init__.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/admin.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/auth.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/branding.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/custom_tags.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/fix_spec.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/messages.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/perf.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/scenarios.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/sessions.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/setup.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/spec_overlay.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/templates.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/routers/ws.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/api/schemas.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/config/__init__.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/__init__.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/atomic_io.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/auth.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/config_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/custom_tag_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/db_migrations.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/events.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/exec_csv_writer.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/fix_application.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/fix_builder.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/fix_parser.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/fix_spec_parser.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/fix_tags.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/fix_time.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/housekeeping.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/inbound.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/json_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/message_log.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/message_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/models.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/perf_engine.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/perf_models.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/perf_stats.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/perf_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/perf_writer.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/scenario_runner.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/scenario_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/session.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/session_manager.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/spec_overlay_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/template_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/user_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/core/venue_responses.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/fix_specs/FIX42.xml +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/fix_specs/FIX44.xml +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/server.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/static/assets/index-BwQf-cei.css +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/static/assets/index-CyNOPa0n.js +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/static/assets/index-DHd_iEnT.js +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/static/favicon.svg +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/static/index.html +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixture/ui/__init__.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixtureqa.egg-info/SOURCES.txt +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixtureqa.egg-info/dependency_links.txt +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixtureqa.egg-info/entry_points.txt +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixtureqa.egg-info/requires.txt +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/fixtureqa.egg-info/top_level.txt +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/setup.cfg +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_atomic_io.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_auth.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_config_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_connection_manager.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_db_migrations.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_fix_builder.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_health.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_inbound.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_inbound_validation.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_message_store.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_perf_api.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_perf_engine.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_perf_models.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_perf_rehydrate.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_scenarios.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_session_lifecycle.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_session_manager_concurrency.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_sessions.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_templates.py +0 -0
- {fixtureqa-0.4.11 → fixtureqa-0.4.13}/tests/test_ws.py +0 -0
|
@@ -59,6 +59,13 @@ _EXEC_TOKENS = {"cum_qty", "leaves_qty", "last_qty", "order_qty", "ord_status",
|
|
|
59
59
|
|
|
60
60
|
_SIDE_BUY, _SIDE_SELL = "1", "2"
|
|
61
61
|
|
|
62
|
+
# Routing/identity tags mirrored from an order onto every 35=G in its chain —
|
|
63
|
+
# endpoints commonly require these to match the original order. Header tags
|
|
64
|
+
# 50/57/115/116/128/129/142/143/144/145 plus the body tags Account(1) and
|
|
65
|
+
# ClientID(109, FIX 4.2) used for the same purpose. CompIDs (49/56) are
|
|
66
|
+
# transport-owned and excluded (the session stamps them).
|
|
67
|
+
AMEND_MIRROR_TAGS = (1, 50, 57, 109, 115, 116, 128, 129, 142, 143, 144, 145)
|
|
68
|
+
|
|
62
69
|
|
|
63
70
|
class PerfConfigError(Exception):
|
|
64
71
|
"""Raised for bad templates/tokens at run start → surfaced as 422."""
|
|
@@ -314,7 +321,8 @@ class PayloadFactory:
|
|
|
314
321
|
self._apply_expiry(msg, self._gen_rules)
|
|
315
322
|
return msg, {"corr_id": corr, "clordid": clordid, "symbol": ctx["symbol"],
|
|
316
323
|
"side": ctx["side"], "qty": ctx["qty"], "price": ctx["price"],
|
|
317
|
-
"order_qty": ctx["qty"], "msg_type": "D"
|
|
324
|
+
"order_qty": ctx["qty"], "msg_type": "D",
|
|
325
|
+
"mirror_tags": self._mirror_tags(msg)}
|
|
318
326
|
|
|
319
327
|
# -- template message (scenarios) ----------------------------------
|
|
320
328
|
|
|
@@ -340,7 +348,8 @@ class PayloadFactory:
|
|
|
340
348
|
refs[tpl_id] = clordid
|
|
341
349
|
return msg, {"corr_id": corr, "clordid": clordid, "symbol": ctx["symbol"],
|
|
342
350
|
"side": ctx["side"], "qty": ctx["qty"], "price": ctx["price"],
|
|
343
|
-
"order_qty": ctx["qty"], "msg_type": mtype
|
|
351
|
+
"order_qty": ctx["qty"], "msg_type": mtype,
|
|
352
|
+
"mirror_tags": self._mirror_tags(msg)}
|
|
344
353
|
|
|
345
354
|
def _resolve(self, value: str, ctx: dict, corr: str, clordid: str, refs: dict,
|
|
346
355
|
tag: int = 0) -> str:
|
|
@@ -385,13 +394,21 @@ class PayloadFactory:
|
|
|
385
394
|
def _clordid(self) -> str:
|
|
386
395
|
return "PCL-" + _guid()[:12].upper()
|
|
387
396
|
|
|
397
|
+
@staticmethod
|
|
398
|
+
def _mirror_tags(msg: Message) -> dict[int, str]:
|
|
399
|
+
"""Rendered routing/identity header tags present on this order — stamped
|
|
400
|
+
verbatim onto every amend in its chain."""
|
|
401
|
+
return {t: v for t in AMEND_MIRROR_TAGS if (v := msg.get_field_or(t, ""))}
|
|
402
|
+
|
|
388
403
|
# -- amend (35=G OrderCancelReplaceRequest, price-only) -------------
|
|
389
404
|
|
|
390
405
|
def build_amend(self, meta: dict, new_price: float) -> tuple[Message, str]:
|
|
391
406
|
"""Re-price a working order. Carries the order's correlation id so the
|
|
392
407
|
replace acks route to the same pending entry; OrigClOrdID(41) is the
|
|
393
408
|
current chain tip, ClOrdID(11) is fresh — returned so the engine can
|
|
394
|
-
promote the chain once the venue confirms (150=5).
|
|
409
|
+
promote the chain once the venue confirms (150=5). Routing/identity
|
|
410
|
+
header tags (AMEND_MIRROR_TAGS) are echoed verbatim from the order —
|
|
411
|
+
endpoints commonly require them to match across the chain."""
|
|
395
412
|
p = self.config.payload.order
|
|
396
413
|
new_clordid = self._clordid()
|
|
397
414
|
msg = Message()
|
|
@@ -399,6 +416,8 @@ class PayloadFactory:
|
|
|
399
416
|
msg.set_field(11, new_clordid)
|
|
400
417
|
msg.set_field(41, meta["clordid"])
|
|
401
418
|
msg.set_field(self.config.correlation_tag, meta["corr_id"])
|
|
419
|
+
for tag, val in (meta.get("mirror_tags") or {}).items():
|
|
420
|
+
msg.set_field(tag, val)
|
|
402
421
|
msg.set_field(55, meta["symbol"])
|
|
403
422
|
msg.set_field(54, meta["side"])
|
|
404
423
|
msg.set_field(38, str(meta["order_qty"]))
|
|
@@ -463,7 +482,8 @@ class PayloadFactory:
|
|
|
463
482
|
self._apply_expiry(msg, fields)
|
|
464
483
|
return msg, {"corr_id": corr, "clordid": clordid, "symbol": ctx["symbol"],
|
|
465
484
|
"side": ctx["side"], "qty": ctx["qty"], "price": ctx["price"],
|
|
466
|
-
"order_qty": order_qty, "msg_type": mtype
|
|
485
|
+
"order_qty": order_qty, "msg_type": mtype,
|
|
486
|
+
"mirror_tags": self._mirror_tags(msg)}
|
|
467
487
|
|
|
468
488
|
# -- Phase B: templated ExecutionReport (venue ack/fill) ----------------
|
|
469
489
|
|
|
@@ -433,3 +433,56 @@ def test_exec_id_is_dashed_uuid():
|
|
|
433
433
|
msg = f.build_exec_message({376: "CORR1", 38: "100"}, exec_type="2", ord_status="2",
|
|
434
434
|
last_qty=100, cum_qty=100, leaves_qty=0, px="10")
|
|
435
435
|
assert _is_dashed_uuid(msg.get_field_or(25116, ""))
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
# --------------------------------------------------------------------------
|
|
439
|
+
# Routing/identity header tags (50/57/115/116/128/129/142/143/144/145)
|
|
440
|
+
# echoed from the order onto each 35=G
|
|
441
|
+
# --------------------------------------------------------------------------
|
|
442
|
+
|
|
443
|
+
def test_amend_mirrors_routing_headers_from_gen_rules():
|
|
444
|
+
rules = {"142": "DUB", "50": "TRADER1", "115": "CLIENTCO", "129": "DESK9",
|
|
445
|
+
"109": "CLIENT42", "1": "ACC-7"}
|
|
446
|
+
f = PayloadFactory(_config([], gen_rules=rules), FakeTemplateStore({}), "u")
|
|
447
|
+
order, meta = f.build_single_order()
|
|
448
|
+
assert meta["mirror_tags"] == {142: "DUB", 50: "TRADER1", 115: "CLIENTCO",
|
|
449
|
+
129: "DESK9", 109: "CLIENT42", 1: "ACC-7"}
|
|
450
|
+
amend, _ = f.build_amend(meta, 10.05)
|
|
451
|
+
for tag, val in meta["mirror_tags"].items():
|
|
452
|
+
assert order.get_field_or(tag, "") == val
|
|
453
|
+
assert amend.get_field_or(tag, "") == val
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def test_amend_mirrors_routing_headers_from_order_template():
|
|
457
|
+
ts = FakeTemplateStore({"ord": _tpl("ord", {"35": "D", "142": "LDN", "57": "BROKER",
|
|
458
|
+
"143": "NY", "116": "SUB1", "144": "PAR",
|
|
459
|
+
"128": "OMSX", "145": "FRA"})})
|
|
460
|
+
f = PayloadFactory(_config([], order_template_id="ord"), ts, "u")
|
|
461
|
+
order, meta = f.build_single_order()
|
|
462
|
+
assert meta["mirror_tags"] == {57: "BROKER", 116: "SUB1", 128: "OMSX",
|
|
463
|
+
142: "LDN", 143: "NY", 144: "PAR", 145: "FRA"}
|
|
464
|
+
amend, _ = f.build_amend(meta, 10.05)
|
|
465
|
+
for tag, val in meta["mirror_tags"].items():
|
|
466
|
+
assert amend.get_field_or(tag, "") == val
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def test_amend_mirrors_dynamic_value_per_order_not_regenerated():
|
|
470
|
+
# A dynamic rule renders differently per order; each amend must carry its
|
|
471
|
+
# OWN order's rendered value, not a fresh render.
|
|
472
|
+
f = PayloadFactory(_config([], gen_rules={"50": "T-{seq(4)}"}), FakeTemplateStore({}), "u")
|
|
473
|
+
o1, m1 = f.build_single_order()
|
|
474
|
+
o2, m2 = f.build_single_order()
|
|
475
|
+
assert o1.get_field_or(50, "") == "T-0001" and o2.get_field_or(50, "") == "T-0002"
|
|
476
|
+
a1, _ = f.build_amend(m1, 10.05)
|
|
477
|
+
a2, _ = f.build_amend(m2, 10.05)
|
|
478
|
+
assert a1.get_field_or(50, "") == "T-0001"
|
|
479
|
+
assert a2.get_field_or(50, "") == "T-0002"
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def test_amend_omits_routing_headers_when_order_has_none():
|
|
483
|
+
f = PayloadFactory(_config([]), FakeTemplateStore({}), "u")
|
|
484
|
+
_, meta = f.build_single_order()
|
|
485
|
+
assert meta["mirror_tags"] == {}
|
|
486
|
+
amend, _ = f.build_amend(meta, 10.05)
|
|
487
|
+
for tag in (1, 50, 57, 109, 115, 116, 128, 129, 142, 143, 144, 145):
|
|
488
|
+
assert amend.get_field_or(tag, "") == ""
|
|
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
|
|
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
|