问题
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:
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)))`
来源:https://stackoverflow.com/questions/61567584/passing-a-list-to-a-function-call-with-the-each-keyword