0

I'm trying to automate a trading strategy which should enter/exit a long position when the current price is the minimum/maximum among the previous k prices.

The result should contain 1 if the current number is maximum among previous k numbers, -1 if it is the minimum and 0 if none of the conditions are true.

For example if k = 3 and the numpyp array = [1, 2, 3, 2, 1, 6], the result should be an array like: [0, 0, 1, 0, -1, 1].

I tried the numpy's max function but don't know how to take into account the previous k numbers instead of fixed index and how to switch to default condition for the first k - 1 numbers which should be 0 since there are not k number available to compare them with.

  • Why the 4th element in your expected result is 0? 2 is the min one in [2, 3, 2], right? – Hanwei Tang Feb 08 '23 at 08:18
  • What if an element is both the min and max? i.e. if you have a stretch of k identical values. – mozway Feb 08 '23 at 09:02
  • I guess that is an implementation detail whether to count it if it is not unique, in my case I would prefer that the element is strictly less than all other elements in that range. This also handles the case when there are k identical values, which should not be counted. – MUHAMMAD LABEEB Feb 08 '23 at 10:32

3 Answers3

4

I will use Pandas

import pandas as pd
array = [1, 2, 3, 2, 1, 6]
df = pd.DataFrame(array)
df['rolling_max'] = df[0].rolling(3).max()
df['rolling_min'] = df[0].rolling(3).min()
df['result'] = df.apply(lambda row: 1 if row[0] == row['rolling_max'] else (-1 if row[0] == row['rolling_min'] else 0), axis=1)
Hanwei Tang
  • 413
  • 3
  • 10
1

Here is a solution with numpy using numpy.lib.stride_tricks.sliding_window_view, which was introduced in version 1.20.0.

Note that this solution (like the one proposed by @Hanwei Tang) does not exactly yield the result you was looking for, because in the second window ([2, 3, 2]) 2 is the minimum value and thus a -1 is returned instead of zero (what you requested). But maybe you should rethink whether you really want a zero for the second window or a -1.

EDIT: If a windows only contains same numbers, i.e. the minimum and maximum are the same, this method returns a zero.

import numpy as np

def rolling_max(a, wsize):
    windows = np.lib.stride_tricks.sliding_window_view(a, wsize)
    return np.max(windows, axis=-1)

def rolling_min(a, wsize):
    windows = np.lib.stride_tricks.sliding_window_view(a, wsize)
    return np.min(windows, axis=-1)

def check_prize(a, wsize):
    rmax = rolling_max(a, wsize)
    rmin = rolling_min(a, wsize)
    ismax = np.where(a[wsize-1:] == rmax, 1, 0)
    ismin = np.where(a[wsize-1:] == rmin, -1, 0)
    result = np.zeros_like(a)
    result[wsize-1:] = ismax + ismin
    return result

a = np.array([1, 2, 3, 2, 1, 6])
check_prize(a, wsize=3)
# Output:
# array([ 0,  0,  1, -1, -1,  1])

b = np.array([1, 2, 4, 3, 1, 6])
check_prize(b, wsize=3)
# Output:
# array([ 0,  0,  1,  0, -1,  1])

c = np.array([1, 2, 2, 2, 1, 6])
check_prize(c, wsize=3)
# Output:
# array([ 0,  0,  1,  0, -1,  1])
andthum
  • 87
  • 6
1

Another approach using sliding_window_view with pad:

from numpy.lib.stride_tricks import sliding_window_view as swv

k = 3
a = np.array([1, 2, 3, 2, 1, 6])

# create sliding window
v = swv(np.pad(a.astype(float), (k-1, 0), constant_values=np.nan), k)

# compare each element to min/max of sliding window
out = np.select([np.max(v, 1)==a, np.min(v, 1)==a], [1, -1], 0)

Output: array([ 0, 0, 1, -1, -1, 1])

mozway
  • 194,879
  • 13
  • 39
  • 75