Overall Statistics |
Total Orders 292 Average Win 1.12% Average Loss -0.28% Compounding Annual Return 1.465% Drawdown 16.300% Expectancy 0.927 Start Equity 100000.00 End Equity 122185.03 Net Profit 22.185% Sharpe Ratio -0.03 Sortino Ratio -0.017 Probabilistic Sharpe Ratio 0.027% Loss Rate 62% Win Rate 38% Profit-Loss Ratio 4.03 Alpha -0.001 Beta 0.019 Annual Standard Deviation 0.061 Annual Variance 0.004 Information Ratio 0.265 Tracking Error 0.093 Treynor Ratio -0.097 Total Fees $0.00 Estimated Strategy Capacity $1000.00 Lowest Capacity Asset CORNUSD 8I Portfolio Turnover 0.32% |
from AlgorithmImports import * class DonchianCfdTradingAlgorithm(QCAlgorithm): def Initialize(self): """Initialize the algorithm and set up necessary parameters and indicators.""" self.SetStartDate(2010, 1, 1) #self.SetEndDate(2021,11,1) self.SetCash(100000) self.SetBrokerageModel(BrokerageName.OandaBrokerage, AccountType.Margin) # Indicator config self.donchian_period = int(self.GetParameter("donchian_period")) self.ema_period = int(self.GetParameter("ema_period")) self.atr_period = 14 self.risk_per_trade = 0.04 # 2% risk per trade self.trade_resolution = Resolution.Daily # CFD symbols to trade (full list of Oanda CFDs) self.cfd_symbols = [ #"AU200AUD", "BCOUSD", "CH20CHF", "CORNUSD", "DE10YBEUR", "DE30EUR", #"EU50EUR", "FR40EUR", "HK33HKD", "JP225USD", "NAS100USD", "NATGASUSD", #"NL25EUR", "SG30SGD", "SOYBNUSD", "SPX500USD", "SUGARUSD", "UK100GBP", #"UK10YBGBP", "US2000USD", "US30USD", "USB02YUSD", "USB05YUSD", "USB10YUSD", #"USB30YUSD", "WHEATUSD", "WTICOUSD", "XAGUSD", "XAUUSD", "XCUUSD", "XPDUSD", "XPTUSD", "AU200AUD", "BCOUSD", "CH20CHF", "CORNUSD", "DE10YBEUR", "DE30EUR", "EU50EUR", "FR40EUR", "HK33HKD", "JP225USD", "NAS100USD", "NATGASUSD", "NL25EUR", "SG30SGD", "SOYBNUSD", "SPX500USD", "SUGARUSD", "UK100GBP", "UK10YBGBP", "US2000USD", "US30USD", "USB02YUSD", "USB05YUSD", "USB10YUSD", "USB30YUSD", "WHEATUSD", "WTICOUSD", "XAGAUD", "XAGCAD", "XAGCHF", "XAGEUR", "XAGGBP", "XAGHKD", "XAGJPY", "XAGNZD", "XAGSGD", "XAGUSD", "XAUAUD", "XAUCAD", "XAUCHF", "XAUEUR", "XAUGBP", "XAUHKD", "XAUJPY", "XAUNZD", "XAUSGD", "XAUUSD", "XAUXAG", "XCUUSD", "XPDUSD", "XPTUSD" ] self.symbols = [] self.donchian_channels = {} self.ema_indicators = {} self.atr_indicators = {} for symbol_str in self.cfd_symbols: symbol = self.AddCfd(symbol_str, self.trade_resolution, Market.Oanda).Symbol self.symbols.append(symbol) self.donchian_channels[symbol] = self.dch(symbol, self.donchian_period, self.donchian_period) self.ema_indicators[symbol] = self.SMA(symbol, self.ema_period) self.atr_indicators[symbol] = self.ATR(symbol, self.atr_period) self.SetWarmUp(max(self.donchian_period, self.ema_period, self.atr_period), self.trade_resolution) def OnData(self, data): """Handle incoming data and execute trading logic.""" if self.IsWarmingUp: return for symbol in self.symbols: if symbol not in data: continue if not self.AreIndicatorsReady(symbol): continue price = data[symbol].Close donchian = self.donchian_channels[symbol].Current.Value donchian_lower = self.donchian_channels[symbol].lower_band.Current.Value donchian_upper = self.donchian_channels[symbol].upper_band.Current.Value ema = self.ema_indicators[symbol].Current.Value atr = self.atr_indicators[symbol].Current.Value is_uptrend = price > ema is_downtrend = price < ema if self.Portfolio[symbol].Invested: self.ManageExistingPosition(symbol, price, donchian_upper, donchian_lower, ema, atr) self.CheckNewEntry(symbol, price, donchian_upper, donchian_lower, is_uptrend, is_downtrend, atr) def AreIndicatorsReady(self, symbol): """Check if all indicators for a symbol are ready.""" return (self.donchian_channels[symbol].IsReady and self.ema_indicators[symbol].IsReady and self.atr_indicators[symbol].IsReady) def ManageExistingPosition(self, symbol, price, donchian_upper, donchian_lower, ema, atr): """Manage existing positions, including trailing stops.""" if self.Portfolio[symbol].IsLong: stop_price = donchian_lower + (2 * atr) if price <=ema or price <= stop_price: self.Liquidate(symbol) self.Debug(f"Liquidated LONG position on {symbol.Value} at price {price}") elif self.Portfolio[symbol].IsShort: stop_price = donchian_upper - (2 * atr) if price >= ema or price >= stop_price: self.Liquidate(symbol) self.Debug(f"Liquidated SHORT position on {symbol.Value} at price {price}") def CheckNewEntry(self, symbol, price, donchian_upper, donchian_lower, is_uptrend, is_downtrend, atr): """Check for new entry conditions and enter positions if conditions are met.""" if is_uptrend and price >= donchian_upper: self.EnterPosition(symbol, True, price, donchian_lower, atr) elif is_downtrend and price <= donchian_lower: self.EnterPosition(symbol, False, price, donchian_upper, atr) def EnterPosition(self, symbol, is_long, price, band, atr): """Enter a new position with proper position sizing.""" stop_price = band + (2 * atr * (-1 if is_long else 1)) position_size = self.CalculatePositionSize(symbol, price, stop_price) if position_size == 0: return if is_long: self.SetHoldings(symbol, 0.1) self.Debug(f"Entered LONG position on {symbol.Value} at price {price}, size: {position_size}") else: #self.SetHoldings(symbol, -0.3) self.Debug(f"Entered SHORT position on {symbol.Value} at price {price}, size: {-position_size}") def CalculatePositionSize(self, symbol, price, stop_price): """Calculate position size based on risk management rules.""" risk_amount = self.Portfolio.TotalPortfolioValue * self.risk_per_trade price_range = abs(price - stop_price) if price_range == 0: self.Debug(f"Zero price range for {symbol.Value}. Skipping trade.") return 0 # Calculate the position size as a fraction of the portfolio position_size = risk_amount / price_range / price # Ensure we're not risking more than 2% per trade return min(position_size, 0.02)