hypertrader 0.1.7__tar.gz → 0.1.8__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 (39) hide show
  1. {hypertrader-0.1.7 → hypertrader-0.1.8}/PKG-INFO +36 -1
  2. {hypertrader-0.1.7 → hypertrader-0.1.8}/README.md +35 -0
  3. {hypertrader-0.1.7 → hypertrader-0.1.8}/hypertrader.egg-info/PKG-INFO +36 -1
  4. {hypertrader-0.1.7 → hypertrader-0.1.8}/hypertrader.egg-info/SOURCES.txt +8 -0
  5. {hypertrader-0.1.7 → hypertrader-0.1.8}/hypertrader.py +132 -11
  6. {hypertrader-0.1.7 → hypertrader-0.1.8}/modes/auto_trader.py +359 -117
  7. {hypertrader-0.1.7 → hypertrader-0.1.8}/modes/market_maker.py +6 -3
  8. {hypertrader-0.1.7 → hypertrader-0.1.8}/modes/position_management.py +16 -6
  9. {hypertrader-0.1.7 → hypertrader-0.1.8}/modes/position_watcher.py +7 -7
  10. hypertrader-0.1.8/modes/trailing_stop.py +776 -0
  11. {hypertrader-0.1.7 → hypertrader-0.1.8}/setup.py +1 -1
  12. {hypertrader-0.1.7 → hypertrader-0.1.8}/strategies/__init__.py +2 -1
  13. {hypertrader-0.1.7 → hypertrader-0.1.8}/strategies/base.py +6 -0
  14. {hypertrader-0.1.7 → hypertrader-0.1.8}/strategies/default.py +16 -16
  15. hypertrader-0.1.8/strategies/orderflow_metrics.py +350 -0
  16. hypertrader-0.1.8/strategies/orderflow_models.py +160 -0
  17. hypertrader-0.1.8/strategies/orderflow_pullback.py +808 -0
  18. {hypertrader-0.1.7 → hypertrader-0.1.8}/strategies/registry.py +8 -0
  19. {hypertrader-0.1.7 → hypertrader-0.1.8}/strategies/reversal.py +90 -1
  20. hypertrader-0.1.8/tests/test_auto_strategy_registry.py +243 -0
  21. hypertrader-0.1.8/tests/test_auto_trader_logging.py +130 -0
  22. hypertrader-0.1.8/tests/test_market_ids.py +72 -0
  23. hypertrader-0.1.8/tests/test_orderflow_metrics.py +180 -0
  24. hypertrader-0.1.8/tests/test_orderflow_pullback_lifecycle.py +325 -0
  25. {hypertrader-0.1.7 → hypertrader-0.1.8}/tests/test_reversal_strategy.py +125 -0
  26. hypertrader-0.1.8/tests/test_trailing_stop.py +415 -0
  27. {hypertrader-0.1.7 → hypertrader-0.1.8}/utils/helpers.py +100 -21
  28. hypertrader-0.1.7/modes/trailing_stop.py +0 -277
  29. hypertrader-0.1.7/tests/test_auto_strategy_registry.py +0 -99
  30. {hypertrader-0.1.7 → hypertrader-0.1.8}/hypertrader.egg-info/dependency_links.txt +0 -0
  31. {hypertrader-0.1.7 → hypertrader-0.1.8}/hypertrader.egg-info/entry_points.txt +0 -0
  32. {hypertrader-0.1.7 → hypertrader-0.1.8}/hypertrader.egg-info/requires.txt +0 -0
  33. {hypertrader-0.1.7 → hypertrader-0.1.8}/hypertrader.egg-info/top_level.txt +0 -0
  34. {hypertrader-0.1.7 → hypertrader-0.1.8}/setup.cfg +0 -0
  35. {hypertrader-0.1.7 → hypertrader-0.1.8}/tests/test_async_hyperliquid.py +0 -0
  36. {hypertrader-0.1.7 → hypertrader-0.1.8}/utils/constants.py +0 -0
  37. {hypertrader-0.1.7 → hypertrader-0.1.8}/utils/style.py +0 -0
  38. {hypertrader-0.1.7 → hypertrader-0.1.8}/utils/timer.py +0 -0
  39. {hypertrader-0.1.7 → hypertrader-0.1.8}/utils/worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypertrader
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Async Hyperliquid trading helper built on the async SDK.
5
5
  Author: darkerego
