fixtureqa 0.4.16__tar.gz → 0.4.17__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.16/fixtureqa.egg-info → fixtureqa-0.4.17}/PKG-INFO +1 -1
  2. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/README.md +1 -1
  3. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/perf_engine.py +8 -5
  4. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/perf_models.py +3 -0
  5. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/perf_stats.py +34 -8
  6. fixtureqa-0.4.16/fixture/static/assets/index-BqX5ek3M.js → fixtureqa-0.4.17/fixture/static/assets/index-BM8-U5CW.js +23 -23
  7. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/static/index.html +1 -1
  8. {fixtureqa-0.4.16 → fixtureqa-0.4.17/fixtureqa.egg-info}/PKG-INFO +1 -1
  9. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixtureqa.egg-info/SOURCES.txt +1 -1
  10. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/pyproject.toml +1 -1
  11. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_perf_engine.py +18 -1
  12. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/LICENSE +0 -0
  13. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/__init__.py +0 -0
  14. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/__main__.py +0 -0
  15. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/__init__.py +0 -0
  16. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/app.py +0 -0
  17. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/connection_manager.py +0 -0
  18. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/deps.py +0 -0
  19. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/__init__.py +0 -0
  20. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/admin.py +0 -0
  21. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/auth.py +0 -0
  22. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/branding.py +0 -0
  23. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/custom_tags.py +0 -0
  24. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/fix_spec.py +0 -0
  25. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/messages.py +0 -0
  26. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/perf.py +0 -0
  27. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/scenarios.py +0 -0
  28. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/sessions.py +0 -0
  29. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/setup.py +0 -0
  30. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/spec_overlay.py +0 -0
  31. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/templates.py +0 -0
  32. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/routers/ws.py +0 -0
  33. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/api/schemas.py +0 -0
  34. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/config/__init__.py +0 -0
  35. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/__init__.py +0 -0
  36. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/atomic_io.py +0 -0
  37. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/auth.py +0 -0
  38. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/config_store.py +0 -0
  39. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/custom_tag_store.py +0 -0
  40. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/db_migrations.py +0 -0
  41. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/events.py +0 -0
  42. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/exec_csv_writer.py +0 -0
  43. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/fix_application.py +0 -0
  44. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/fix_builder.py +0 -0
  45. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/fix_parser.py +0 -0
  46. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/fix_spec_parser.py +0 -0
  47. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/fix_tags.py +0 -0
  48. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/fix_time.py +0 -0
  49. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/housekeeping.py +0 -0
  50. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/inbound.py +0 -0
  51. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/json_store.py +0 -0
  52. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/message_log.py +0 -0
  53. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/message_store.py +0 -0
  54. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/models.py +0 -0
  55. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/perf_payload.py +0 -0
  56. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/perf_store.py +0 -0
  57. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/perf_writer.py +0 -0
  58. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/scenario_runner.py +0 -0
  59. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/scenario_store.py +0 -0
  60. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/session.py +0 -0
  61. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/session_manager.py +0 -0
  62. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/spec_overlay_store.py +0 -0
  63. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/template_store.py +0 -0
  64. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/user_store.py +0 -0
  65. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/core/venue_responses.py +0 -0
  66. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/fix_specs/FIX42.xml +0 -0
  67. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/fix_specs/FIX44.xml +0 -0
  68. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/server.py +0 -0
  69. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
  70. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/static/assets/index-BwQf-cei.css +0 -0
  71. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/static/assets/index-CyNOPa0n.js +0 -0
  72. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
  73. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/static/favicon.svg +0 -0
  74. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixture/ui/__init__.py +0 -0
  75. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixtureqa.egg-info/dependency_links.txt +0 -0
  76. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixtureqa.egg-info/entry_points.txt +0 -0
  77. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixtureqa.egg-info/requires.txt +0 -0
  78. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/fixtureqa.egg-info/top_level.txt +0 -0
  79. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/setup.cfg +0 -0
  80. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_atomic_io.py +0 -0
  81. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_auth.py +0 -0
  82. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_config_store.py +0 -0
  83. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_connection_manager.py +0 -0
  84. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_db_migrations.py +0 -0
  85. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_fix_builder.py +0 -0
  86. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_health.py +0 -0
  87. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_inbound.py +0 -0
  88. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_inbound_validation.py +0 -0
  89. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_message_store.py +0 -0
  90. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_perf_api.py +0 -0
  91. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_perf_models.py +0 -0
  92. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_perf_payload.py +0 -0
  93. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_perf_rehydrate.py +0 -0
  94. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_scenarios.py +0 -0
  95. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_session_lifecycle.py +0 -0
  96. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_session_manager_concurrency.py +0 -0
  97. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_sessions.py +0 -0
  98. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_templates.py +0 -0
  99. {fixtureqa-0.4.16 → fixtureqa-0.4.17}/tests/test_ws.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fixtureqa
