|
1 | | -from collections import defaultdict |
| 1 | +from collections import Counter, defaultdict |
2 | 2 | import json |
3 | 3 | from datetime import datetime, timezone |
4 | 4 | from itertools import combinations |
@@ -668,6 +668,9 @@ def maker_hybrid_tape_sim_report( |
668 | 668 | "diagnostic_only": True, |
669 | 669 | "diagnostic_warning": "public trade prints can prove sell-through, but queue position is still uncertain without live order fills", |
670 | 670 | "by_kind": _hybrid_fill_summary_by_kind(results), |
| 671 | + "rejection_by_reason": _hybrid_rejection_summary(results), |
| 672 | + "maker_fill_progress_distribution": _maker_fill_progress_distribution(results), |
| 673 | + "top_unfilled_maker_legs": _top_unfilled_maker_legs(results, top_n), |
671 | 674 | "top_completed": sorted(completed, key=_hybrid_result_sort_key)[:top_n], |
672 | 675 | "top_unique_completed": sorted(unique_completed, key=_hybrid_result_sort_key)[:top_n], |
673 | 676 | "top_unsafe": sorted(unsafe, key=_hybrid_result_sort_key)[:top_n], |
@@ -2571,6 +2574,184 @@ def _hybrid_fill_summary_by_kind(rows: List[dict]) -> list: |
2571 | 2574 | return sorted(summary.values(), key=lambda row: (-row["completed_count"], -row["max_completed_realized_edge_at_cap"], row["kind"])) |
2572 | 2575 |
|
2573 | 2576 |
|
| 2577 | +def _hybrid_rejection_summary(rows: List[dict]) -> list: |
| 2578 | + summary = {} |
| 2579 | + for row in rows: |
| 2580 | + reason = str(row.get("rejection_reason") or ("completed" if row.get("completed") else "unknown")) |
| 2581 | + item = summary.setdefault( |
| 2582 | + reason, |
| 2583 | + { |
| 2584 | + "reason": reason, |
| 2585 | + "candidate_observation_count": 0, |
| 2586 | + "completed_count": 0, |
| 2587 | + "max_expected_edge_at_cap": 0.0, |
| 2588 | + "max_expected_edge_per_share": 0.0, |
| 2589 | + "max_realized_edge_at_cap": 0.0, |
| 2590 | + }, |
| 2591 | + ) |
| 2592 | + item["candidate_observation_count"] += 1 |
| 2593 | + if row.get("completed"): |
| 2594 | + item["completed_count"] += 1 |
| 2595 | + item["max_expected_edge_at_cap"] = max( |
| 2596 | + item["max_expected_edge_at_cap"], |
| 2597 | + float(row.get("expected_edge_at_cap") or 0.0), |
| 2598 | + ) |
| 2599 | + item["max_expected_edge_per_share"] = max( |
| 2600 | + item["max_expected_edge_per_share"], |
| 2601 | + float(row.get("expected_edge_per_share") or 0.0), |
| 2602 | + ) |
| 2603 | + item["max_realized_edge_at_cap"] = max( |
| 2604 | + item["max_realized_edge_at_cap"], |
| 2605 | + float(row.get("realized_edge_at_cap") or 0.0), |
| 2606 | + ) |
| 2607 | + return sorted( |
| 2608 | + summary.values(), |
| 2609 | + key=lambda row: ( |
| 2610 | + -row["candidate_observation_count"], |
| 2611 | + -row["max_expected_edge_at_cap"], |
| 2612 | + row["reason"], |
| 2613 | + ), |
| 2614 | + ) |
| 2615 | + |
| 2616 | + |
| 2617 | +def _maker_fill_progress_distribution(rows: List[dict]) -> list: |
| 2618 | + counts = Counter( |
| 2619 | + ( |
| 2620 | + int(row.get("filled_maker_leg_count") or 0), |
| 2621 | + int(row.get("maker_leg_count") or 0), |
| 2622 | + ) |
| 2623 | + for row in rows |
| 2624 | + ) |
| 2625 | + distribution = [] |
| 2626 | + for (filled_count, maker_count), count in counts.items(): |
| 2627 | + distribution.append( |
| 2628 | + { |
| 2629 | + "filled_maker_leg_count": filled_count, |
| 2630 | + "maker_leg_count": maker_count, |
| 2631 | + "candidate_observation_count": count, |
| 2632 | + "maker_leg_fill_ratio": filled_count / maker_count if maker_count else 0.0, |
| 2633 | + } |
| 2634 | + ) |
| 2635 | + return sorted( |
| 2636 | + distribution, |
| 2637 | + key=lambda row: ( |
| 2638 | + row["filled_maker_leg_count"], |
| 2639 | + row["maker_leg_count"], |
| 2640 | + -row["candidate_observation_count"], |
| 2641 | + ), |
| 2642 | + ) |
| 2643 | + |
| 2644 | + |
| 2645 | +def _top_unfilled_maker_legs(rows: List[dict], top_n: int) -> list: |
| 2646 | + if top_n <= 0: |
| 2647 | + return [] |
| 2648 | + summary = {} |
| 2649 | + for row in rows: |
| 2650 | + unfilled_indices = set(int(index) for index in (row.get("unfilled_maker_indices") or [])) |
| 2651 | + for index, leg in enumerate(row.get("maker_legs") or []): |
| 2652 | + source_index = _source_leg_index(leg, index) |
| 2653 | + key = ( |
| 2654 | + str(leg.get("venue") or ""), |
| 2655 | + str(leg.get("market_id") or ""), |
| 2656 | + str(leg.get("token") or ""), |
| 2657 | + str(leg.get("token_id") or ""), |
| 2658 | + str(leg.get("side") or ""), |
| 2659 | + float(leg.get("limit_price") or 0.0), |
| 2660 | + str(leg.get("quote_mode") or ""), |
| 2661 | + int(leg.get("quote_offset_ticks") or 0), |
| 2662 | + ) |
| 2663 | + item = summary.setdefault( |
| 2664 | + key, |
| 2665 | + { |
| 2666 | + "venue": key[0], |
| 2667 | + "market_id": key[1], |
| 2668 | + "token": key[2], |
| 2669 | + "token_id": key[3], |
| 2670 | + "side": key[4], |
| 2671 | + "limit_price": key[5], |
| 2672 | + "quote_mode": key[6], |
| 2673 | + "quote_offset_ticks": key[7], |
| 2674 | + "best_bid": _float_or_none(leg.get("best_bid")), |
| 2675 | + "best_ask": _float_or_none(leg.get("best_ask")), |
| 2676 | + "spread": _float_or_none(leg.get("spread")), |
| 2677 | + "candidate_observation_count": 0, |
| 2678 | + "unfilled_count": 0, |
| 2679 | + "max_expected_edge_at_cap": 0.0, |
| 2680 | + "max_expected_edge_per_share": 0.0, |
| 2681 | + "min_distance_to_best_ask": None, |
| 2682 | + "max_improvement_over_best_bid": 0.0, |
| 2683 | + }, |
| 2684 | + ) |
| 2685 | + item["candidate_observation_count"] += 1 |
| 2686 | + item["max_expected_edge_at_cap"] = max( |
| 2687 | + item["max_expected_edge_at_cap"], |
| 2688 | + float(row.get("expected_edge_at_cap") or 0.0), |
| 2689 | + ) |
| 2690 | + item["max_expected_edge_per_share"] = max( |
| 2691 | + item["max_expected_edge_per_share"], |
| 2692 | + float(row.get("expected_edge_per_share") or 0.0), |
| 2693 | + ) |
| 2694 | + distance = _leg_distance_to_best_ask(leg) |
| 2695 | + if distance is not None: |
| 2696 | + item["min_distance_to_best_ask"] = ( |
| 2697 | + distance |
| 2698 | + if item["min_distance_to_best_ask"] is None |
| 2699 | + else min(item["min_distance_to_best_ask"], distance) |
| 2700 | + ) |
| 2701 | + improvement = _leg_improvement_over_best_bid(leg) |
| 2702 | + if improvement is not None: |
| 2703 | + item["max_improvement_over_best_bid"] = max(item["max_improvement_over_best_bid"], improvement) |
| 2704 | + if source_index in unfilled_indices: |
| 2705 | + item["unfilled_count"] += 1 |
| 2706 | + |
| 2707 | + rows = [] |
| 2708 | + for item in summary.values(): |
| 2709 | + if item["unfilled_count"] <= 0: |
| 2710 | + continue |
| 2711 | + count = item["candidate_observation_count"] |
| 2712 | + item["unfilled_rate"] = item["unfilled_count"] / count if count else 0.0 |
| 2713 | + rows.append(item) |
| 2714 | + return sorted( |
| 2715 | + rows, |
| 2716 | + key=lambda row: ( |
| 2717 | + -row["unfilled_count"], |
| 2718 | + -row["unfilled_rate"], |
| 2719 | + -row["max_expected_edge_at_cap"], |
| 2720 | + row["market_id"], |
| 2721 | + row["token"], |
| 2722 | + ), |
| 2723 | + )[:top_n] |
| 2724 | + |
| 2725 | + |
| 2726 | +def _source_leg_index(leg: dict, default: int) -> int: |
| 2727 | + try: |
| 2728 | + return int(leg.get("source_leg_index", default)) |
| 2729 | + except (TypeError, ValueError): |
| 2730 | + return default |
| 2731 | + |
| 2732 | + |
| 2733 | +def _leg_distance_to_best_ask(leg: dict) -> Optional[float]: |
| 2734 | + distance = _float_or_none(leg.get("distance_to_best_ask")) |
| 2735 | + if distance is not None: |
| 2736 | + return distance |
| 2737 | + best_ask = _float_or_none(leg.get("best_ask")) |
| 2738 | + limit_price = _float_or_none(leg.get("limit_price")) |
| 2739 | + if best_ask is None or limit_price is None: |
| 2740 | + return None |
| 2741 | + return best_ask - limit_price |
| 2742 | + |
| 2743 | + |
| 2744 | +def _leg_improvement_over_best_bid(leg: dict) -> Optional[float]: |
| 2745 | + improvement = _float_or_none(leg.get("improvement_over_best_bid")) |
| 2746 | + if improvement is not None: |
| 2747 | + return improvement |
| 2748 | + best_bid = _float_or_none(leg.get("best_bid")) |
| 2749 | + limit_price = _float_or_none(leg.get("limit_price")) |
| 2750 | + if best_bid is None or limit_price is None: |
| 2751 | + return None |
| 2752 | + return max(0.0, limit_price - best_bid) |
| 2753 | + |
| 2754 | + |
2574 | 2755 | def _unique_tape_completed_rows(rows: List[dict]) -> List[dict]: |
2575 | 2756 | deduped = {} |
2576 | 2757 | for row in rows: |
|
0 commit comments