3
votes

First things first, I will confess I am not an M or Power Query expert, though I do have some experience with Power BI.

I am trying to develop a stock portfolio that will track a customised list of stocks with their price history and other metrics. Part of my code is based on the following blog, due to the problem I am trying to solve:

https://datachant.com/2016/08/09/sentiment-analysis-power-bi-part-2/

I have the following function getPriceMetrics in my project that will take one parameter Sym and return the price metrics for an individual stock symbol:

(Sym as any) => let
    Source = Json.Document(Web.Contents("
        https://finnhub.io/api/v1/stock/metric?metric=price&token=ABCXYZ&symbol=" & Sym))
in
    Source

The parameter Sym is based on a "Query" that contains a list of stock symbols.

The function itself works well when I try to call it with one individual symbol. But I would like to run the function against a list of stock symbols - hence the blog above.

I think the solution offered in the blog would serve my purpose well. The only hitch is that I am unable to make the function call correctly with the each keyword, I think:

Table.Group(#"Renamed Columns", {"Index"},
    {{"Data", each getPriceMetrics(_), type record}})

At this point, I get the following error.

An error occurred in the ‘getPriceMetrics’ query. Expression.Error: We cannot apply operator & to types Text and Table. Details:
Operator = &
Left = https://finnhub.io/api/v1/stock/metric?metric=price&token=ABCXYZ&symbol=
Right = [Table]

I would appreciate it if someone can offer any insight in how to supply a list argument to the function call when using the each keyword.

Edit: Here's my original approach, which is working technically speaking, but due to the limit of 30 API calls per second, some records are returned as null.

let
Source = Excel.Workbook(File.Contents("C:\Users\Sikander\Google Drive\Docs\Finance\Investments\Questrade\FIRE_strategy.xlsx"), null, true),
Portfolio_Sheet = Source{[Item="Portfolio",Kind="Sheet"]}[Data],
#"Promoted Headers" = Table.PromoteHeaders(Portfolio_Sheet, [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Symbol", type text}, {"Company", type text}}),
#"Invoked Custom Function" = Table.AddColumn(#"Changed Type", "getPriceMetrics", each getPriceMetrics([Symbol])) 
in #"Invoked Custom Function"

To counter this, I followed the approach suggested in the blog that will allow one to "cheat" this API limit, so to speak, by indexing and grouping, like so:

let
Source = Excel.Workbook(File.Contents("C:\Users\Sikander\Google Drive\Docs\Finance\Investments\Questrade\FIRE_strategy.xlsx"), null, true),
Portfolio_Sheet = Source{[Item="Portfolio",Kind="Sheet"]}[Data],
#"Promoted Headers" = Table.PromoteHeaders(Portfolio_Sheet, [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Symbol", type text}, {"Company", type text}}),
#"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 0, 1),
#"Integer-Divided Column" = Table.TransformColumns(#"Added Index", {{"Index", each Number.IntegerDivide(_, 10), Int64.Type}}),
#"Renamed Columns" = Table.RenameColumns(#"Integer-Divided Column",{{"Symbol", "Ticker"}}),
#"Grouped Rows" = Table.Group(#"Renamed Columns", {"Index"}, {{"Data", each getPriceMetrics(_), type record}})

in #"Grouped Rows"

1

1 Answers

3
votes

The error is happening because you are passing a table to your function instead of a fixed symbol. That table is the subtable of #"Renamed Columns" for a particular (single) Index value. Depending on what your table is, you might be able to get away with picking a single element from that table to pass to the function. Here's what that would look like if you took the first row {0} and [Index] column of each table _.

Table.Group(#"Renamed Columns", {"Index"},
    {{"Data", each getPriceMetrics(_{0}[Index]), type record}})

However, I don't think there's a good reason to be doing Table.Group at all. If your "Query" is a list, then you can simply write the following to apply the function to each element:

List.Transform(Query, each getPriceMetrics(_))

If "Query" is a table with a column [Sym] you can replace Query with Query[Sym] in the above since a table column is a list.

Alternatively, you could add a customized column in the query editor GUI with the simple formula getPriceMetrics([Sym]) and it will create a step with the following code:

Table.AddColumn(#"Previous Step Name Here", "Custom", each getPriceMetrics([Sym]))

Edit: This recent answer of mine has some screenshots of working with functions in the query editor that are relevant here as well:

Power Query: how to add one to a column when a specific values appear in an other column


Edit 2: As pointed out in the comments below, the solution to this particular problem does not have to do with getting the syntax exactly correct but require a sleep functionality as I pointed to in this linked post.

Table.AddColumn(#"Changed Type", "PriceMetrics",
    each Function.InvokeAfter(()=>getPriceMetrics([Symbol]),#duration(0,0,0,1)))`