3
- Version: 0.4.16
3
+ Version: 0.4.17
4
4
  Summary: FIXture — FIX Protocol Testing Tool
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE
@@ -17,7 +17,7 @@ A Python FIX protocol testing tool. Supports FIX 4.2 and 4.4, dynamic session cr
17
17
  - **Templates** — create/edit reusable message templates with FIX spec field definitions
18
18
  - **Scenarios** — automated send/expect/delay test scripts with live pass/fail results
19
19
  - **Venue simulation** — auto-ack and auto-fill responses for inbound NewOrderSingles
20
- - **Performance testing** — rate-controlled order injection with latency correlation (p50/p95/p99), live dashboard, run history and CSV export; order/exec shapes templatable per counterparty
20
+ - **Performance testing** — rate-controlled order injection with latency correlation (p50/p95/p99), live dashboard, run history and CSV export; order/exec shapes templatable per counterparty; amend (35=G) and cancel (35=F) flavours with their own reply latencies (see [PERF.md](PERF.md))
21
21
  - **Log analysis** — Overview stats, Throughput chart, Reject detail, Latency measurement (p50/p95/p99)
22
22
  - **Export** — download session messages as CSV or raw FIX
23
23
  - **Sequence numbers** — view and reset TX/RX seqnums per session
@@ -1014,23 +1014,26 @@ class PerfRun:
1014
1014
  return (time.perf_counter_ns() - self._start_ns) / 1e9 if self._start_ns else 0.0
1015
1015
 
1016
1016
  def _build_snapshot(self) -> None:
1017
- ops, fps, sps, aps = self.stats.rates()
1017
+ r = self.stats.rates()
1018
1018
  client = None
1019
1019
  if self._has_client:
1020
1020
  client = ClientLeg(
1021
- orders_sent=self.stats.orders_sent, ops_live=round(ops, 1),
1021
+ orders_sent=self.stats.orders_sent, ops_live=round(r.ops, 1),
1022
1022
  pending=len(self._pending), dropped=self.stats.dropped,
1023
1023
  amends_sent=self.stats.amends_sent,
1024
1024
  replaces_received=self.stats.replaces_received,
1025
1025
  amends_lost=self.stats.amends_lost,
1026
1026
  amend_rejects=self.stats.amend_rejects,
1027
- aps_live=round(aps, 1),
1027
+ aps_live=round(r.aps, 1),
1028
1028
  cancels_sent=self.stats.cancels_sent,
1029
1029
  cancel_rejects=self.stats.cancel_rejects,
1030
1030
  cancels_canceled=self.stats.cancels_canceled,
1031
1031
  cancels_lost=self.stats.cancels_lost,
1032
+ cps_live=round(r.cps, 1),
1033
+ crps_live=round(r.crps, 1),
1034
+ ccps_live=round(r.ccps, 1),
1032
1035
  scenarios_dispatched=self.stats.scenarios_dispatched,
1033
- scenarios_per_sec=round(sps, 1),
1036
+ scenarios_per_sec=round(r.sps, 1),
1034
1037
  scenarios_completed=self.stats.scenarios_completed,
1035
1038
  scenarios_lost=self.stats.scenarios_lost)
1036
1039
  venue = None
