Overall Statistics
Total Trades
10
Average Win
0%
Average Loss
0.00%
Compounding Annual Return
0.000%
Drawdown
0.000%
Expectancy
-1
Net Profit
0.000%
Sharpe Ratio
-148720.835
Sortino Ratio
-11213.437
Probabilistic Sharpe Ratio
0.000%
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.01
Beta
-0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-0.548
Tracking Error
0.155
Treynor Ratio
771346.915
Total Fees
$0.00
Estimated Strategy Capacity
$400000.00
Lowest Capacity Asset
EURUSD 8G
Portfolio Turnover
0.00%
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, data: 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 1


 

    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.")