Overall Statistics
Total Orders
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Start Equity
100000
End Equity
100000
Net Profit
0%
Sharpe Ratio
0
Sortino Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-0.729
Tracking Error
0.142
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
Drawdown Recovery
0
# region imports
from AlgorithmImports import *
import time
# endregion

class TradingSignal:
    """Trading signal states"""
    NO_SIGNAL = 0
    BUY = 1
    SELL = 2

class DualMovingAverageStrategy(QCAlgorithm):
    """
    Dual Moving Average Strategy with Volatility Filters
    
    Strategy Logic:
    - Uses fast (20-period) and slow (50-period) moving averages
    - Entry: Fast MA crosses above Slow MA + Volatility within range
    - Exit: Fast MA crosses below Slow MA OR stop-loss/take-profit hit
    - Risk Management: Fixed position sizing with stop-loss at 2% below entry
    """
    
    def initialize(self):
        """Initialize algorithm parameters and indicators"""
        self.set_start_date(2013, 1, 1)
        self.set_end_date(2024, 1, 1)
        self.set_cash(100000)
        # Strategy parameters
        self._symbol = "SPY"
        self._fast_period = 20
        self._slow_period = 50
        self._volatility_period = 20
        self._min_volatility = 0.01  # 1% minimum ATR
        self._max_volatility = 0.03  # 3% maximum ATR
        self._position_size = 0.95   # Use 95% of portfolio
        self._stop_loss_percent = 0.02  # 2% stop loss
        self._take_profit_percent = 0.05  # 5% take profit
        
        # Add equity with minute resolution for detailed tracking
        self.add_equity(self._symbol, Resolution.MINUTE)
        
        # Initialize indicators
        self._fast_ma = self.sma(self._symbol, self._fast_period, Resolution.DAILY)
        self._slow_ma = self.sma(self._symbol, self._slow_period, Resolution.DAILY)
        self._atr = self.atr(self._symbol, self._volatility_period)
        
        # Track entry price and current signal
        self._entry_price = 0
        self._current_signal = TradingSignal.NO_SIGNAL
        self._previous_signal = TradingSignal.NO_SIGNAL
        
        # Scheduling
        self.schedule.on(self.date_rules.every_day(self._symbol), 
                        self.time_rules.after_market_open(self._symbol, 5),
                        self.check_trading_signals)
        
    def check_trading_signals(self):
        """Check for trading signals every day after market open"""
        if not self._fast_ma.is_ready or not self._slow_ma.is_ready or not self._atr.is_ready:
            return
        
        # Get current values
        current_price = self.securities[self._symbol].price
        fast_ma = self._fast_ma.current.value
        slow_ma = self._slow_ma.current.value
        atr_value = self._atr.current.value
        volatility_pct = atr_value / current_price if current_price > 0 else 0
        
        # Determine signal
        self._current_signal = self._get_signal(fast_ma, slow_ma, volatility_pct)
        
        # Log signal information
        signal_name = self._get_signal_name(self._current_signal)
        self.debug(f"Price: {current_price:.2f} | Fast MA: {fast_ma:.2f} | Slow MA: {slow_ma:.2f} | " +
                  f"Volatility: {volatility_pct:.4f} | Signal: {signal_name}")
        
        # Execute trading logic
        self._execute_trades(current_price)
        
        # Update previous signal
        self._previous_signal = self._current_signal
    
    def _get_signal_name(self, signal: int) -> str:
        """Get human-readable signal name"""
        if signal == TradingSignal.BUY:
            return "BUY"
        elif signal == TradingSignal.SELL:
            return "SELL"
        else:
            return "NO_SIGNAL"
    
    def _get_signal(self, fast_ma: float, slow_ma: float, volatility_pct: float) -> int:
        """
        Determine trading signal based on moving average crossover and volatility
        
        Args:
            fast_ma: Fast moving average value
            slow_ma: Slow moving average value
            volatility_pct: ATR as percentage of price
            
        Returns:
            TradingSignal value
        """
        # Check volatility filter
        volatility_acceptable = self._min_volatility <= volatility_pct <= self._max_volatility
        
        if not volatility_acceptable:
            return TradingSignal.NO_SIGNAL
        
        # Check for crossover signals
        if fast_ma > slow_ma:
            return TradingSignal.BUY
        elif fast_ma < slow_ma:
            return TradingSignal.SELL
        else:
            return TradingSignal.NO_SIGNAL
    
    def _execute_trades(self, current_price: float):
        """
        Execute trades based on signals and position status
        
        Args:
            current_price: Current price of the security
        """
        holdings = self.portfolio[self._symbol]
        
        # Entry signals
        if self._current_signal == TradingSignal.BUY and not self.portfolio.invested:
            self._enter_long(current_price)
        
        # Exit signals
        elif self._current_signal == TradingSignal.SELL and self.portfolio.invested:
            self._exit_position("Signal crossover")
        
        # Risk management: Check stop loss and take profit
        elif self.portfolio.invested and holdings.quantity > 0:
            pnl_pct = (current_price - self._entry_price) / self._entry_price
            
            # Stop loss
            if pnl_pct <= -self._stop_loss_percent:
                self._exit_position(f"Stop loss triggered ({pnl_pct:.2%})")
            
            # Take profit
            elif pnl_pct >= self._take_profit_percent:
                self._exit_position(f"Take profit triggered ({pnl_pct:.2%})")
    
    def _enter_long(self, entry_price: float):
        """
        Enter a long position
        
        Args:
            entry_price: Price at which to enter
        """
        self._entry_price = entry_price
        quantity = int((self.portfolio.total_portfolio_value * self._position_size) / entry_price)
        
        if quantity > 0:
            self.buy(self._symbol, quantity)
            self.log(f"ENTRY: Buying {quantity} shares of {self._symbol} at ${entry_price:.2f}")
    
    def _exit_position(self, reason: str):
        """
        Exit current position
        
        Args:
            reason: Reason for exiting
        """
        holdings = self.portfolio[self._symbol]
        if holdings.quantity > 0:
            exit_price = self.securities[self._symbol].price
            pnl = (exit_price - self._entry_price) * holdings.quantity
            pnl_pct = (exit_price - self._entry_price) / self._entry_price
            
            self.sell(self._symbol, holdings.quantity)
            self.log(f"EXIT: Selling {holdings.quantity} shares at ${exit_price:.2f} | " +
                    f"Reason: {reason} | PnL: ${pnl:.2f} ({pnl_pct:.2%})")
            
            self._entry_price = 0
            self._current_signal = TradingSignal.NO_SIGNAL
# region imports
from AlgorithmImports import *
# endregion
# test.py
# A new Python file

def main():
    print("Hello, World!")

if __name__ == "__main__":
    main()