6
6
  License: MIT
@@ -281,6 +281,7 @@ Key options:
281
281
  `auto` now supports selectable strategies:
282
282
 
283
283
  - `default`: the existing MACD/SAR/ADX/Bollinger auto trader and the implicit default when `--strategy` is omitted
284
+ - `orderflow_pullback`: order-flow-confirmed trend pullback scalp strategy, also available as `of_pullback` and `orderflow`
284
285
  - `reversal`: a closed-candle trend-exhaustion, structure-break, and retest strategy
285
286
 
286
287
  The strategy selection changes the signal engine only. Shared sizing, risk controls, modify-order entry, market-fallback cleanup, hidden-order behavior, and bracket management still run through the same common auto execution path.
@@ -301,6 +302,7 @@ python3 hypertrader.py auto [COIN] (--size SIZE | --size-pct SIZE_PCT) [options]
301
302
  Key options:
302
303
 
303
304
  - `--strategy {default,reversal}`
305
+ - `--strategy {default,orderflow_pullback,reversal}` with aliases `of_pullback` and `orderflow`
304
306
  - `--size` or `--size-pct`
305
307
  - `--top-markets`
306
308
  - `--intervals`
@@ -399,6 +401,39 @@ python3 hypertrader.py auto --strategy reversal \
399
401
  --reversal-exit-on-sar-flip
400
402
  ```
401
403
 
404
+ Order-flow pullback strategy notes:
405
+
406
+ - Uses closed `1m`/`5m` candles for regime context and live `l2Book` plus `trades` websocket feeds for entry confirmation.
407
+ - Indicators classify context only; executed trade flow, weighted book imbalance, and microprice authorize entries.
408
+ - Entries still route through the shared modify-order post-only entry path.
409
+ - Hidden-order mode still applies: `--hide-orders` keeps TP/SL targets local instead of placing exchange-visible brackets.
410
+
411
+ Important order-flow options:
412
+
413
+ - `--of-max-active-books`: maximum promoted markets with live book/trade monitoring
414
+ - `--of-max-spread-bps` and `--of-max-spread-ratio`: live spread gates
415
+ - `--of-min-depth-ratio`: executable depth gate versus intended order notional
416
+ - `--of-warmup-seconds`: warm-up period after promotion before entries are allowed
417
+ - `--of-min-edge-cost-multiple`: expected-move versus estimated round-trip-cost gate
418
+ - `--of-pullback-min` and `--of-pullback-max`: retracement zone bounds
419
+ - `--of-book-imbalance-min`, `--of-micro-bias-min`, `--of-trade-imbalance-2s-min`, and `--of-trade-imbalance-10s-min`: mandatory confirmation thresholds
420
+ - `--of-entry-timeout-seconds` and `--of-max-chase-ticks`: entry aggressiveness controls layered onto the shared entry helper
421
+
422
+ Example order-flow scan:
423
+
424
+ ```bash
425
+ python3 hypertrader.py auto \
426
+ --strategy orderflow_pullback \
427
+ --top-markets 30 \
428
+ --size-pct 5 \
429
+ --scan-interval 1 \
430
+ --of-max-active-books 8 \
431
+ --of-max-spread-bps 3 \
432
+ --of-min-depth-ratio 10 \
433
+ --of-entry-timeout-seconds 2 \
434
+ --of-max-chase-ticks 2
435
+ ```
436
+
402
437
  Example top-market reversal scan:
403
438
 
404
439
  ```bash
@@ -250,6 +250,7 @@ Key options:
250
250
  `auto` now supports selectable strategies:
251
251
 
252
252
  - `default`: the existing MACD/SAR/ADX/Bollinger auto trader and the implicit default when `--strategy` is omitted
253
+ - `orderflow_pullback`: order-flow-confirmed trend pullback scalp strategy, also available as `of_pullback` and `orderflow`
253
254
  - `reversal`: a closed-candle trend-exhaustion, structure-break, and retest strategy
