Daniel Alnawaz commited on
Commit
d67f432
·
unverified ·
1 Parent(s): 0936622

fixing order issues

Browse files
hbot.py CHANGED
@@ -252,6 +252,15 @@ class HyperliquidAPI:
252
  logger.error(f"Size rounded to zero for {coin} (sz_dec={sz_dec}). Skipping.")
253
  return None
254
 
 
 
 
 
 
 
 
 
 
255
  limit_px = round(px * 1.02, px_dec)
256
  logger.info(f"Placing {'BUY' if is_buy else 'SELL'} limit-IOC {sz} {coin} @ {limit_px} (mark={round(px, px_dec)}) [px_dec={px_dec} sz_dec={sz_dec}]")
257
 
@@ -372,7 +381,7 @@ class HyperliquidAPI:
372
  Guarantees a fill but pays taker fee.
373
  Best for fast strategies (scalp 15m/30m, hype).
374
 
375
- "none" — log the rejection and give up (original behaviour).
376
  """
377
  sz_dec = self.get_sz_decimals(coin)
378
  px_dec = self.get_px_decimals(coin)
@@ -380,6 +389,16 @@ class HyperliquidAPI:
380
  if sz <= 0:
381
  logger.error(f"Size rounded to zero for {coin}. Skipping.")
382
  return None
 
 
 
 
 
 
 
 
 
 
383
  limit_px = round(float(px), px_dec)
384
 
385
  tif = "Alo" if post_only else "Gtc"
 
252
  logger.error(f"Size rounded to zero for {coin} (sz_dec={sz_dec}). Skipping.")
253
  return None
254
 
255
+ # ── Check notional after rounding ───────────────────────────────────
256
+ notional_after_rounding = sz * px
257
+ if notional_after_rounding < 10.0:
258
+ logger.error(
259
+ f"[{coin}] Notional ${notional_after_rounding:.2f} falls below $10 after rounding "
260
+ f"(sz={sz}, px={px:.{px_dec}f}). Skipping."
261
+ )
262
+ return None
263
+
264
  limit_px = round(px * 1.02, px_dec)
265
  logger.info(f"Placing {'BUY' if is_buy else 'SELL'} limit-IOC {sz} {coin} @ {limit_px} (mark={round(px, px_dec)}) [px_dec={px_dec} sz_dec={sz_dec}]")
266
 
 
381
  Guarantees a fill but pays taker fee.
382
  Best for fast strategies (scalp 15m/30m, hype).
383
 
384
+ "none" — log the rejection and give up (original behaviour).
385
  """
386
  sz_dec = self.get_sz_decimals(coin)
387
  px_dec = self.get_px_decimals(coin)
 
389
  if sz <= 0:
390
  logger.error(f"Size rounded to zero for {coin}. Skipping.")
391
  return None
392
+
393
+ # ── Check notional after rounding ───────────────────────────────────
394
+ notional_after_rounding = sz * px
395
+ if notional_after_rounding < 10.0:
396
+ logger.error(
397
+ f"[{coin}] Notional ${notional_after_rounding:.2f} falls below $10 after rounding "
398
+ f"(sz={sz}, px={px:.{px_dec}f}). Skipping."
399
+ )
400
+ return None
401
+
402
  limit_px = round(float(px), px_dec)
403
 
404
  tif = "Alo" if post_only else "Gtc"
strategy_mean_reversion_zscore.py CHANGED
@@ -3,14 +3,15 @@
3
  Strategy: Z-Score Mean Reversion (1h)
4
  =====================================
5
  This strategy identifies extreme price deviations from the mean (20-period SMA)
6
- and trades the reversion back to that mean.
7
 
8
  Core Logic:
9
  - Z-Score: (Close - SMA) / StdDev. Measures how many standard deviations price is from the mean.
10
- - Entry (Long): Z-Score < -2.0 (Oversold extreme).
11
- - Entry (Short): Z-Score > 2.0 (Overbought extreme).
12
  - Exit: Price hits the SMA (Z-Score returns to 0) or Stop Loss is hit.
13
- - Filter: ADX < 25 (Market should NOT be strongly trending).
 
