Add Entry opportunity training diagnostics
This commit is contained in:
@@ -20,6 +20,7 @@ from trader_training.dynamic_exit_search import search_dynamic_exit_plans
|
||||
from trader_training.entry_condition_pair_screen import screen_entry_condition_pairs
|
||||
from trader_training.entry_feature_screen import _bucket_edges, _screen_edge_column
|
||||
from trader_training.entry_mae_label_diagnostic import diagnose_entry_mae_labels
|
||||
from trader_training.good_trade_structure import _side_frame, _top_fraction_metrics
|
||||
from trader_training.io_utils import read_json, write_json
|
||||
from trader_training.labels import ENTRY_LABEL_METHOD, _path_stats_for_group, build_entry_labels
|
||||
from trader_training.nonlinear_pm_probe import _expanded_threshold_candidates
|
||||
@@ -97,6 +98,27 @@ class TrainingContractTest(unittest.TestCase):
|
||||
self.assertEqual("ALL_FIT_ROWS", default_filter)
|
||||
self.assertEqual([True, True, True, True], default_mask.tolist())
|
||||
|
||||
def test_conditional_entry_training_can_use_side_opportunity_rows(self) -> None:
|
||||
train = pd.DataFrame(
|
||||
{
|
||||
"long_max_achievable_net_edge_bps": [45.0, 10.0, 60.0, 39.0],
|
||||
"short_max_achievable_net_edge_bps": [8.0, 42.0, 15.0, 80.0],
|
||||
}
|
||||
)
|
||||
args = Namespace(
|
||||
conditional_entry_source="side_opportunity",
|
||||
conditional_entry_direction_labels=False,
|
||||
conditional_entry_opportunity_bps=40.0,
|
||||
)
|
||||
|
||||
long_mask, long_filter = _head_train_mask("ENTRY", "long_entry_prob", train, args)
|
||||
short_mask, short_filter = _head_train_mask("ENTRY", "short_expected_net_edge_bps", train, args)
|
||||
|
||||
self.assertEqual("SIDE_OPPORTUNITY_LONG_GE_40_BPS_FIT_ROWS", long_filter)
|
||||
self.assertEqual([True, False, True, False], long_mask.tolist())
|
||||
self.assertEqual("SIDE_OPPORTUNITY_SHORT_GE_40_BPS_FIT_ROWS", short_filter)
|
||||
self.assertEqual([False, True, False, True], short_mask.tolist())
|
||||
|
||||
def test_direction_opportunity_labels_choose_clear_path_opportunity(self) -> None:
|
||||
labels = _opportunity_labels(
|
||||
np.array([45.0, 10.0, 45.0, 42.0, np.nan]),
|
||||
@@ -142,6 +164,26 @@ class TrainingContractTest(unittest.TestCase):
|
||||
self.assertEqual("dataset/entry_train.parquet", summary["fit_inner"]["entry"]["source"])
|
||||
self.assertEqual(0.5, summary["fit_inner"]["entry"]["target_rate_by_side"]["LONG"])
|
||||
|
||||
def test_good_trade_structure_builds_side_frame_and_top_metrics(self) -> None:
|
||||
dataset = pd.DataFrame(
|
||||
{
|
||||
"sample_id": ["s1", "s2", "s3"],
|
||||
"split_id": ["fit_inner", "fit_inner", "fit_inner"],
|
||||
"long_actual_plan_net_edge_bps": [4.0, -5.0, 1.0],
|
||||
"short_actual_plan_net_edge_bps": [-5.0, 6.0, -1.0],
|
||||
**{feature: [0.1, 0.2, 0.3] for feature in FEATURE_ORDER},
|
||||
}
|
||||
)
|
||||
|
||||
frame = _side_frame(dataset, "LONG", min_good_edge_bps=3.0, bad_edge_bps=-3.0)
|
||||
metrics = _top_fraction_metrics(frame, np.array([0.9, 0.1, 0.2]), 1 / 3)
|
||||
|
||||
self.assertEqual([1, 0, 0], frame["good_trade"].tolist())
|
||||
self.assertEqual([0, 1, 0], frame["bad_trade"].tolist())
|
||||
self.assertEqual(1, metrics["rows"])
|
||||
self.assertEqual(1.0, metrics["good_rate"])
|
||||
self.assertEqual(4.0, metrics["avg_edge_bps"])
|
||||
|
||||
def test_entry_feature_screen_keeps_zero_inflated_event_features(self) -> None:
|
||||
values = np.concatenate((np.zeros(5000), np.linspace(1.0, 100.0, 500)))
|
||||
edges = _bucket_edges(values)
|
||||
|
||||
Reference in New Issue
Block a user