254
255
 
255
256
  The strategy selection changes the signal engine only. Shared sizing, risk controls, modify-order entry, market-fallback cleanup, hidden-order behavior, and bracket management still run through the same common auto execution path.
@@ -270,6 +271,7 @@ python3 hypertrader.py auto [COIN] (--size SIZE | --size-pct SIZE_PCT) [options]
270
271
  Key options:
271
272
 
272
273
  - `--strategy {default,reversal}`
274
+ - `--strategy {default,orderflow_pullback,reversal}` with aliases `of_pullback` and `orderflow`
273
275
  - `--size` or `--size-pct`
274
276
  - `--top-markets`
275
277
  - `--intervals`
@@ -368,6 +370,39 @@ python3 hypertrader.py auto --strategy reversal \
368
370
  --reversal-exit-on-sar-flip
369
371
  ```
370
372
 
373
+ Order-flow pullback strategy notes:
374
+
375
+ - Uses closed `1m`/`5m` candles for regime context and live `l2Book` plus `trades` websocket feeds for entry confirmation.
376
+ - Indicators classify context only; executed trade flow, weighted book imbalance, and microprice authorize entries.
377
+ - Entries still route through the shared modify-order post-only entry path.
378
+ - Hidden-order mode still applies: `--hide-orders` keeps TP/SL targets local instead of placing exchange-visible brackets.
379
+
380
+ Important order-flow options:
381
+
382
+ - `--of-max-active-books`: maximum promoted markets with live book/trade monitoring
383
+ - `--of-max-spread-bps` and `--of-max-spread-ratio`: live spread gates
384
+ - `--of-min-depth-ratio`: executable depth gate versus intended order notional
385
+ - `--of-warmup-seconds`: warm-up period after promotion before entries are allowed
386
+ - `--of-min-edge-cost-multiple`: expected-move versus estimated round-trip-cost gate
387
+ - `--of-pullback-min` and `--of-pullback-max`: retracement zone bounds
388
+ - `--of-book-imbalance-min`, `--of-micro-bias-min`, `--of-trade-imbalance-2s-min`, and `--of-trade-imbalance-10s-min`: mandatory confirmation thresholds
389
+ - `--of-entry-timeout-seconds` and `--of-max-chase-ticks`: entry aggressiveness controls layered onto the shared entry helper
390
+
391
+ Example order-flow scan:
392
+
393
+ ```bash
394
+ python3 hypertrader.py auto \
395
+ --strategy orderflow_pullback \
396
+ --top-markets 30 \
397
+ --size-pct 5 \
398
+ --scan-interval 1 \
399
+ --of-max-active-books 8 \
400
+ --of-max-spread-bps 3 \
401
+ --of-min-depth-ratio 10 \
402
+ --of-entry-timeout-seconds 2 \
403
+ --of-max-chase-ticks 2
404
+ ```
405
+
371
406
  Example top-market reversal scan:
372
407
 
373
408
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypertrader
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Async Hyperliquid trading helper built on the async SDK.
5
5
  Author: darkerego
6
6
  License: MIT
@@ -281,6 +281,7 @@ Key options:
281
281
  `auto` now supports selectable strategies:
282
282
 
283
283
  - `default`: the existing MACD/SAR/ADX/Bollinger auto trader and the implicit default when `--strategy` is omitted
284
+ - `orderflow_pullback`: order-flow-confirmed trend pullback scalp strategy, also available as `of_pullback` and `orderflow`
284
285
  - `reversal`: a closed-candle trend-exhaustion, structure-break, and retest strategy
285
286
 
286
287
  The strategy selection changes the signal engine only. Shared sizing, risk controls, modify-order entry, market-fallback cleanup, hidden-order behavior, and bracket management still run through the same common auto execution path.
@@ -301,6 +302,7 @@ python3 hypertrader.py auto [COIN] (--size SIZE | --size-pct SIZE_PCT) [options]
301
302
  Key options:
302
303
 
303
304
  - `--strategy {default,reversal}`
305
+ - `--strategy {default,orderflow_pullback,reversal}` with aliases `of_pullback` and `orderflow`
304
306
  - `--size` or `--size-pct`
305
307
  - `--top-markets`
306
308
  - `--intervals`
@@ -399,6 +401,39 @@ python3 hypertrader.py auto --strategy reversal \
399
401
  --reversal-exit-on-sar-flip
400
402
  ```
