feat(strategy): add Opening Range + Fair Value Gap (OR-FVG) execution logic

This commit is contained in:
justin committed 2026-02-08 13:23:21 -07:00
1 parent 23c6c5dafd
commit fe28c82379
4 files changed
+519 -113

No files matched your search

Regular → Executable
View File
File mode changed.
+226 -110
View File
@@ -1,149 +1,265 @@
# Alpaca Trader # Alpaca Trader
Automated day trading bot for Alpaca Markets using technical indicators and risk management. Advanced algorithmic trading bot built for Alpaca Markets.
## Features Designed for automated strategy execution, multi-layer technical analysis, and research-driven trading experimentation.
- Multiple technical indicators (SMA/EMA, RSI, ADX, ATR, MACD, Bollinger Bands) Supports configurable strategies, risk management automation, and detailed performance logging.
- Advanced risk management with trailing stops and profit targets
- Market regime detection (trend/range/high vol/low vol)
- Multi-timeframe confluence analysis
- Position sizing based on account equity and risk per trade
- Support for both long and short positions
- Configurable via JSON config file
- Comprehensive debug logging
## Requirements ---
- Python 3.8+ ## 🚀 Quick Start
- Alpaca Markets account (paper or live)
## Installation Clone and install:
```bash ```bash
git clone https://github.com/YOUR_REPO/alpaca-trader.git
cd alpaca-trader
pip install -r requirements.txt pip install -r requirements.txt
``` ```
## Configuration Run:
1. Create `.env` file in `alpaca_trader/` directory:
```
APCA_API_KEY_ID="your_api_key"
APCA_API_SECRET_KEY="your_secret_key"
APCA_API_BASE_URL="https://paper-api.alpaca.markets"
```
2. Modify `config.json` to adjust trading parameters:
- `SYMBOL`: Stock to trade (default: SPY)
- `RISK_PER_TRADE`: Risk per trade as % of equity (default: 0.01)
- `MAX_TRADES_PER_DAY`: Daily trade limit (default: 5)
- `ENABLE_SHORT_SELLING`: Enable/disable short positions (default: true)
- `MIN_SIGNAL_STRENGTH`: Minimum signal strength to enter trades (default: 0.3)
## Usage
```bash ```bash
python3 run.py python3 run.py
``` ```
Or: On first launch, the bot will create:
```bash ```
python3 -m alpaca_trader alpaca_trader/.env
``` ```
## Key Parameters Add your Alpaca API keys:
### General Settings ```
- **DEBUG_MODE**: Enable detailed debug logging (default: true) APCA_API_KEY_ID="your_key"
- **BAR_TIMEFRAME**: Candlestick timeframe for analysis (default: "5Min") APCA_API_SECRET_KEY="your_secret"
- **POLL_INTERVAL**: Seconds between market checks (default: 60) APCA_API_BASE_URL="https://paper-api.alpaca.markets"
- **MIN_NOTIONAL**: Minimum position size in dollars (default: 1.0) ```
- **PDT_RULE**: Enforce pattern day trader rules (default: true)
### Entry/Exit Signals ---
- **SHORT_WINDOW**: Fast moving average period (default: 10)
- **LONG_WINDOW**: Slow moving average period (default: 30)
- **USE_EMA**: Use EMA instead of SMA (default: true)
- **REQUIRE_MA_CROSSOVER**: Require recent MA crossover for signals (default: true)
- **CROSSOVER_LOOKBACK**: Bars to look back for crossovers (default: 5)
- **RSI_BUY_MAX**: Maximum RSI for buy signals in trend (default: 55)
- **RSI_SELL_MIN**: Minimum RSI for sell signals in trend (default: 45)
- **RSI_SELL_MAX**: Maximum RSI for sell signals in trend (default: 70)
- **RSI_RANGE_OVERSOLD**: RSI threshold for range-bound buy (default: 30)
- **RSI_RANGE_OVERBOUGHT**: RSI threshold for range-bound sell (default: 70)
- **ADX_THRESHOLD**: Minimum ADX for trend detection (default: 25)
- **MIN_SIGNAL_STRENGTH**: Minimum signal strength threshold (default: 0.3)
### Risk Management ## 🎯 Features
- **ATR_STOP_MULTIPLIER**: Stop loss distance in ATR units (default: 2.0)
- **USE_TRAILING_STOP**: Enable trailing stop loss (default: true) ### Core Trading Engine
- **PROFIT_TARGET_1**: First profit target in R (default: 2.0)
- **PROFIT_TARGET_2**: Second profit target in R (default: 4.0) - Automated signal evaluation loop
- **MAX_DRAWDOWN**: Maximum account drawdown threshold (default: 0.08) - Multi-strategy architecture
- **MAX_HOLD_TIME**: Maximum position hold time in seconds (default: 3600) - Risk-aware position sizing
- **MIN_RISK_REWARD**: Minimum risk/reward ratio required (default: 2.0) - Market regime detection
- **VOLATILITY_ADJUSTMENT**: Adjust position size based on volatility (default: true) - Config-driven behavior (no code changes required)
### Technical Indicators ### Technical Indicators
- **BB_WINDOW**: Bollinger Bands period (default: 20)
- **BB_STD**: Bollinger Bands standard deviation (default: 2.0) - SMA / EMA
- **REQUIRE_CANDLE_PATTERN**: Require bullish/bearish candle patterns (default: false) - RSI
- **REQUIRE_MACD_CONFIRMATION**: Require MACD crossover confirmation (default: false) - MACD
- ADX
- ATR
- Bollinger Bands
- Multi-timeframe signal confirmation
### Strategy System
Supports multiple strategy modes:
- Moving Average crossover (default)
- Opening Range + Fair Value Gap (OR/FVG)
- Regime-filtered execution
### Risk Management
- ATR-based stop loss
- Trailing stop logic
- Multi-level take profits
- Risk-per-trade sizing
- Max drawdown protection
- Risk/reward validation
- Position hold-time limits
### Execution Controls
- Market or limit orders
- Slippage simulation
- Commission modeling
- Cash account compatibility
- T+1 settlement handling
- PDT rule awareness
### Market Filters
- Market regime classification
- Volume filters
- 200 SMA trend filter
- VIX volatility filter
- Candle confirmation
- MACD confirmation layer
### Analytics & Logging
Automatically generates:
```
logs/
├── trading.log
├── debug.log
data/
├── trades.csv
├── signals.csv
├── performance.csv
├── indicators.csv
├── session.csv
```
---
## 🧠 Strategy Overview
### Moving Average Strategy
Primary signal generated when:
- Short MA crosses long MA
- Trend filters confirm
- Risk/reward meets threshold
- Market regime supports trade
Optional confirmation:
- MACD alignment
- RSI thresholds
- Volume confirmation
---
### Opening Range + Fair Value Gap Strategy
Designed for intraday momentum:
1. Detect opening range window.
2. Identify Fair Value Gap structures.
3. Validate volume and direction.
4. Execute with ATR-based risk controls.
Configurable parameters:
- Opening range duration
- Minimum gap size
- Entry timeframe
- Risk/reward target
- Maximum entry window
---
## ⚙️ Configuration
All trading behavior controlled via:
```
alpaca_trader/config.json
```
Key sections:
### Strategy
```
STRATEGY_MODE
OR_FVG_ENABLED
OR_FVG_OPENING_RANGE_MINUTES
OR_FVG_MIN_GAP_SIZE
```
### Risk
```
RISK_PER_TRADE
ATR_STOP_MULTIPLIER
MAX_DRAWDOWN
MIN_RISK_REWARD
```
### Filters ### Filters
- **REGIME_DETECTION**: Enable market regime detection (default: true)
- **MULTIFRAME_FILTER**: Enable hourly timeframe confirmation (default: false)
- **USE_VIX_FILTER**: Filter trades based on VIX (default: false)
- **VIX_THRESHOLD**: Maximum VIX level to allow trades (default: 30)
- **USE_200_SMA_FILTER**: Filter based on 200-day SMA (default: false)
- **VOLUME_MULTIPLIER**: Minimum volume as multiple of average (default: 0.5)
- **MARKET_HOURS_FILTER**: Only trade during specific hours (default: false)
- **SKIP_MONDAYS_FRIDAYS**: Skip trading on Mondays and Fridays (default: false)
### Order Execution ```
- **USE_LIMIT_ORDERS**: Use limit orders instead of market orders (default: false) REGIME_DETECTION
- **LIMIT_ORDER_TIMEOUT**: Seconds to wait for limit order fill (default: 60) USE_200_SMA_FILTER
- **ENABLE_SLIPPAGE**: Account for slippage in backtesting (default: true) USE_VIX_FILTER
- **SLIPPAGE_PCT**: Estimated slippage percentage (default: 0.0005) MULTIFRAME_FILTER
- **COMMISSION_PCT**: Commission percentage per trade (default: 0.0005) ```
### Backtesting ### Execution
- **BACKTEST_DAYS**: Days of historical data for backtesting (default: 90)
### Advanced Features ```
- **USE_PIVOT_POINTS**: Use pivot point analysis (default: false) USE_LIMIT_ORDERS
- **USE_FIBONACCI**: Use Fibonacci retracement levels (default: false) LIMIT_ORDER_TIMEOUT
- **PULLBACK_PERCENTAGE**: Fibonacci pullback level (default: 0.382) SLIPPAGE_PCT
COMMISSION_PCT
```
## Logging ---
- `trading.log`: Main trading activity log ## 🏗 Architecture
- `debug.log`: Detailed debug information (when DEBUG_MODE is enabled)
## Architecture
``` ```
alpaca_trader/ alpaca_trader/
├── __init__.py # Package initialization ├── api.py # Alpaca API interface
├── __main__.py # Module entry point ├── engine.py # Core trading loop
├── api.py # Alpaca API wrapper with retry logic ├── indicators.py # Technical analysis
├── engine.py # Main trading engine ├── filters.py # Market condition filters
├── indicators.py # Technical indicator calculations ├── risk.py # Risk & position sizing
├── filters.py # Market filters and regime detection ├── utils.py # Helpers
├── risk.py # Risk management data structures ├── cli.py # CLI interface
├── utils.py # Utility functions ├── config.json # Main configuration
├── config.json # Configuration parameters
└── .env # API credentials (create this)
``` ```
## Warning ---
**DISCLAIMER: This software is provided for educational and research purposes only. The author is not responsible for any financial losses, damages, or liabilities incurred from using this trading bot. Use at your own risk.** ## 🔄 How It Works
- Test thoroughly in paper trading before using real capital 1. Load configuration and API credentials
- Past performance does not guarantee future results 2. Fetch historical market data
- Trading involves substantial risk of loss 3. Calculate indicators
- Never trade with money you cannot afford to lose 4. Evaluate market regime
- This is NOT financial advice 5. Generate trading signals
- Consult a licensed financial advisor before making investment decisions 6. Validate risk constraints
7. Execute trades via Alpaca API
8. Log analytics data
---
## 📊 Design Philosophy
- Config-first architecture
- Strategy isolation
- Risk before execution
- Modular extensibility
- Research-friendly logging
---
## ⚠️ Important Notes
- Use paper trading first.
- Algorithmic trading involves financial risk.
- No strategy guarantees profit.
---
## 🛠 Roadmap (Example)
- Strategy plug-in system
- ML signal scoring
- Portfolio-level risk controls
- Multi-symbol scanning
- Performance dashboard
---
## Disclaimer
This software is provided for educational and research purposes only.
Not financial advice.
+293 -3
View File
@@ -110,7 +110,15 @@ DEFAULT_CONFIG = {
"CROSSOVER_LOOKBACK": 3, "CROSSOVER_LOOKBACK": 3,
"REQUIRE_CASH_ACCOUNT": False, "REQUIRE_CASH_ACCOUNT": False,
"T1_SETTLEMENT_ENABLED": False, "T1_SETTLEMENT_ENABLED": False,
"CASH_RESERVE_PCT": 0.0 "CASH_RESERVE_PCT": 0.0,
"STRATEGY_MODE": "ma_crossover",
"OR_FVG_ENABLED": False,
"OR_FVG_OPENING_RANGE_MINUTES": 15,
"OR_FVG_ENTRY_TIMEFRAME": "3Min",
"OR_FVG_MIN_GAP_SIZE": 0.05,
"OR_FVG_RISK_REWARD_RATIO": 2.0,
"OR_FVG_MAX_ENTRY_TIME": "10:30",
"OR_FVG_REQUIRE_VOLUME_CONFIRM": True
} }
if not ENV_PATH.exists(): if not ENV_PATH.exists():
@@ -155,6 +163,15 @@ RISK_PER_TRADE = float(config["RISK_PER_TRADE"])
SHORT_WINDOW = int(config["SHORT_WINDOW"]) SHORT_WINDOW = int(config["SHORT_WINDOW"])
LONG_WINDOW = int(config["LONG_WINDOW"]) LONG_WINDOW = int(config["LONG_WINDOW"])
STRATEGY_MODE = config.get("STRATEGY_MODE", "ma_crossover")
OR_FVG_ENABLED = bool(config.get("OR_FVG_ENABLED", False))
OR_FVG_OPENING_RANGE_MINUTES = int(config.get("OR_FVG_OPENING_RANGE_MINUTES", 15))
OR_FVG_ENTRY_TIMEFRAME = config.get("OR_FVG_ENTRY_TIMEFRAME", "3Min")
OR_FVG_MIN_GAP_SIZE = float(config.get("OR_FVG_MIN_GAP_SIZE", 0.05))
OR_FVG_RISK_REWARD_RATIO = float(config.get("OR_FVG_RISK_REWARD_RATIO", 2.0))
OR_FVG_MAX_ENTRY_TIME = config.get("OR_FVG_MAX_ENTRY_TIME", "10:30")
OR_FVG_REQUIRE_VOLUME_CONFIRM = bool(config.get("OR_FVG_REQUIRE_VOLUME_CONFIRM", True))
if SHORT_WINDOW >= LONG_WINDOW: if SHORT_WINDOW >= LONG_WINDOW:
logger.error(f"⚠️ Configuration error: SHORT_WINDOW ({SHORT_WINDOW}) must be less than LONG_WINDOW ({LONG_WINDOW})") logger.error(f"⚠️ Configuration error: SHORT_WINDOW ({SHORT_WINDOW}) must be less than LONG_WINDOW ({LONG_WINDOW})")
sys.exit(1) sys.exit(1)
@@ -791,6 +808,189 @@ def calculate_position_size(equity, stop_loss, current_price):
if position_value > max_position: if position_value > max_position:
position_value = max_position position_value = max_position
debug_print(f"Position capped at 25% equity: ${position_value:.2f}") debug_print(f"Position capped at 25% equity: ${position_value:.2f}")
class ORFVGState:
def __init__(self):
self.opening_range_high = None
self.opening_range_low = None
self.opening_range_set = False
self.fvg_detected = False
self.fvg_direction = None
self.fvg_candle_index = None
self.entry_triggered = False
def reset(self):
self.opening_range_high = None
self.opening_range_low = None
self.opening_range_set = False
self.fvg_detected = False
self.fvg_direction = None
self.fvg_candle_index = None
self.entry_triggered = False
or_fvg_state = ORFVGState()
def detect_fair_value_gap(bars, min_gap_pct=0.05):
if bars is None or len(bars) < 3:
return None, None
for i in range(len(bars) - 3, max(len(bars) - 10, 0) - 1, -1):
if i < 0 or i + 2 >= len(bars):
continue
candle_1_high = bars['high'].iloc[i]
candle_1_low = bars['low'].iloc[i]
candle_2_high = bars['high'].iloc[i + 1]
candle_2_low = bars['low'].iloc[i + 1]
candle_3_high = bars['high'].iloc[i + 2]
candle_3_low = bars['low'].iloc[i + 2]
bullish_gap = candle_3_low > candle_1_high
if bullish_gap:
gap_size = candle_3_low - candle_1_high
if candle_2_high > 0:
gap_pct = (gap_size / candle_2_high) * 100
if gap_pct >= min_gap_pct:
debug_print(f"Bullish FVG detected: gap={gap_size:.2f} ({gap_pct:.2f}%)")
return "bullish", i + 2
bearish_gap = candle_3_high < candle_1_low
if bearish_gap:
gap_size = candle_1_low - candle_3_high
if candle_2_low > 0:
gap_pct = (gap_size / candle_2_low) * 100
if gap_pct >= min_gap_pct:
debug_print(f"Bearish FVG detected: gap={gap_size:.2f} ({gap_pct:.2f}%)")
return "bearish", i + 2
return None, None
def or_fvg_signal_generator(symbol):
debug_print("Checking OR-FVG strategy")
now = datetime.now(EASTERN)
market_open = now.replace(hour=9, minute=30, second=0, microsecond=0)
opening_range_end = market_open + timedelta(minutes=OR_FVG_OPENING_RANGE_MINUTES)
max_entry_time_parts = OR_FVG_MAX_ENTRY_TIME.split(":")
max_entry_time = now.replace(
hour=int(max_entry_time_parts[0]),
minute=int(max_entry_time_parts[1]),
second=0,
microsecond=0
)
if now > max_entry_time:
debug_print(f"Past max entry time ({OR_FVG_MAX_ENTRY_TIME})")
return None, 0, 0, None
if not or_fvg_state.opening_range_set and now >= opening_range_end:
start_time = market_open
end_time = opening_range_end
bars_or = api.get_bars(
symbol,
"1Min",
start=start_time.isoformat(),
end=end_time.isoformat(),
limit=OR_FVG_OPENING_RANGE_MINUTES
)
if bars_or is not None and len(bars_or) > 0:
or_fvg_state.opening_range_high = bars_or['high'].max()
or_fvg_state.opening_range_low = bars_or['low'].min()
if (pd.isna(or_fvg_state.opening_range_high) or
pd.isna(or_fvg_state.opening_range_low) or
or_fvg_state.opening_range_high <= 0 or
or_fvg_state.opening_range_low <= 0 or
or_fvg_state.opening_range_low >= or_fvg_state.opening_range_high):
logger.error(f"❌ Invalid opening range: High={or_fvg_state.opening_range_high}, Low={or_fvg_state.opening_range_low}")
debug_print("Invalid opening range values detected")
return None, 0, 0, None
or_fvg_state.opening_range_set = True
logger.info(f"📊 Opening Range set: High=${or_fvg_state.opening_range_high:.2f}, Low=${or_fvg_state.opening_range_low:.2f}")
debug_print(f"OR set: H={or_fvg_state.opening_range_high:.2f}, L={or_fvg_state.opening_range_low:.2f}")
if not or_fvg_state.opening_range_set:
debug_print("Opening range not yet set")
return None, 0, 0, None
bars_1min = api.get_bars(symbol, OR_FVG_ENTRY_TIMEFRAME, limit=50)
if bars_1min is None or len(bars_1min) == 0:
debug_print("No 1-min bars available")
return None, 0, 0, None
bars_df = bars_1min.reset_index()
bars_after_or = bars_df[bars_df['timestamp'] >= opening_range_end]
if len(bars_after_or) < 3:
debug_print("Not enough bars after opening range")
return None, 0, 0, None
current_price = bars_after_or['close'].iloc[-1]
if not or_fvg_state.fvg_detected:
fvg_direction, fvg_index = detect_fair_value_gap(bars_after_or, OR_FVG_MIN_GAP_SIZE)
if fvg_direction:
or_fvg_state.fvg_detected = True
or_fvg_state.fvg_direction = fvg_direction
or_fvg_state.fvg_candle_index = fvg_index
logger.info(f"🎯 FVG detected: {fvg_direction.upper()}")
debug_print(f"FVG set: direction={fvg_direction}")
if not or_fvg_state.fvg_detected:
debug_print("No FVG detected yet")
return None, 0, 0, None
if or_fvg_state.entry_triggered:
debug_print("Entry already triggered today")
return None, 0, 0, None
breakout_detected = False
position_type = None
if or_fvg_state.fvg_direction == "bullish":
if current_price > or_fvg_state.opening_range_high:
breakout_detected = True
position_type = "long"
debug_print(f"Bullish breakout: ${current_price:.2f} > ${or_fvg_state.opening_range_high:.2f}")
elif or_fvg_state.fvg_direction == "bearish":
if current_price < or_fvg_state.opening_range_low:
breakout_detected = True
position_type = "short"
debug_print(f"Bearish breakout: ${current_price:.2f} < ${or_fvg_state.opening_range_low:.2f}")
if not breakout_detected:
debug_print("No breakout detected")
return None, 0, 0, None
if OR_FVG_REQUIRE_VOLUME_CONFIRM:
if len(bars_after_or) >= 20:
avg_volume = bars_after_or['volume'].rolling(window=20).mean().iloc[-1]
current_volume = bars_after_or['volume'].iloc[-1]
if current_volume < avg_volume * 1.2:
debug_print(f"Volume confirmation failed: {current_volume:.0f} < {avg_volume*1.2:.0f}")
return None, 0, 0, None
else:
debug_print(f"Volume confirmation skipped: only {len(bars_after_or)} bars available (need 20)")
if position_type == "long":
stop_loss = or_fvg_state.opening_range_low
signal = "buy"
else:
stop_loss = or_fvg_state.opening_range_high
signal = "sell"
strength = 1.0
logger.info(f"✅ OR-FVG Entry: {signal.upper()} @ ${current_price:.2f}, Stop=${stop_loss:.2f}")
debug_print(f"OR-FVG signal generated: {signal}, stop={stop_loss:.2f}")
return signal, strength, stop_loss, position_type
if position_value < MIN_NOTIONAL: if position_value < MIN_NOTIONAL:
position_value = MIN_NOTIONAL position_value = MIN_NOTIONAL
debug_print(f"Position set to minimum: ${position_value:.2f}") debug_print(f"Position set to minimum: ${position_value:.2f}")
@@ -960,6 +1160,23 @@ def scale_out_profit_taking(symbol, entry_price, current_price, stop_loss, posit
risk_pct = abs((entry_price - stop_loss) / entry_price) * 100 risk_pct = abs((entry_price - stop_loss) / entry_price) * 100
if STRATEGY_MODE == "or_fvg" or OR_FVG_ENABLED:
target_pct = risk_pct * OR_FVG_RISK_REWARD_RATIO
if profit_pct >= target_pct:
qty = current_position_qty(symbol)
if qty != 0:
debug_print(f"OR-FVG target hit ({target_pct:.2f}%), closing {qty} shares")
exit_price = None
if position_type == 'long':
exit_price = submit_market_sell(symbol, qty)
else:
exit_price = submit_buy_to_cover(symbol, qty)
logger.info(f"💰 OR-FVG Target @ {profit_pct:.2f}%")
debug_print(f"OR-FVG profit target hit: closed @ {profit_pct:.2f}%")
return True, exit_price if exit_price else current_price
return False, None
target_1_pct = risk_pct * PROFIT_TARGET_1 target_1_pct = risk_pct * PROFIT_TARGET_1
target_2_pct = risk_pct * PROFIT_TARGET_2 target_2_pct = risk_pct * PROFIT_TARGET_2
@@ -1079,6 +1296,7 @@ def main():
signal_state.reset() signal_state.reset()
position_state.reset() position_state.reset()
or_fvg_state.reset()
restored_state = load_session_state() restored_state = load_session_state()
if restored_state: if restored_state:
@@ -1289,7 +1507,72 @@ def main():
time.sleep(POLL_INTERVAL) time.sleep(POLL_INTERVAL)
continue continue
if atr_based_trailing_stop(SYMBOL, entry_price, current_price, stop_loss, position_type): if STRATEGY_MODE == "or_fvg" or OR_FVG_ENABLED:
stop_hit = False
if position_type == 'long' and current_price <= stop_loss:
stop_hit = True
debug_print(f"OR-FVG long stop hit: ${current_price:.2f} <= ${stop_loss:.2f}")
elif position_type == 'short' and current_price >= stop_loss:
stop_hit = True
debug_print(f"OR-FVG short stop hit: ${current_price:.2f} >= ${stop_loss:.2f}")
if stop_hit:
qty = current_position_qty(SYMBOL)
if qty != 0:
exit_time = datetime.now(EASTERN)
hold_minutes = (exit_time - entry_time).total_seconds() / 60 if entry_time else 0
if position_type == 'long':
exit_price = submit_market_sell(SYMBOL, qty)
pnl_dollars = (exit_price - entry_price) * qty if exit_price else 0
else:
exit_price = submit_buy_to_cover(SYMBOL, abs(qty))
pnl_dollars = (entry_price - exit_price) * abs(qty) if exit_price else 0
pnl_percent = (pnl_dollars / (entry_price * abs(qty)) * 100) if entry_price > 0 and qty != 0 else 0
if pnl_dollars > 0:
winners += 1
elif pnl_dollars < 0:
losers += 1
risk_pct = abs((entry_price - stop_loss) / entry_price) if entry_price > 0 else 0
target_1 = entry_price + (entry_price - stop_loss) * PROFIT_TARGET_1 if position_type == 'long' else entry_price - (stop_loss - entry_price) * PROFIT_TARGET_1
target_2 = entry_price + (entry_price - stop_loss) * PROFIT_TARGET_2 if position_type == 'long' else entry_price - (stop_loss - entry_price) * PROFIT_TARGET_2
log_trade(
entry_time,
exit_time,
SYMBOL,
position_type,
entry_price,
exit_price if exit_price else current_price,
abs(qty),
entry_price * abs(qty),
stop_loss,
target_1,
target_2,
pnl_dollars,
pnl_percent,
hold_minutes,
'stop_hit',
entry_regime,
entry_strength,
entry_rsi,
entry_adx,
entry_ma_spread,
0
)
position_active = False
trade_count += 1
logger.info("🛑 Stop hit")
debug_print("Stop hit, position closed")
position_state.reset()
debug_print(f"Sleeping {seconds_to_human_readable(POLL_INTERVAL)} after exit")
time.sleep(POLL_INTERVAL)
continue
elif atr_based_trailing_stop(SYMBOL, entry_price, current_price, stop_loss, position_type):
qty = current_position_qty(SYMBOL) qty = current_position_qty(SYMBOL)
if qty != 0: if qty != 0:
exit_time = datetime.now(EASTERN) exit_time = datetime.now(EASTERN)
@@ -1346,7 +1629,10 @@ def main():
time.sleep(POLL_INTERVAL) time.sleep(POLL_INTERVAL)
continue continue
signal, strength, signal_stop_loss, signal_position_type = advanced_signal_generator(SYMBOL) if STRATEGY_MODE == "or_fvg" or OR_FVG_ENABLED:
signal, strength, signal_stop_loss, signal_position_type = or_fvg_signal_generator(SYMBOL)
else:
signal, strength, signal_stop_loss, signal_position_type = advanced_signal_generator(SYMBOL)
bars_for_signal = get_recent_bars(SYMBOL, 50) bars_for_signal = get_recent_bars(SYMBOL, 50)
signal_rsi = 0 signal_rsi = 0
@@ -1438,6 +1724,10 @@ def main():
logger.info(f" Regime={regime}, Strength={strength:.2f}, Trade {trade_count} ({trades_today}/{MAX_TRADES_PER_DAY})") logger.info(f" Regime={regime}, Strength={strength:.2f}, Trade {trade_count} ({trades_today}/{MAX_TRADES_PER_DAY})")
debug_print(f"Trade executed: entry=${entry_price:.2f}, stop=${stop_loss:.2f}, regime={regime}") debug_print(f"Trade executed: entry=${entry_price:.2f}, stop=${stop_loss:.2f}, regime={regime}")
if STRATEGY_MODE == "or_fvg" or OR_FVG_ENABLED:
or_fvg_state.entry_triggered = True
debug_print("OR-FVG entry_triggered flag set")
position_state.trailing_stop = stop_loss position_state.trailing_stop = stop_loss
debug_print(f"Trailing stop initialized: ${stop_loss:.2f}") debug_print(f"Trailing stop initialized: ${stop_loss:.2f}")
else: else:
Executable → Regular
View File
File mode changed.