Crossed check vs simple comparison

One of questions frequently asked by new users, especially those new to trading, is which one is better, using crossing logic (crossed above/below) or simple comparison (more/less/or equal than). First of all, you need to really understand the important aspect about crossing logic. As the name suggested, the logic only triggered at the point where value A crossed above/below value B. Other than that, it won’t be triggered.

Let’s take this entry logic as example.

def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            qtpylib.crossed_below(dataframe['close'], dataframe['bb_lowerband'])
        ),
        'enter_long'] = 1
    return dataframe

It’s very simple. The entry logic is only triggered when close value crossed below bb_lowerband. Once the close value crossed below, the entry signal won’t be triggered on the next candles, until it goes above bb_lowerband and then crossing below it again. For this case, using crossed_below is preferred than the simple close < bb_lowerband.

Now take a look at this exit logic.

def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            qtpylib.crossed_above(dataframe['close'], dataframe['bb_upperband'])
            &
            (dataframe['rsi'] > 75)
            &
            (dataframe'volume'] > 0)
        ),
        'enter_long'] = 1
    return dataframe

This might seems like a normal logic, but no it’s not. There is a hidden big issue in this logic. Let me try to explain the logic, and let’s see if you can notice what is the big issue. The exit signal will only be triggered if

  • close value is crossing above bb_upperband
  • rsi value at that time is above 75
  • volume at that time is above zero

Notice the issue? No? Let’s see this case.

At candle 15:05:00, as you can see in the image, close value is above bb_upperband, rsi is above 75 and volume above zero. But as you can also see in the image, exit signal isn’t triggered. The question is why? The answer lies in candle 14:55:00, as seen below

At candle 14:55:00, the close value crossed above bb_upperband, but the rsi value at that time was below 75, so no exit signal was being triggered. And then if you see candle 15:00:00, you can see that the rsi value is above 75, but then close value is not crossing above bb_upperband, because it’s already above bb_upperband.

Same issue on candle 16:10:00, which as seen in the image, there is no exit signal being triggered, since close was crossing above bb_upperband at 16:05:00, but the rsi at that time is below 75.

This is the issue when you are using crossed logic with other strict checks, where it’s gonna be quite hard to get them to aligned. For such scenario, I would advise using the simple comparison, like this

def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            (dataframe['close'] > dataframe['bb_upperband'])
            &
            (dataframe['rsi'] > 75)
            &
            (dataframe'volume'] > 0)
        ),
        'enter_long'] = 1
    return dataframe

For this logic, the exit signal would have been triggered at candle 15:00, 15:05 and 16:10 for the example above.

So in summary, if your signal logic is only using a single crossed check, then it’s okay to use it. But if you gonna combine crossed logic with other logics, then you might want to check whether using simple comparison would be better, or you would want the other logics to be less strict (for example in the case above, maybe you set the rsi check to be above 50 instead of 75).

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *