fixtureqa 0.4.11__tar.gz → 0.4.12__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 (99) hide show
  1. {fixtureqa-0.4.11/fixtureqa.egg-info → fixtureqa-0.4.12}/PKG-INFO +1 -1
  2. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/perf_payload.py +24 -4
  3. {fixtureqa-0.4.11 → fixtureqa-0.4.12/fixtureqa.egg-info}/PKG-INFO +1 -1
  4. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/pyproject.toml +1 -1
  5. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_perf_payload.py +53 -0
  6. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/LICENSE +0 -0
  7. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/README.md +0 -0
  8. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/__init__.py +0 -0
  9. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/__main__.py +0 -0
  10. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/__init__.py +0 -0
  11. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/app.py +0 -0
  12. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/connection_manager.py +0 -0
  13. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/deps.py +0 -0
  14. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/__init__.py +0 -0
  15. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/admin.py +0 -0
  16. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/auth.py +0 -0
  17. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/branding.py +0 -0
  18. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/custom_tags.py +0 -0
  19. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/fix_spec.py +0 -0
  20. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/messages.py +0 -0
  21. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/perf.py +0 -0
  22. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/scenarios.py +0 -0
  23. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/sessions.py +0 -0
  24. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/setup.py +0 -0
  25. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/spec_overlay.py +0 -0
  26. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/templates.py +0 -0
  27. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/routers/ws.py +0 -0
  28. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/api/schemas.py +0 -0
  29. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/config/__init__.py +0 -0
  30. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/__init__.py +0 -0
  31. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/atomic_io.py +0 -0
  32. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/auth.py +0 -0
  33. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/config_store.py +0 -0
  34. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/custom_tag_store.py +0 -0
  35. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/db_migrations.py +0 -0
  36. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/events.py +0 -0
  37. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/exec_csv_writer.py +0 -0
  38. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/fix_application.py +0 -0
  39. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/fix_builder.py +0 -0
  40. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/fix_parser.py +0 -0
  41. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/fix_spec_parser.py +0 -0
  42. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/fix_tags.py +0 -0
  43. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/fix_time.py +0 -0
  44. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/housekeeping.py +0 -0
  45. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/inbound.py +0 -0
  46. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/json_store.py +0 -0
  47. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/message_log.py +0 -0
  48. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/message_store.py +0 -0
  49. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/models.py +0 -0
  50. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/perf_engine.py +0 -0
  51. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/perf_models.py +0 -0
  52. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/perf_stats.py +0 -0
  53. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/perf_store.py +0 -0
  54. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/perf_writer.py +0 -0
  55. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/scenario_runner.py +0 -0
  56. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/scenario_store.py +0 -0
  57. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/session.py +0 -0
  58. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/session_manager.py +0 -0
  59. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/spec_overlay_store.py +0 -0
  60. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/template_store.py +0 -0
  61. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/user_store.py +0 -0
  62. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/core/venue_responses.py +0 -0
  63. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/fix_specs/FIX42.xml +0 -0
  64. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/fix_specs/FIX44.xml +0 -0
  65. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/server.py +0 -0
  66. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
  67. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/static/assets/index-BwQf-cei.css +0 -0
  68. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/static/assets/index-CyNOPa0n.js +0 -0
  69. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/static/assets/index-DHd_iEnT.js +0 -0
  70. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
  71. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/static/favicon.svg +0 -0
  72. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/static/index.html +0 -0
  73. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixture/ui/__init__.py +0 -0
  74. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixtureqa.egg-info/SOURCES.txt +0 -0
  75. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixtureqa.egg-info/dependency_links.txt +0 -0
  76. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixtureqa.egg-info/entry_points.txt +0 -0
  77. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixtureqa.egg-info/requires.txt +0 -0
  78. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/fixtureqa.egg-info/top_level.txt +0 -0
  79. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/setup.cfg +0 -0
  80. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_atomic_io.py +0 -0
  81. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_auth.py +0 -0
  82. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_config_store.py +0 -0
  83. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_connection_manager.py +0 -0
  84. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_db_migrations.py +0 -0
  85. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_fix_builder.py +0 -0
  86. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_health.py +0 -0
  87. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_inbound.py +0 -0
  88. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_inbound_validation.py +0 -0
  89. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_message_store.py +0 -0
  90. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_perf_api.py +0 -0
  91. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_perf_engine.py +0 -0
  92. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_perf_models.py +0 -0
  93. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_perf_rehydrate.py +0 -0
  94. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_scenarios.py +0 -0
  95. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_session_lifecycle.py +0 -0
  96. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_session_manager_concurrency.py +0 -0
  97. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_sessions.py +0 -0
  98. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_templates.py +0 -0
  99. {fixtureqa-0.4.11 → fixtureqa-0.4.12}/tests/test_ws.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fixtureqa
3
- Version: 0.4.11
3
+ Version: 0.4.12
4
4
  Summary: FIXture — FIX Protocol Testing Tool
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE
@@ -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 ClientID(109), the FIX 4.2 body
65
+ # tag for the same purpose. CompIDs (49/56) are transport-owned and excluded
66
+ # (the session stamps them).
67
+ AMEND_MIRROR_TAGS = (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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fixtureqa
3
- Version: 0.4.11
3
+ Version: 0.4.12
4
4
  Summary: FIXture — FIX Protocol Testing Tool
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fixtureqa"
7
- version = "0.4.11"
7
+ version = "0.4.12"
8
8
  description = "FIXture — FIX Protocol Testing Tool"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -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"}
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"}
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 (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