2
\$\begingroup\$

I am calculating a relative strength index.

I took the calculations quite literally so I am sure everything's correct but now it is time to optimize the code and take it down to fewer variables.

The data is obtained from an API:

if let marketData = try? JSONDecoder().decode(Response.self, from: data) {
            print(marketData.data[0].open)
            completion(marketData, nil)
}

and:

fetchData() { items, error in
guard let items = items,
    error == nil else {
        print(error ?? "Unknown error")
        return
}

let otherObject = calculateRSI()

otherObject.items = items
otherObject.calculateRSI()

}

Here is the struct:

    struct Level: Codable {
    let time: Double
    let close: Double
    let high: Double
    let low: Double
    let open: Double
}

struct Response: Codable {
    let data: [Level]

    private enum CodingKeys : String, CodingKey {
        case data = "Data"
    }
}

How would you do it? I tried to loop through an array but I'm struggling with incrementing the data[x] number...

    func calculateRSI()
{
    // Upward Movements and Downward Movements
    let upwardMovement1 = items.data[1].close > items.data[0].close ? (items.data[1].close - items.data[0].close) : 0
    let downwardMovement1 = items.data[1].close < items.data[0].close ? (items.data[0].close - items.data[1].close) : 0
    let upwardMovement2 = items.data[2].close > items.data[1].close ? (items.data[2].close - items.data[1].close) : 0
    let downwardMovement2 = items.data[2].close < items.data[1].close ? (items.data[1].close - items.data[2].close) : 0
    let upwardMovement3 = items.data[3].close > items.data[2].close ? (items.data[3].close - items.data[2].close) : 0
    let downwardMovement3 = items.data[3].close < items.data[2].close ? (items.data[2].close - items.data[3].close) : 0
    let upwardMovement4 = items.data[4].close > items.data[3].close ? (items.data[4].close - items.data[3].close) : 0
    let downwardMovement4 = items.data[4].close < items.data[3].close ? (items.data[3].close - items.data[4].close) : 0
    let upwardMovement5 = items.data[5].close > items.data[4].close ? (items.data[5].close - items.data[4].close) : 0
    let downwardMovement5 = items.data[5].close < items.data[4].close ? (items.data[4].close - items.data[5].close) : 0
    let upwardMovement6 = items.data[6].close > items.data[5].close ? (items.data[6].close - items.data[5].close) : 0
    let downwardMovement6 = items.data[6].close < items.data[5].close ? (items.data[5].close - items.data[6].close) : 0
    let upwardMovement7 = items.data[7].close > items.data[6].close ? (items.data[7].close - items.data[6].close) : 0
    let downwardMovement7 = items.data[7].close < items.data[6].close ? (items.data[6].close - items.data[7].close) : 0
    let upwardMovement8 = items.data[8].close > items.data[7].close ? (items.data[8].close - items.data[7].close) : 0
    let downwardMovement8 = items.data[8].close < items.data[7].close ? (items.data[7].close - items.data[8].close) : 0
    let upwardMovement9 = items.data[9].close > items.data[8].close ? (items.data[9].close - items.data[8].close) : 0
    let downwardMovement9 = items.data[9].close < items.data[8].close ? (items.data[8].close - items.data[9].close) : 0
    let upwardMovement10 = items.data[10].close > items.data[9].close ? (items.data[10].close - items.data[9].close) : 0
    let downwardMovement10 = items.data[10].close < items.data[9].close ? (items.data[9].close - items.data[10].close) : 0
    let upwardMovement11 = items.data[11].close > items.data[10].close ? (items.data[11].close - items.data[10].close) : 0
    let downwardMovement11 = items.data[11].close < items.data[10].close ? (items.data[10].close - items.data[11].close) : 0
    let upwardMovement12 = items.data[12].close > items.data[11].close ? (items.data[12].close - items.data[11].close) : 0
    let downwardMovement12 = items.data[12].close < items.data[11].close ? (items.data[11].close - items.data[12].close) : 0
    let upwardMovement13 = items.data[13].close > items.data[12].close ? (items.data[13].close - items.data[12].close) : 0
    let downwardMovement13 = items.data[13].close < items.data[12].close ? (items.data[12].close - items.data[13].close) : 0
    let upwardMovement14 = items.data[14].close > items.data[13].close ? (items.data[14].close - items.data[13].close) : 0
    let downwardMovement14 = items.data[14].close < items.data[13].close ? (items.data[13].close - items.data[14].close) : 0
    let upwardMovement15 = items.data[15].close > items.data[14].close ? (items.data[15].close - items.data[14].close) : 0
    let downwardMovement15 = items.data[15].close < items.data[14].close ? (items.data[14].close - items.data[15].close) : 0

    // Average Upward Movements and Average Downward Movements
    let averageUpwardMovement1 = (upwardMovement1 + upwardMovement2 + upwardMovement3 + upwardMovement4 + upwardMovement5 + upwardMovement6 + upwardMovement7 + upwardMovement8 + upwardMovement9 +   upwardMovement10 + upwardMovement11 + upwardMovement12 + upwardMovement13 + upwardMovement14) / 14
    let averageDownwardMovement1 = (downwardMovement1 + downwardMovement2 + downwardMovement3 + downwardMovement4 + downwardMovement5 + downwardMovement6 + downwardMovement7 + downwardMovement8 + downwardMovement9 + downwardMovement10 + downwardMovement11 + downwardMovement12 + downwardMovement13 + downwardMovement14) / 14

    let averageUpwardMovement2 = ((averageUpwardMovement1 * (14 - 1) + upwardMovement15) / 14)
    let averageDownwardMovement2 = ((averageDownwardMovement1 * (14 - 1) + downwardMovement15) / 14)

    // Relative Strength
    let relativeStrength1 = averageUpwardMovement1 / averageDownwardMovement1
    let relativeStrength2 = averageUpwardMovement2 / averageDownwardMovement2

    // Relative Strength Index
    let rSI1 = 100 - (100 / (relativeStrength1 + 1))
    let rSI2 = 100 - (100 / (relativeStrength2 + 1))

    // Relative Strength Index Average
    let rsiAverage = (rSI1 + rSI2) / 2

    print("Relative Strength Index =", rsiAverage)

}