@@ -1038,7 +1041,7 @@ class PerfRun:
1038
1041
  venue = VenueLeg(
1039
1042
  orders_received=self.stats.orders_received,
1040
1043
  acks_sent=self.stats.acks_sent,
1041
- fills_sent=self.stats.fills_sent, fps_live=round(fps, 1),
1044
+ fills_sent=self.stats.fills_sent, fps_live=round(r.fps, 1),
1042
1045
  fill_ratio=round(self.stats.fill_ratio(), 3),
1043
1046
  unfilled=max(0, self.stats.orders_sent - self.stats.completions),
1044
1047
  amends_received=self.stats.amends_received,
@@ -245,6 +245,9 @@ class ClientLeg(BaseModel):
245
245
  cancel_rejects: int = 0
246
246
  cancels_canceled: int = 0
247
247
  cancels_lost: int = 0
248
+ cps_live: float = 0.0
249
+ crps_live: float = 0.0
250
+ ccps_live: float = 0.0
248
251
  scenarios_dispatched: int = 0
249
252
  scenarios_per_sec: float = 0.0
250
253
  scenarios_completed: int = 0
@@ -11,6 +11,20 @@ from __future__ import annotations
11
11
  import asyncio
12
12
  import random
13
13
  import time
14
+ from typing import NamedTuple
15
+
16
+
17
+ class Rates(NamedTuple):
18
+ """Per-second deltas since the previous snapshot (one entry per charted
19
+ stream). Extend here + in PerfStats.rates() when a new flavour adds a
20
+ charted message stream."""
21
+ ops: float # orders sent/s
22
+ fps: float # fills sent/s
23
+ sps: float # scenarios dispatched/s
24
+ aps: float # amends sent/s (35=G)
25
+ cps: float # cancels sent/s (35=F)
26
+ crps: float # cancel-rejects received/s (35=9)
27
+ ccps: float # canceled received/s (150=4)
14
28
 
15
29
 
16
30
  class TokenBucket:
@@ -152,23 +166,35 @@ class PerfStats:
152
166
  self._last_fills = 0
153
167
  self._last_scen = 0
154
168
  self._last_amends = 0
169
+ self._last_cancels = 0
170
+ self._last_cancel_rejects = 0
171
+ self._last_canceled = 0
155
172
 
156
- def rates(self) -> tuple[float, float, float, float]:
157
- """(orders/s, fills/s, scenarios/s, amends/s) since the last call (delta-based)."""
173
+ def rates(self) -> Rates:
174
+ """Per-second deltas since the last call. Stateful — call exactly once
175
+ per snapshot (it advances the delta baseline each time)."""
158
176
  now = time.monotonic()
159
177
  dt = now - self._last_t
160
178
  if dt <= 0:
161
- return 0.0, 0.0, 0.0, 0.0
162
- ops = (self.orders_sent - self._last_orders) / dt
163
- fps = (self.fills_sent - self._last_fills) / dt
164
- sps = (self.scenarios_dispatched - self._last_scen) / dt
165
- aps = (self.amends_sent - self._last_amends) / dt
179
+ return Rates(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
180
+ r = Rates(
181
+ ops=(self.orders_sent - self._last_orders) / dt,
182
+ fps=(self.fills_sent - self._last_fills) / dt,
183
+ sps=(self.scenarios_dispatched - self._last_scen) / dt,
184
+ aps=(self.amends_sent - self._last_amends) / dt,
185
+ cps=(self.cancels_sent - self._last_cancels) / dt,
186
+ crps=(self.cancel_rejects - self._last_cancel_rejects) / dt,
187
+ ccps=(self.cancels_canceled - self._last_canceled) / dt,
188
+ )
166
189
  self._last_t = now
167
190
  self._last_orders = self.orders_sent
168
191
  self._last_fills = self.fills_sent
169
192
  self._last_scen = self.scenarios_dispatched
170
193
  self._last_amends = self.amends_sent
171
- return ops, fps, sps, aps
194
+ self._last_cancels = self.cancels_sent
195
+ self._last_cancel_rejects = self.cancel_rejects
196
+ self._last_canceled = self.cancels_canceled
197
+ return r
172
198
 
173
199
  def fill_ratio(self) -> float:
174
200
  # Fraction of orders fully filled (cumQty >= orderQty). Uses completions,