1
\$\begingroup\$

I am writing trading software, and have written this utility function. Its purpose is to return a list of timeframes relevant to the just-elapsed time period. "Timeframe" here means bar or candle period.

E.g if time has just struck UTC 10:30am, the returned list will contain "1m", "3m", "5m", "m15" and "30m" strings, as those are the candles or bars that have just closed.

The first minute of a new day or week will add daily/weekly/monthly timeframe strings.

Timeframes in use are 1, 3, 5, 15 and 30 mins, 1, 2, 3, 4, 6, 8 and 12 hours, 1, 2 and 3 days, as well as weekly and monthly.

Is there a more elegant, less verbose way to achieve this?

Note: the output could also be integers instead of strings, where each timeframe is expressed as its total number of minutes.

def get_timeframes(self):
    timestamp = datetime.datetime.utcnow()
    timeframes = ["1m"]

    # 3 minute bars
    for x in range(0, 20):
        val = x * 3
        if timestamp.minute == val:
            timeframes.append("3m")

    # 5 minute bars
    for x in range(0, 12):
        val = x * 5
        if timestamp.minute == val:
            timeframes.append("5m")

    # 15 minute bars
    for x in range(0, 4):
        val = x * 15
        if timestamp.minute == val:
            timeframes.append("15m")

    # 30 minute bars
    for x in range(0, 2):
        val = x * 30
        if timestamp.minute == val:
            timeframes.append("30m")

    # 1 hour bars
    if timestamp.minute == 0:
        timeframes.append("1h")

    # 2 hour bars
    if timestamp.minute == 0 & timestamp.hour % 2 == 0:
        timeframes.append("2h")

    # 3 hour bars
    if timestamp.minute == 0 & timestamp.hour % 3 == 0:
        timeframes.append("3h")

    # 4 hour bars
    if timestamp.minute == 0 & timestamp.hour % 4 == 0:
        timeframes.append("4h")

    # 6 hour bars
    if timestamp.minute == 0 & timestamp.hour % 6 == 0:
        timeframes.append("6h")

    # 8 hour bars
    if timestamp.minute == 0 & timestamp.hour % 8 == 0:
        timeframes.append("8h")

    # 12 hour bars
    if timestamp.minute == 0 & timestamp.hour % 12 == 0:
        timeframes.append("12h")

    # 1 day bars
    if timestamp.minute == 0 & timestamp.hour == 0:
        timeframes.append("1d")

    # 2 day bars
    if (
        timestamp.minute == 0 & timestamp.hour == 0 &
            timestamp.day % 2 == 0):
                timeframes.append("2d")

    # 3 day bars
    if (
        timestamp.minute == 0 & timestamp.hour == 0 &
            timestamp.day % 3 == 0):
                timeframes.append("3d")

    # weekly bars
    if (
        timestamp.minute == 0 & timestamp.hour == 0 &
            calendar.day_name[date.today().weekday()] == "Monday"):
                timeframes.append("1w")

    return timeframes
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I think you should separate the computation in three different functions, one for minutes, one for hours, and one for days. And generalize each of these functions, so they can be used for other time frames if you ever decide to change them.

Minutes

You can specify an extra parameter in range(), the step: how much is added every iteration. With this the method to check a minute time frame is quite simple:

def minute_timeframe(minutes, timestamp, timeframes):
    for x in range(0, 60, minutes):
        if timestamp.minute == x:
            timeframes.append(f"{minutes}m")

Since your tags say you use Python 3, I used formatted string literals.

Hours

Be careful! You are using the & operator, which in Python is the binary and operation. Since what you want is probably the logical and (i.e. this is true and the other is true), use the and keyword (same for or and not).

By looking at your implementation of hour timeframes, it's pretty easy to generalize:

def hour_timeframe(hours, timestamp, timeframes):
    if timestamp.minute == 0 and timestamp.hour % hours == 0:
        timeframes.append(f"{hours}h")

Days

The same happens for generalizing days. I must comment though, that your original does not seem to follow the PEP 8 Style for the indentations on that part of the code. You also do not need the parenthesis surrounding the if condition.

def day_timeframe(days, timestamp, timeframes):
    if timestamp.minute == 0 and timestamp.hour == 0 and timestamp.day % days == 0:
          timeframes.append(f"{days}d")

Final result

Now that our functions are generalized, in my opinion what would be best is to store a list of the different timeframes you want to use, that way adding new ones or changing them is simple. This is the final function:

# Constants go in ALL_CAPS
MINUTE_TIMEFRAMES = [3, 5, 15, 30]
HOUR_TIMEFRAMES = [1, 2, 3, 4, 6, 8]
DAY_TIMEFRAMES = [1, 2, 3]

def get_timeframes(self):
    timestamp = datetime.datetime.utcnow()
    timeframes = ["1m"]

    for x in MINUTE_TIMEFRAMES:
        minute_timeframe(x, timestamp, timeframes)

    for x in HOUR_TIMEFRAMES:
        hour_timeframe(x, timestamp, timeframes)

    for x in DAY_TIMEFRAMES:
        day_timeframe(x, timestamp, timeframes)

    # Format it correctly and use 'and'
    if (timestamp.minute == 0 and timestamp.hour == 0 and
        calendar.day_name[date.today().weekday()] == "Monday"):
        timeframes.append("1w")
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.