14
  """
15
 
16
  from __future__ import annotations
@@ -59,24 +60,27 @@ class StrategyEngine:
59
  return d.dropna()
60
 
61
  def signal_for_row(self, row: pd.Series) -> int:
62
- z = float(row["zscore"])
63
- adx = float(row["adx"])
64
- close = float(row["close"])
65
- ema200 = float(row["ema200"])
66
-
67
- # Only trade in moderately trending or ranging markets
68
- if adx > 30:
69
- return 0
70
-
71
- # LONG: Z-Score below -1.5 AND price above EMA200 (reversion in uptrend)
72
- if z < -1.5 and close > ema200:
73
- return 1
74
-
75
- # SHORT: Z-Score above 1.5 AND price below EMA200 (reversion in downtrend)
76
- if z > 1.5 and close < ema200:
77
- return -1
78
-
79
- return 0
 
 
 
80
 
81
  def risk_levels(self, entry: float, atr: float, side: int, atr_pct: float = 0.0, row: Optional[pd.Series] = None) -> Tuple[float, float]:
82
  # TP is the mean (SMA), which we can approximate as the current entry minus/plus some distance
 
3
  Strategy: Z-Score Mean Reversion (1h)
4
  =====================================
5
  This strategy identifies extreme price deviations from the mean (20-period SMA)
6
+ and trades the reversion back to that mean, with 200 EMA directional filter.
7
 
8
  Core Logic:
9
  - Z-Score: (Close - SMA) / StdDev. Measures how many standard deviations price is from the mean.
10
+ - Entry (Long): Z-Score < -1.5 (Oversold) AND Price > 200 EMA (reversion in uptrend).
11
+ - Entry (Short): Z-Score > 1.5 (Overbought) AND Price < 200 EMA (reversion in downtrend).
12
  - Exit: Price hits the SMA (Z-Score returns to 0) or Stop Loss is hit.
13
+ - Filter: ADX < 30 (avoid strong trends), 200 EMA for trend direction.
14
+ - Philosophy: Trade extreme reversals only in the direction of the longer-term trend.
15
  """
16
 
17
  from __future__ import annotations
 
60
  return d.dropna()
61
 
62
  def signal_for_row(self, row: pd.Series) -> int:
63
+ z = float(row["zscore"])
64
+ adx = float(row["adx"])
65
+ close = float(row["close"])
66
+ ema200 = float(row["ema200"])
67
+
68
+ # Only trade in moderately trending or ranging markets
69
+ if adx > 30:
70
+ return 0
71
+
72
+ # ── 200 EMA directional filter (only trade extreme reversion in direction of trend) ──
73
+ # LONG only if z-score is BELOW -1.5 AND price is ABOVE 200 EMA (reversion in uptrend)
74
+ # This avoids catching falling knives in downtrends
75
+ if z < -1.5 and close > ema200:
76
+ return 1
77
+
78
+ # SHORT only if z-score is ABOVE 1.5 AND price is BELOW 200 EMA (reversion in downtrend)
79
+ # This avoids catching rallies in uptrends
80
+ if z > 1.5 and close < ema200:
81
+ return -1
82
+
83
+ return 0
84
 
85
  def risk_levels(self, entry: float, atr: float, side: int, atr_pct: float = 0.0, row: Optional[pd.Series] = None) -> Tuple[float, float]:
86
  # TP is the mean (SMA), which we can approximate as the current entry minus/plus some distance