let array = [items] would be the full API data array from which I could increment I guess?

\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review! I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Feel free to give it a different title if there is something more appropriate. \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Dec 29 '17 at 14:56
1
\$\begingroup\$

First I would separate the calculations from the I/O and change the function signature to

func calculateRSI(data: [Data]) -> Double {
    // ...    
    return rsiAverage
}

where Data is your data type containing a close property (which I assume to be of type Double).

The next step is to define the up/downward movements as arrays and to use a for-loop:

// Upward Movements and Downward Movements
var upwardMovements: [Double] = []
var downwardMovements: [Double] = []
for idx in 0..<15 {
    upwardMovements.append(data[idx + 1].close > data[idx].close ? data[idx + 1].close - data[idx].close : 0)
    downwardMovements.append(data[idx + 1].close > data[idx].close ? data[idx + 1].close - data[idx].close : 0)
}

The difference calculation can be simplified by using the max() function:

for idx in 0..<15 {
    let diff =  data[idx + 1].close - data[idx].close
    upwardMovements.append(max(diff, 0))
    downwardMovements.append(max(-diff, 0))
}

Or use zip to iterate over adjacent data values in parallel:

for (prev, now) in zip(data, data.dropFirst()) {
    let diff = now.close - prev.close
    upwardMovements.append(max(diff, 0))
    downwardMovements.append(max(-diff, 0))
}

Adding all elements in an sequence is conveniently done with the reduce method, here we can apply it to an array slice:

// Average Upward Movements and Average Downward Movements
let averageUpwardMovement1 = upwardMovements[0..<14].reduce(0, +) / 14
let averageDownwardMovement1 = downwardMovements[0..<14].reduce(0, +) / 14

let averageUpwardMovement2 = ((averageUpwardMovement1 * (14 - 1) + upwardMovements[14]) / 14)
let averageDownwardMovement2 = ((averageDownwardMovement1 * (14 - 1) + downwardMovements[14]) / 14)

Finally you should define constants for the integer literals 14 and 15, e.g.

let period = 14

or derive it from data.count, i.e. the number of given values.

Putting it all together, the function could look like this:

func calculateRSI(data: [Data]) -> Double {

    let period = data.count - 2

    // Upward Movements and Downward Movements
    var upwardMovements: [Double] = []
    var downwardMovements: [Double] = []

    for (prev, now) in zip(data, data.dropFirst()) {
        let diff = now.close - prev.close
        upwardMovements.append(max(diff, 0))
        downwardMovements.append(max(-diff, 0))
    }

    // Average Upward Movements and Average Downward Movements
    let averageUpwardMovement1 = upwardMovements[0..<period].reduce(0, +) / Double(period)
    let averageDownwardMovement1 = downwardMovements[0..<period].reduce(0, +) / Double(period)

    let averageUpwardMovement2 = (averageUpwardMovement1 * Double(period - 1) + upwardMovements[period]) / Double(period)
    let averageDownwardMovement2 = (averageDownwardMovement1 * Double(period - 1) + downwardMovements[period]) / Double(period)

    // Relative Strength
    let relativeStrength1 = averageUpwardMovement1 / averageDownwardMovement1
    let relativeStrength2 = averageUpwardMovement2 / averageDownwardMovement2

    // Relative Strength Index
    let rSI1 = 100 - (100 / (relativeStrength1 + 1))
    let rSI2 = 100 - (100 / (relativeStrength2 + 1))

    // Relative Strength Index Average
    let rsiAverage = (rSI1 + rSI2) / 2

    return rsiAverage
}
\$\endgroup\$
  • \$\begingroup\$ Thank you very much Martin I couldn't ask for a better explanation! I am getting an error that I don't understand tho Value of type 'Data' has no member 'close' at let diff = now.close - prev.close \$\endgroup\$ – Wizzardzz Dec 29 '17 at 16:11
  • \$\begingroup\$ @Wizzardzz: I assumed that your data array has elements of type struct Data { var close: Double }. \$\endgroup\$ – Martin R Dec 29 '17 at 16:14
  • \$\begingroup\$ I added the struct to the code \$\endgroup\$ – Wizzardzz Dec 29 '17 at 16:19
  • \$\begingroup\$ I fixed that by renaming Level to Data. I have one last error tho, Use of unresolved identifier 'calculateRSI' at let otherObject = calculateRSI() \$\endgroup\$ – Wizzardzz Dec 29 '17 at 16:45
  • \$\begingroup\$ I solved it by simply changing let diff = data[idx + 1].close - data[idx].close to let diff = items.data[idx + 1].close - items.data[idx].close. Thanks again for your help Martin! \$\endgroup\$ – Wizzardzz Dec 29 '17 at 17:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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