401
403
 
404
+ Order-flow pullback strategy notes:
405
+
406
+ - Uses closed `1m`/`5m` candles for regime context and live `l2Book` plus `trades` websocket feeds for entry confirmation.
407
+ - Indicators classify context only; executed trade flow, weighted book imbalance, and microprice authorize entries.
408
+ - Entries still route through the shared modify-order post-only entry path.
409
+ - Hidden-order mode still applies: `--hide-orders` keeps TP/SL targets local instead of placing exchange-visible brackets.
410
+
411
+ Important order-flow options:
412
+
413
+ - `--of-max-active-books`: maximum promoted markets with live book/trade monitoring
414
+ - `--of-max-spread-bps` and `--of-max-spread-ratio`: live spread gates
415
+ - `--of-min-depth-ratio`: executable depth gate versus intended order notional
416
+ - `--of-warmup-seconds`: warm-up period after promotion before entries are allowed
417
+ - `--of-min-edge-cost-multiple`: expected-move versus estimated round-trip-cost gate
418
+ - `--of-pullback-min` and `--of-pullback-max`: retracement zone bounds
419
+ - `--of-book-imbalance-min`, `--of-micro-bias-min`, `--of-trade-imbalance-2s-min`, and `--of-trade-imbalance-10s-min`: mandatory confirmation thresholds
420
+ - `--of-entry-timeout-seconds` and `--of-max-chase-ticks`: entry aggressiveness controls layered onto the shared entry helper
421
+
422
+ Example order-flow scan:
423
+
424
+ ```bash
425
+ python3 hypertrader.py auto \
426
+ --strategy orderflow_pullback \
427
+ --top-markets 30 \
428
+ --size-pct 5 \
429
+ --scan-interval 1 \
430
+ --of-max-active-books 8 \
431
+ --of-max-spread-bps 3 \
432
+ --of-min-depth-ratio 10 \
433
+ --of-entry-timeout-seconds 2 \
434
+ --of-max-chase-ticks 2
435
+ ```
436
+
402
437
  Example top-market reversal scan:
403
438
 
