user3746428
user3746428

Reputation: 11175

Generate array containing weighted average values of other arrays

I'm creating a cryptocurrency price tracking app which has a chart to illustrate the value of a portfolio over the past 7 days. As such, I need a way to generate the data points on the chart.

For each cryptocurrency in a portfolio, I have an array of NSDecimalNumber values which represent the price of the cryptocurrency at each hour in the past week. To create the data points, I need a way to calculate an overall weighted average of the hourly prices. By weighted, I mean that the overall average array should represent the amount of each cryptocurrency currently held.

For example, if I have the following arrays:

Cryptocurrency 1 (25% of the overall portfolio value) - [1.2, 8.3, 7.2] 
Cryptocurrency 2 (25% of the overall portfolio value) - [3.4, 9.2, 6.3]
Cryptocurrency 3 (50% of the overall portfolio value) - [6.3, 1.1, 5.9]

The result should be:

[4.3, 4.92, 6.33]

As a result of the calculations:

((1.2 * 0.75) + (3.4 * 0.75) + (6.3 * 1.5)) / 3 = 4.3
((8.3 * 0.75) + (9.2 * 0.75) + (1.1 * 1.5)) / 3 = 4.92
((7.2 * 0.75) + (6.3 * 0.75) + (5.9 * 1.5)) / 3 = 6.33

I'm certain there must be a clean way to do this with map and reduce, however I haven't yet thought of any potential solutions. Even a loop-based answer would be appreciated, as I can streamline it later.

Upvotes: 1

Views: 91

Answers (2)

user3746428
user3746428

Reputation: 11175

If anyone is curious, this is what I ended up with based on qtngo's answer:

    let holdings = cryptocurrencies.flatMap { cryptocurrency -> (cryptocurrency: Cryptocurrency, assetHolding: AssetHolding)? in
        guard let assetHolding = portfolio.assetHolding(forCryptocurrency: cryptocurrency) else {
            return nil
        }
        return (cryptocurrency, assetHolding)
    }
    let sparklineLength = holdings.map { $0.cryptocurrency.marketOverview.sparkline.count }.min() ?? 0
    let weights = holdings.flatMap { holding -> NSDecimalNumber in
        let quantity = NSDecimalNumber(value: holding.assetHolding.quantity)
        let value = holding.cryptocurrency.marketOverview.valuation.value
        let totalHoldingValue = value.multiplying(by: quantity)
        let totalPortfolioValue = portfolio.totalValue(withCryptocurrencies: cryptocurrencies)
        let percentage = totalHoldingValue.dividing(by: totalPortfolioValue)
        return percentage
    }

    var averageSparkline = [NSDecimalNumber]()
    for index in 0..<sparklineLength {
        let indexSparklineValues = holdings.flatMap { holding -> NSDecimalNumber? in
            return holding.cryptocurrency.marketOverview.sparkline[index]
        }
        let averageSparklineValue = zip(indexSparklineValues, weights).map { $0.multiplying(by: $1) }.reduce(0, +)
        averageSparkline.append(averageSparklineValue)
    }

It needs some tidying up and can probably be simplified but it produces the result I was looking for.

Upvotes: 0

qtngo
qtngo

Reputation: 1679

You can try this code:

    let weight = [0.25, 0.25, 0.5]// Weights of your crypto, matrix 1*N
    let crypto1 = [1.2, 8.3, 7.2]
    let crypto2 = [3.4, 9.2, 6.3]
    let crypto3 = [6.3, 1.1, 5.9]
    let crypto = [crypto1, crypto2, crypto3]// Matrix M*N

    var result: [Double] = []
    for i in 0..<weight.count {
        let aux = crypto.map { $0[i] }
        let sum = zip(aux, weight).map { $0 * $1 }.reduce(0,+)
        result.append(sum)
    }
    print("test: \(result)")  // print "test: [4.3, 4.925, 6.325]"

Hope this helps.

Upvotes: 1

Related Questions