strategy_rsi_extreme.py DELETED
@@ -1,97 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Strategy: RSI Extreme Reversal (1h)
4
- ====================================
5
- Targets "over-extended" markets using multiple RSI timeframes and Bollinger Bands.
6
- Designed to capture sharp reversals after parabolic moves.
7
-
8
- Core Logic:
9
- - RSI(14) and RSI(2) for dual-speed momentum assessment.
10
- - Bollinger Bands (20, 2.5) to identify price extremes.
11
- - Entry (Long): RSI(14) < 30 AND RSI(2) < 10 AND price below Lower BB.
12
- - Entry (Short): RSI(14) > 70 AND RSI(2) > 90 AND price above Upper BB.
13
- - Exit: RSI(14) crosses back to the mean (50) or 2:1 RR hit.
14
- """
15
-
16
- from __future__ import annotations
17
- from dataclasses import dataclass
18
- from typing import Dict, Optional, Tuple
19
- import numpy as np
20
- import pandas as pd
21
- import ta
22
-
23
- @dataclass
24
- class TradeIntent:
25
- coin: str
26
- timeframe: str
27
- side: str
28
- entry_price: float
29
- stop_loss: float
30
- take_profit: float
31
- confidence: float
32
-
33
- class StrategyEngine:
34
- def __init__(self, config: Dict):
35
- self.config = config
36
- self.use_maker_orders = False
37
-
38
- def add_indicators(self, df: pd.DataFrame) -> pd.DataFrame:
39
- d = df.copy()
40
-
41
- # RSI 14 and 2
42
- d["rsi14"] = ta.momentum.RSIIndicator(d["close"], window=14).rsi()
43
- d["rsi2"] = ta.momentum.RSIIndicator(d["close"], window=2).rsi()
44
-
45
- # Bollinger Bands (20, 2.5)
46
- bb = ta.volatility.BollingerBands(d["close"], window=20, window_dev=2.5)
47
- d["bb_high"] = bb.bollinger_hband()
48
- d["bb_low"] = bb.bollinger_lband()
49
-
50
- # ATR for risk
51
- d["atr"] = ta.volatility.AverageTrueRange(d["high"], d["low"], d["close"], window=14).average_true_range()
52
- d["atr_pct"] = d["atr"] / d["close"] * 100
53
-
54
- return d.dropna()
55
-
56
- def signal_for_row(self, row: pd.Series) -> int:
57
- close = float(row["close"])
58
- rsi14 = float(row["rsi14"])
59
- rsi2 = float(row["rsi2"])
60
- bb_high = float(row["bb_high"])
61
- bb_low = float(row["bb_low"])
62
-
63
- # LONG: Deeply oversold on both RSIs + Price below BB Lower
64
- if rsi14 < 30 and rsi2 < 10 and close < bb_low:
65
- return 1
66
-
67
- # SHORT: Deeply overbought on both RSIs + Price above BB Upper
68
- if rsi14 > 70 and rsi2 > 90 and close > bb_high:
69
- return -1
70
-
71
- return 0
72
-
73
- def risk_levels(self, entry: float, atr: float, side: int, atr_pct: float = 0.0, row: Optional[pd.Series] = None) -> Tuple[float, float]:
74
- # Tight 1.5 ATR Stop, 3.5 ATR TP (Aggressive RR for sharp reversals)
75
- if side == 1:
76
- sl = entry - (1.5 * atr)
77
- tp = entry + (3.5 * atr)
78
- else:
79
- sl = entry + (1.5 * atr)
80
- tp = entry - (3.5 * atr)
81
- return sl, tp
82
-
83
- def trail_stop(self, side: int, entry: float, current_sl: float, close: float, atr: float) -> float:
84
- # Trail more tightly once in profit
85
- if side == 1:
86
- return max(current_sl, close - (1.5 * atr))
87
- else:
88
- return min(current_sl, close + (1.5 * atr))
89
-
90
- def build_intent(self, coin: str, timeframe: str, row: pd.Series) -> Optional[TradeIntent]:
91
- signal = self.signal_for_row(row)
92
- if signal == 0:
93
- return None
94
- side = "long" if signal == 1 else "short"
95
- entry = float(row["close"])
96
- stop, tp = self.risk_levels(entry, float(row["atr"]), signal, 0, row)
97
- return TradeIntent(coin, timeframe, side, entry, stop, tp, 0.85)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
strategy_st_pullback.py DELETED
@@ -1,120 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Strategy: SuperTrend Pullback (1h)
4
- ==================================
5
- A trend-following strategy that enters on pullbacks during a confirmed SuperTrend.
6
- Matches the logic style of 'strategy_hype' and 'strategy_conservative'.
7
-
8
- Core Logic:
9
- - SuperTrend (10, 3.0) for trend direction.
10
- - RSI (14) for pullback timing.
11
- - Entry (Long): SuperTrend is UP and RSI < 45 (Pullback in uptrend).
12
- - Entry (Short): SuperTrend is DOWN and RSI > 55 (Bounce in downtrend).
13
- - ATR-based stops and targets (1.5:3.0 RR).
14
- """
15
-
16
- from __future__ import annotations
17
- from dataclasses import dataclass
18
- from typing import Dict, Optional, Tuple
19
- import numpy as np
20
- import pandas as pd
21
- import ta
22
-
23
- @dataclass
24
- class TradeIntent:
25
- coin: str
26
- timeframe: str
27
- side: str
28
- entry_price: float
29
- stop_loss: float
30
- take_profit: float
31
- confidence: float
32
-
33
- class StrategyEngine:
34
- def __init__(self, config: Dict):
35
- self.config = config
36
- self.use_maker_orders = False
37
-
38
- def add_indicators(self, df: pd.DataFrame) -> pd.DataFrame:
39
- d = df.copy()
40
-
41
- # ATR for SuperTrend and Risk
42
- d["atr"] = ta.volatility.AverageTrueRange(d["high"], d["low"], d["close"], window=14).average_true_range()
43
- d["atr_pct"] = d["atr"] / d["close"] * 100
44
-
45
- # RSI 14
46
- d["rsi"] = ta.momentum.RSIIndicator(d["close"], window=14).rsi()
47
-
48
- # SuperTrend (10, 3.0)
49
- multiplier = 3.0
50
- hl2 = (d["high"] + d["low"]) / 2
51
- d["st_upper_basic"] = hl2 + (multiplier * d["atr"])
52
- d["st_lower_basic"] = hl2 - (multiplier * d["atr"])
53
-
54
- upper_band = d["st_upper_basic"].values
55
- lower_band = d["st_lower_basic"].values
56
- close = d["close"].values
57
-
58
- final_upper = np.zeros(len(d))
59
- final_lower = np.zeros(len(d))
60
- direction = np.zeros(len(d))
61
-
62
- for i in range(1, len(d)):
63
- # Upper Band
64
- if upper_band[i] < final_upper[i-1] or close[i-1] > final_upper[i-1]:
65
- final_upper[i] = upper_band[i]
66
- else:
67
- final_upper[i] = final_upper[i-1]
68
- # Lower Band
69
- if lower_band[i] > final_lower[i-1] or close[i-1] < final_lower[i-1]:
70
- final_lower[i] = lower_band[i]
71
- else:
72
- final_lower[i] = final_lower[i-1]
73
- # Direction
74
- if i == 1: direction[i] = 1
75
- elif close[i] > final_upper[i-1]: direction[i] = 1
76
- elif close[i] < final_lower[i-1]: direction[i] = -1
77
- else: direction[i] = direction[i-1]
78
-
79
- d["st_direction"] = direction
80
-
81
- return d.dropna()
82
-
83
- def signal_for_row(self, row: pd.Series) -> int:
84
- st_dir = int(row["st_direction"])
85
- rsi = float(row["rsi"])
86
-
87
- # LONG: SuperTrend UP and RSI pulled back below 45
88
- if st_dir == 1 and rsi < 45:
89
- return 1
90
-
91
- # SHORT: SuperTrend DOWN and RSI bounced above 55
92
- if st_dir == -1 and rsi > 55:
93
- return -1
94
-
95
- return 0
96
-
97
- def risk_levels(self, entry: float, atr: float, side: int, atr_pct: float = 0.0, row: Optional[pd.Series] = None) -> Tuple[float, float]:
98
- # 1.5 ATR SL, 3.5 ATR TP (consistent with your successful strategies)
99
- if side == 1:
100
- sl = entry - (1.5 * atr)
101
- tp = entry + (3.5 * atr)
102
- else:
103
- sl = entry + (1.5 * atr)
104
- tp = entry - (3.5 * atr)
105
- return sl, tp
106
-
107
- def trail_stop(self, side: int, entry: float, current_sl: float, close: float, atr: float) -> float:
108
- if side == 1:
109
- return max(current_sl, close - (2.5 * atr))
110
- else:
111
- return min(current_sl, close + (2.5 * atr))
112
-
113
- def build_intent(self, coin: str, timeframe: str, row: pd.Series) -> Optional[TradeIntent]:
114
- signal = self.signal_for_row(row)
115
- if signal == 0:
116
- return None
117
- side = "long" if signal == 1 else "short"
118
- entry = float(row["close"])
119
- stop, tp = self.risk_levels(entry, float(row["atr"]), signal, 0, row)
120
- return TradeIntent(coin, timeframe, side, entry, stop, tp, 0.8)