The SetCash value doesn't even reach the back-tester, what's going on, I've spent days trying to work this out !!?
I've even commented out all methods apart from a basic execution, but it still doesn't initiate any trade activity. Here's the code and I attached the backtest:
#from AlgorithmImports import *
#from datetime import timedelta
#class ForexCFDAlgorithm(QCAlgorithm):
# from AlgorithmImports import *
from AlgorithmImports import *
class ForexCFDAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2016, 5, 5) # Start Date
self.SetCash(50000) # Set Strategy Cash
self.symbol = self.AddForex("EURUSD", Resolution.Daily).Symbol
self.Debug("Initialization complete")
def OnData(self, data):
# Manual trigger for a long trade
if self.Time.hour == 10 and not self.Portfolio.Invested:
quantity = 1 # Positive for long position
self.MarketOrder(self.symbol, quantity)
self.Debug(f"Manual long trade executed for {self.symbol.Value}")
#def Initialize(self):
# self.SetStartDate(2016, 1, 1) # Start Date
# self.SetCash(50000) # Set Strategy Cash
# Forex symbol
# self.symbol = self.AddForex("EURUSD", Resolution.Daily).Symbol
# Indicators
# self.daily_ema = self.EMA(self.symbol, 10, Resolution.Daily)
# self.atr = self.ATR(self.symbol, 100, Resolution.Daily)
# self.long_term_ema = self.EMA(self.symbol, 200, Resolution.Daily)
# State variables
# self.is_long_term_bullish = False
# self.batch_positions = []
# self.batch_entry_prices = []
# self.open_positions = 0
# self.basket_size = 5
# self.atr_multiplier_for_space = 1
# self.last_basket_position_time = self.Time
# self.trailing_stop = 0
# self.last_10ema_cross = 'none'
#def OnEndOfDay(self, symbol):
# if symbol != self.symbol:
# return
# Update long-term bullish/bearish based on daily 200 EMA
# if self.long_term_ema.IsReady:
# self.is_long_term_bullish = self.Securities[self.symbol].Close > self.long_term_ema.Current.Value
#def OnData(self, data):
# Force open a position for testing
# if not self.Portfolio.Invested:
# self.MarketOrder(self.symbol, 0.05) # Replace 'quantity' with the desired number of units
# self.Debug(f"Forced position opened for {self.symbol.Value}")
#if not data.ContainsKey(self.symbol):
# return
# Update ATR and ensure all indicators are ready
#if not self.daily_ema.IsReady or not self.atr.IsReady or not self.long_term_ema.IsReady:
# return
#current_price = data[self.symbol].Price
# Update last 10 EMA cross state
#if current_price > self.daily_ema.Current.Value:
# self.last_10ema_cross = 'above'
#elif current_price < self.daily_ema.Current.Value:
# self.last_10ema_cross = 'below'
# Check if conditions are met to start a new basket
#if self.ShouldStartNewBasket(current_price):
# self.StartNewBasket()
# Execute trailing stop loss logic
#self.ExecuteTrailingStopLoss(data)
# Logic to handle entry for both bullish and bearish trends
#if self.is_long_term_bullish and self.open_positions < self.basket_size:
# if self.CanEnterNewPosition(current_price, 'Long'):
# self.EnterPosition('Long', data)
#elif not self.is_long_term_bullish and self.open_positions < self.basket_size:
# if self.CanEnterNewPosition(current_price, 'Short'):
# self.EnterPosition('Short', data)
#def EnterPosition(self, direction, data):
# lot_size = self.CalculateLotSize() # Calculate lot size based on risk management
# self.batch_positions.append(self.symbol)
# self.batch_entry_prices.append((data[self.symbol].Close, lot_size, direction))
# self.open_positions += 1
# self.last_basket_position_time = self.Time
# if direction == 'Long':
# self.Buy(self.symbol, lot_size)
# else:
# self.Sell(self.symbol, lot_size)
#def ShouldStartNewBasket(self, current_price):
# if self.last_10ema_cross == 'none':
# return False
# if self.is_long_term_bullish and self.last_10ema_cross == 'below' and current_price > self.daily_ema.Current.Value:
# return True
# if not self.is_long_term_bullish and self.last_10ema_cross == 'above' and current_price < self.daily_ema.Current.Value:
# return True
# return False
#def StartNewBasket(self):
# self.batch_positions = []
# self.batch_entry_prices = []
# self.open_positions = 0
# self.last_basket_position_time = self.Time
#def CanEnterNewPosition(self, current_price, direction):
# if len(self.batch_entry_prices) == 0:
# return True
# last_entry_price, _, _ = self.batch_entry_prices[-1]
# elapsed_time = self.Time - self.last_basket_position_time
# Check time delay and price distance
# if elapsed_time >= timedelta(minutes=120):
# atr_value = self.atr.Current.Value
# pip_distance = self.atr_multiplier_for_space * atr_value
# if direction == 'Long' and current_price - last_entry_price >= pip_distance:
# return True
# elif direction == 'Short' and last_entry_price - current_price >= pip_distance:
# return True
# return False
#def CalculateLotSize(self):
# Implement lot size calculation logic based on account value and risk management
# Placeholder value
# return 0.05
#def ExecuteTrailingStopLoss(self, data):
# if not self.atr.IsReady or len(self.batch_entry_prices) == 0:
# return
# atr_value = self.atr.Current.Value
# trailing_stop_distance = 0.5 * atr_value # Trailing by 0.5x ATR
# Calculate new trailing stop for each position
# for i in range(len(self.batch_entry_prices)):
# entry_price, lot_size, direction = self.batch_entry_prices[i]
# if direction == 'Long':
# For long positions, trailing stop is below the entry price
# new_trailing_stop = entry_price + trailing_stop_distance
# if new_trailing_stop > self.trailing_stop:
# self.trailing_stop = new_trailing_stop
# elif direction == 'Short':
# For short positions, trailing stop is above the entry price
# new_trailing_stop = entry_price - trailing_stop_distance
# if self.trailing_stop == 0 or new_trailing_stop < self.trailing_stop:
# self.trailing_stop = new_trailing_stop
# Check if current price has hit the trailing stop
# current_price = data[self.symbol].Price
# if ((direction == 'Long' and current_price <= self.trailing_stop) or
# (direction == 'Short' and current_price >= self.trailing_stop)):
# self.Liquidate(self.symbol)
# self.Debug("Trailing stop hit. Liquidating positions.")
Lee Allen
What I see (and I am not very experienced with this):
You are using a Daily feed but you test if hour==10. I dont know when the Day ends, if it ends at 10:00 that condition will always succeed, if it ends at any other time that condition will never be true.
So I would check that first, and then put some self.Debug() statements at the top of OnData() and after your if test, to see if the code is being hit.
Lee Allen
BTW my OnData() code looks like this:
Now you have access to quote_bar.Bid.Open, etc.
But as I said, if you are accessing Daily resolution data, you should not be testing Time.hour – it will always have the same value.
Ross Brook
Code revised, I noticed a few loop and indentation errors. but still doesn't work…
from AlgorithmImports import *
from datetime import timedelta
class ForexCFDAlgorithm(QCAlgorithm):
def Initialize(self):
# Example of adding a security and an indicator
self.symbol = self.AddForex("EURUSD", Resolution.Daily).Symbol
self.ema = self.EMA("EURUSD", 200, Resolution.Daily)
# Warm-up period based on the longest indicator
self.SetWarmUp(200, Resolution.Daily)
self.SetStartDate(2016, 5, 5) # Start Date
self.SetCash(50000) # Set Strategy Cash
# Indicators
self.daily_ema = self.EMA(self.symbol, 10, Resolution.Daily)
self.atr = self.ATR(self.symbol, 100, Resolution.Daily)
self.long_term_ema = self.EMA(self.symbol, 200, Resolution.Daily)
# State variables
self.is_long_term_bullish = False
self.batch_positions = []
self.batch_entry_prices = []
self.open_positions = 0
self.basket_size = 5
self.atr_multiplier_for_space = 1
self.last_basket_position_time = self.Time
self.trailing_stop = 0
self.last_10ema_cross = 'none'
self.previous_day = self.Time.date()
def OnEndOfDay(self, symbol):
if symbol != self.symbol:
return
# Update long-term bullish/bearish based on daily 200 EMA
if self.long_term_ema.IsReady:
self.is_long_term_bullish = self.Securities[self.symbol].Close > self.long_term_ema.Current.Value
def OnData(self, slice: Slice) -> None:
# Check if the algorithm is still warming up
if self.IsWarmingUp:
return
# Check if we have data for the symbol
if not data.ContainsKey(self.symbol):
return
# Update ATR and EMA indicators
if not (self.daily_ema.IsReady and self.atr.IsReady and self.long_term_ema.IsReady):
return
# Retrieve current price
current_price = self.Securities[self.symbol].Price
# Update the state for the last 10 EMA cross
if current_price > self.daily_ema.Current.Value:
self.last_10ema_cross = 'above'
elif current_price < self.daily_ema.Current.Value:
self.last_10ema_cross = 'below'
# Execute trailing stop loss logic
self.ExecuteTrailingStopLoss(data)
# Check if conditions are met to start a new basket of positions
if self.ShouldStartNewBasket(current_price):
self.StartNewBasket()
# Trading logic for both bullish and bearish market conditions
if self.is_long_term_bullish and self.open_positions < self.basket_size:
if self.CanEnterNewPosition(current_price, 'Long'):
self.EnterPosition('Long', data)
elif not self.is_long_term_bullish and self.open_positions < self.basket_size:
if self.CanEnterNewPosition(current_price, 'Short'):
self.EnterPosition('Short', data)
def EnterPosition(self, direction, data):
lot_size = self.CalculateLotSize() # Calculate lot size based on risk management
self.batch_positions.append(self.symbol)
self.batch_entry_prices.append((data[self.symbol].Close, lot_size, direction))
self.open_positions += 1
self.last_basket_position_time = self.Time
if direction == 'Long':
self.Buy(self.symbol, lot_size)
else:
self.Sell(self.symbol, lot_size)
def ShouldStartNewBasket(self, current_price):
if self.last_10ema_cross == 'none':
return False
if self.is_long_term_bullish and self.last_10ema_cross == 'below' and current_price > self.daily_ema.Current.Value:
return True
if not self.is_long_term_bullish and self.last_10ema_cross == 'above' and current_price < self.daily_ema.Current.Value:
return True
return False
def StartNewBasket(self):
self.batch_positions = []
self.batch_entry_prices = []
self.open_positions = 0
self.last_basket_position_time = self.Time
def CanEnterNewPosition(self, current_price, direction):
if len(self.batch_entry_prices) == 0:
return True
last_entry_price, _, _ = self.batch_entry_prices[-1]
elapsed_time = self.Time - self.last_basket_position_time
# Check time delay and price distance
if elapsed_time >= timedelta(minutes=120):
atr_value = self.atr.Current.Value
pip_distance = self.atr_multiplier_for_space * atr_value
if direction == 'Long' and current_price - last_entry_price >= pip_distance:
return True
elif direction == 'Short' and last_entry_price - current_price >= pip_distance:
return True
return False
def CalculateLotSize(self):
# Implement lot size calculation logic based on account value and risk management
# Placeholder value
return 0.05
def ExecuteTrailingStopLoss(self, data):
if not self.atr.IsReady or len(self.batch_entry_prices) == 0:
return
atr_value = self.atr.Current.Value
trailing_stop_distance = 0.5 * atr_value # Trailing by 0.5x ATR
# Calculate new trailing stop for each position
for entry_price, lot_size, direction in self.batch_entry_prices:
if direction == 'Long':
# For long positions, trailing stop is below the entry price
new_trailing_stop = entry_price + trailing_stop_distance
if new_trailing_stop > self.trailing_stop:
self.trailing_stop = new_trailing_stop
elif direction == 'Short':
# For short positions, trailing stop is above the entry price
new_trailing_stop = entry_price - trailing_stop_distance
if self.trailing_stop == 0 or new_trailing_stop < self.trailing_stop:
self.trailing_stop = new_trailing_stop
# Check if current price has hit the trailing stop
current_price = data[self.symbol].Price
if ((direction == 'Long' and current_price <= self.trailing_stop) or
(direction == 'Short' and current_price >= self.trailing_stop)):
self.Liquidate(self.symbol)
self.Debug("Trailing stop hit. Liquidating positions.")
Ashutosh
Hi Ross,
I appreciate your work to date but I noticed a couple of issues with the code.
Changing this to “def OnData(self, data: Slice) -> None:” triggers OnData.
In response to implementing these changes, trades were quickly initiated and subsequently liquidated based on the updated logic. It might be beneficial to debug the code's logic to ensure trades and stop-loss mechanisms are executed correctly.
Best,
Ashutosh
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Ross Brook
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!