404
439
  ```bash
@@ -15,11 +15,19 @@ modes/trailing_stop.py
15
15
  strategies/__init__.py
16
16
  strategies/base.py
17
17
  strategies/default.py
18
+ strategies/orderflow_metrics.py
19
+ strategies/orderflow_models.py
20
+ strategies/orderflow_pullback.py
18
21
  strategies/registry.py
19
22
  strategies/reversal.py
20
23
  tests/test_async_hyperliquid.py
21
24
  tests/test_auto_strategy_registry.py
25
+ tests/test_auto_trader_logging.py
26
+ tests/test_market_ids.py
27
+ tests/test_orderflow_metrics.py
28
+ tests/test_orderflow_pullback_lifecycle.py
22
29
  tests/test_reversal_strategy.py
30
+ tests/test_trailing_stop.py
23
31
  utils/constants.py
24
32
  utils/helpers.py
25
33
  utils/style.py
@@ -23,7 +23,7 @@ import os
23
23
  import sys
24
24
  from typing import Any, Dict, List, Optional
25
25
 
26
- from strategies.registry import available_strategies
26
+ from strategies.registry import available_strategy_names, available_strategies, normalize_strategy_name
27
27
 
28
28
  from hyperliquid.info import Info
29
29
 
@@ -32,7 +32,7 @@ from modes.position_management import run_bracket_entry
32
32
  from modes.position_watcher import run_position_watcher
33
33
  from modes.trailing_stop import trailing_stop_for_all_positions
34
34
  from utils.constants import DEFAULT_LOG_FILE, INTERVAL_TO_MS
35
- from utils.helpers import parse_fractional_pct
35
+ from utils.helpers import parse_fractional_pct, normalize_hyperliquid_market_id, split_hyperliquid_market_id
36
36
  from utils.style import install_pretty_stdout
37
37
 
38
38
  try:
@@ -107,9 +107,15 @@ async def _compat_name_to_asset(self: Info, name: str) -> int:
107
107
 
108
108
  name_to_coin = getattr(self, "name_to_coin", {})
109
109
  coin_to_asset = getattr(self, "coin_to_asset", {})
110
- coin = name_to_coin.get(name, name)
110
+ normalized_name = normalize_hyperliquid_market_id(name)
111
+ _, base_symbol = split_hyperliquid_market_id(normalized_name)
112
+ coin = name_to_coin.get(normalized_name, normalized_name)
111
113
  if coin not in coin_to_asset:
112
- raise KeyError(f"No Hyperliquid asset id found for {name!r}; known names include {list(name_to_coin)[:20]}")
114
+ coin = name_to_coin.get(base_symbol, base_symbol)
115
+ if coin not in coin_to_asset:
116
+ raise KeyError(
117
+ f"No Hyperliquid asset id found for {name!r}; known names include {list(name_to_coin)[:20]}"
118
+ )
113
119
  return int(coin_to_asset[coin])
114
120
 
115
121
 
@@ -150,7 +156,7 @@ def build_arg_parser() -> argparse.ArgumentParser:
150
156
  trailing_parser = subparsers.add_parser("trailing", help="Trailing stop manager for open perp positions.")
151
157
  trailing_parser.add_argument("coin", type=str, nargs="?", default=None, help="Optional coin symbol, e.g. BTC, ETH.")
152
158
  trailing_parser.add_argument("--trail-pct", type=float, default=0.01,
153
- help="Trailing distance fraction, e.g. 0.01 = 1pct.")
159
+ help="Fraction of favorable unrealized profit to give back before exit, e.g. 0.33 keeps 67pct.")
154
160
  trailing_parser.add_argument("--poll-interval", type=float, default=2.0,
155
161
  help="Polling interval in seconds. Default: 2.0.")
156
162
  trailing_parser.add_argument("--testnet", action="store_true", help="Use Hyperliquid testnet instead of mainnet.")
@@ -258,7 +264,7 @@ def build_arg_parser() -> argparse.ArgumentParser:
258
264
  help="Percent of available collateral to use for sizing. Accepts 10, 10%%, or 0.10.")
259
265
  auto_parser.add_argument("--top-markets", type=int, default=10,
260
266
  help="When auto coin is omitted, scan the top N perp markets by day notional volume. Default: 10.")
261
- auto_parser.add_argument("--strategy", type=str, default="default", choices=available_strategies(),
267
+ auto_parser.add_argument("--strategy", type=str, default="default", choices=available_strategy_names(),
262
268
  help="Auto strategy selection. Default: default.")
263
269
  auto_parser.add_argument("--intervals", type=str, default="1h,15m,5m,1m",
264
270
  help="Comma/space separated intervals. Default: 1h,15m,5m,1m.")
@@ -407,6 +413,51 @@ def build_arg_parser() -> argparse.ArgumentParser:
407
413
  reversal_exit_group.add_argument("--reversal-exit-on-sar-flip", dest="reversal_exit_on_sar_flip", action="store_true")
408
414
  reversal_exit_group.add_argument("--no-reversal-exit-on-sar-flip", dest="reversal_exit_on_sar_flip", action="store_false")
409
415
  auto_parser.set_defaults(reversal_exit_on_sar_flip=True)
416
+ orderflow_group = auto_parser.add_argument_group("Order-flow pullback strategy")
417
+ orderflow_group.add_argument("--of-max-active-books", type=int, default=8)
418
+ orderflow_group.add_argument("--of-max-spread-bps", type=float, default=3.0)
419
+ orderflow_group.add_argument("--of-max-spread-ratio", type=float, default=1.5)
420
+ orderflow_group.add_argument("--of-depth-bps", type=float, default=5.0)
421
+ orderflow_group.add_argument("--of-min-depth-ratio", type=float, default=10.0)
422
+ orderflow_group.add_argument("--of-max-data-age-seconds", type=float, default=1.5)
423
+ orderflow_group.add_argument("--of-warmup-seconds", type=float, default=60.0)
424
+ orderflow_group.add_argument("--of-min-edge-cost-multiple", type=float, default=2.5)
425
+ orderflow_group.add_argument("--of-trend-efficiency-min", type=float, default=0.30)
426
+ orderflow_group.add_argument("--of-adx-min", type=float, default=18.0)
427
+ orderflow_group.add_argument("--of-impulse-atr-multiple", type=float, default=0.35)
428
+ orderflow_group.add_argument("--of-impulse-spread-multiple", type=float, default=4.0)
429
+ orderflow_group.add_argument("--of-impulse-flow-min", type=float, default=0.20)
430
+ orderflow_group.add_argument("--of-impulse-volume-ratio-min", type=float, default=1.40)
431
+ orderflow_group.add_argument("--of-impulse-expiry-seconds", type=float, default=120.0)
432
+ orderflow_group.add_argument("--of-pullback-min", type=float, default=0.20)
433
+ orderflow_group.add_argument("--of-pullback-max", type=float, default=0.60)
434
+ orderflow_group.add_argument("--of-pullback-min-seconds", type=float, default=5.0)
435
+ orderflow_group.add_argument("--of-pullback-timeout-seconds", type=float, default=90.0)
436
+ orderflow_group.add_argument("--of-book-imbalance-min", type=float, default=0.15)
437
+ orderflow_group.add_argument("--of-micro-bias-min", type=float, default=0.10)
438
+ orderflow_group.add_argument("--of-trade-imbalance-2s-min", type=float, default=0.20)
439
+ orderflow_group.add_argument("--of-trade-imbalance-10s-min", type=float, default=0.08)
440
+ orderflow_group.add_argument("--of-confirmations-required", type=int, default=2)
441
+ orderflow_group.add_argument("--of-confirmation-window", type=int, default=3)
442
+ orderflow_group.add_argument("--of-min-score", type=float, default=0.70)
443
+ orderflow_group.add_argument("--of-min-flow-score", type=float, default=0.60)
444
+ orderflow_group.add_argument("--of-min-book-score", type=float, default=0.55)
445
+ orderflow_group.add_argument("--of-entry-timeout-seconds", type=float, default=2.0)
446
+ orderflow_group.add_argument("--of-max-chase-ticks", type=int, default=2)
447
+ orderflow_group.add_argument("--of-allow-market-fallback", action="store_true")
448
+ orderflow_group.add_argument("--of-stop-atr-fraction", type=float, default=0.10)
449
+ orderflow_group.add_argument("--of-tp1-r", type=float, default=1.0)
450
+ orderflow_group.add_argument("--of-tp1-size-pct", type=float, default=50.0)
451
+ orderflow_group.add_argument("--of-tp2-r", type=float, default=1.6)
452
+ orderflow_group.add_argument("--of-continuation-timeout-seconds", type=float, default=45.0)
453
+ orderflow_group.add_argument("--of-continuation-min-progress-r", type=float, default=0.35)
454
+ orderflow_group.add_argument("--of-max-hold-seconds", type=float, default=180.0)
455
+ of_flow_scratch_group = orderflow_group.add_mutually_exclusive_group()
456
+ of_flow_scratch_group.add_argument("--of-flow-scratch", dest="of_flow_scratch", action="store_true")
457
+ of_flow_scratch_group.add_argument("--no-of-flow-scratch", dest="of_flow_scratch", action="store_false")
458
+ orderflow_group.add_argument("--of-flow-scratch-grace-seconds", type=float, default=1.0)
459
+ orderflow_group.add_argument("--of-log-evaluations", action="store_true")
460
+ auto_parser.set_defaults(of_flow_scratch=True)
410
461
 
411
462
  mm_parser = subparsers.add_parser(
412
463
  "market_maker",
@@ -448,7 +499,7 @@ async def async_main(argv: Optional[List[str]] = None) -> None:
448
499
  trail_pct=args.trail_pct,
449
500
  poll_interval=args.poll_interval,
450
501
  use_testnet=args.testnet,
451
- only_coin=(args.coin.upper() if args.coin is not None else None),
502
+ only_coin=(normalize_hyperliquid_market_id(args.coin) if args.coin is not None else None),
452
503
  use_websocket=(not args.no_websocket),
453
504
  hide_orders=args.hide_orders,
454
505
  )
@@ -490,7 +541,7 @@ async def async_main(argv: Optional[List[str]] = None) -> None:
490
541
  sys.exit(1)
491
542
 
492
543
  await run_bracket_entry(
493
- coin=args.coin.upper(),
544
+ coin=normalize_hyperliquid_market_id(args.coin),
494
545
  direction=args.direction,
495
546
  size=args.size,
496
547
  take_profit_pct=args.take_profit_pct,
@@ -544,7 +595,7 @@ async def async_main(argv: Optional[List[str]] = None) -> None:
544
595
  sys.exit(1)
545
596
 
546
597
  await run_position_watcher(
547
- only_coin=(args.coin.upper() if args.coin is not None else None),
598
+ only_coin=(normalize_hyperliquid_market_id(args.coin) if args.coin is not None else None),
548
599
  take_profit_pct=args.take_profit_pct,
549
600
  stop_loss_pct=args.stop_loss_pct,
550
601
  take_profit_levels=args.take_profit_levels,
@@ -567,6 +618,7 @@ async def async_main(argv: Optional[List[str]] = None) -> None:
567
618
 
568
619
  if args.command == "auto":
569
620
  from modes.auto_trader import run_auto_trader
621
+ args.strategy = normalize_strategy_name(args.strategy)
570
622
 
571
623
  if args.size is not None and args.size <= 0.0:
572
624
  print("[ERROR] --size must be > 0.")
@@ -714,9 +766,37 @@ async def async_main(argv: Optional[List[str]] = None) -> None:
714
766
  if abs((args.reversal_tp1_pct + args.reversal_tp2_pct + args.reversal_runner_pct) - 100.0) > 1e-9:
715
767
  print("[ERROR] Reversal TP percentages must total 100.")
716
768
  sys.exit(1)
769
+ if args.strategy == "orderflow_pullback":
770
+ if not (0.0 < args.of_pullback_min < args.of_pullback_max < 1.0):
771
+ print("[ERROR] Require 0 < --of-pullback-min < --of-pullback-max < 1.")
772
+ sys.exit(1)
773
+ if not (args.of_confirmation_window >= args.of_confirmations_required >= 1):
774
+ print("[ERROR] Require --of-confirmation-window >= --of-confirmations-required >= 1.")
775
+ sys.exit(1)
776
+ if args.of_max_active_books < 1:
777
+ print("[ERROR] --of-max-active-books must be >= 1.")
778
+ sys.exit(1)
779
+ if args.of_max_chase_ticks < 0:
780
+ print("[ERROR] --of-max-chase-ticks must be >= 0.")
781
+ sys.exit(1)
782
+ if args.of_max_data_age_seconds <= 0.0:
783
+ print("[ERROR] --of-max-data-age-seconds must be > 0.")
784
+ sys.exit(1)
785
+ if args.of_entry_timeout_seconds <= 0.0:
786
+ print("[ERROR] --of-entry-timeout-seconds must be > 0.")
787
+ sys.exit(1)
788
+ if args.of_min_edge_cost_multiple <= 1.0:
789
+ print("[ERROR] --of-min-edge-cost-multiple must be > 1.")
790
+ sys.exit(1)
791
+ if not (0.0 < args.of_tp1_size_pct <= 100.0):
792
+ print("[ERROR] --of-tp1-size-pct must be > 0 and <= 100.")
793
+ sys.exit(1)
794
+ if min(args.of_tp1_r, args.of_tp2_r) <= 0.0:
795
+ print("[ERROR] --of-tp1-r and --of-tp2-r must be > 0.")
796
+ sys.exit(1)
717
797
 
718
798
  await run_auto_trader(
719
- coin=(args.coin.upper() if args.coin is not None else None),
799
+ coin=(normalize_hyperliquid_market_id(args.coin) if args.coin is not None else None),
720
800
  strategy_name=args.strategy,
721
801
  size=args.size,
722
802
  size_pct=args.size_pct,
@@ -811,6 +891,47 @@ async def async_main(argv: Optional[List[str]] = None) -> None:
811
891
  reversal_runner_pct=args.reversal_runner_pct,
812
892
  reversal_exit_on_sar_flip=args.reversal_exit_on_sar_flip,
813
893
  reversal_min_ema50_slope=args.reversal_min_ema50_slope,
894
+ of_max_active_books=args.of_max_active_books,
895
+ of_max_spread_bps=args.of_max_spread_bps,
896
+ of_max_spread_ratio=args.of_max_spread_ratio,
897
+ of_depth_bps=args.of_depth_bps,
898
+ of_min_depth_ratio=args.of_min_depth_ratio,
899
+ of_max_data_age_seconds=args.of_max_data_age_seconds,
900
+ of_warmup_seconds=args.of_warmup_seconds,
901
+ of_min_edge_cost_multiple=args.of_min_edge_cost_multiple,
902
+ of_trend_efficiency_min=args.of_trend_efficiency_min,
903
+ of_adx_min=args.of_adx_min,
904
+ of_impulse_atr_multiple=args.of_impulse_atr_multiple,
905
+ of_impulse_spread_multiple=args.of_impulse_spread_multiple,
906
+ of_impulse_flow_min=args.of_impulse_flow_min,
907
+ of_impulse_volume_ratio_min=args.of_impulse_volume_ratio_min,
908
+ of_impulse_expiry_seconds=args.of_impulse_expiry_seconds,
909
+ of_pullback_min=args.of_pullback_min,
910
+ of_pullback_max=args.of_pullback_max,
911
+ of_pullback_min_seconds=args.of_pullback_min_seconds,
912
+ of_pullback_timeout_seconds=args.of_pullback_timeout_seconds,
913
+ of_book_imbalance_min=args.of_book_imbalance_min,
914
+ of_micro_bias_min=args.of_micro_bias_min,
915
+ of_trade_imbalance_2s_min=args.of_trade_imbalance_2s_min,
916
+ of_trade_imbalance_10s_min=args.of_trade_imbalance_10s_min,
917
+ of_confirmations_required=args.of_confirmations_required,
918
+ of_confirmation_window=args.of_confirmation_window,
919
+ of_min_score=args.of_min_score,
920
+ of_min_flow_score=args.of_min_flow_score,
921
+ of_min_book_score=args.of_min_book_score,
922
+ of_entry_timeout_seconds=args.of_entry_timeout_seconds,
923
+ of_max_chase_ticks=args.of_max_chase_ticks,
924
+ of_allow_market_fallback=args.of_allow_market_fallback,
925
+ of_stop_atr_fraction=args.of_stop_atr_fraction,
926
+ of_tp1_r=args.of_tp1_r,
927
+ of_tp1_size_pct=args.of_tp1_size_pct,
928
+ of_tp2_r=args.of_tp2_r,
929
+ of_continuation_timeout_seconds=args.of_continuation_timeout_seconds,
930
+ of_continuation_min_progress_r=args.of_continuation_min_progress_r,
931
+ of_max_hold_seconds=args.of_max_hold_seconds,
932
+ of_flow_scratch=args.of_flow_scratch,
933
+ of_flow_scratch_grace_seconds=args.of_flow_scratch_grace_seconds,
934
+ of_log_evaluations=args.of_log_evaluations,
814
935
  )
815
936
  return
816
937
 
@@ -831,7 +952,7 @@ async def async_main(argv: Optional[List[str]] = None) -> None:
831
952
  print("[ERROR] --rebalance-threshold-pct must be > 0.")
832
953
  sys.exit(1)
833
954
  await run_market_maker(
834
- coin=args.coin.upper(),
955
+ coin=normalize_hyperliquid_market_id(args.coin),
835
956
  interval=args.interval,
836
957
  periods=args.periods,
837
958
  levels=args.levels,