[Help] Share Price API

Hi Everyone,

I’m looking for help with parsing of a json string from an API I’ve recently found. My coding skills are not great and therefore need a little assistance.

Here’s an example of the output from the API

{
    "Meta Data": {
        "1. Information": "Intraday (15min) prices and volumes",
        "2. Symbol": "LON:TW",
        "3. Last Refreshed": "2017-12-29 07:30:00",
        "4. Interval": "15min",
        "5. Output Size": "Compact",
        "6. Time Zone": "US/Eastern"
    },
    "Time Series (15min)": {
        "2017-12-29 07:30:00": {
            "1. open": "206.8000",
            "2. high": "208.3000",
            "3. low": "206.8000",
            "4. close": "208.1000",
            "5. volume": "393996"
        },
        "2017-12-29 07:15:00": {
            "1. open": "206.5310",
            "2. high": "207.0000",
            "3. low": "206.5000",
            "4. close": "206.8000",
            "5. volume": "316518"
           }
    }
}

The JSONPATH syntax I think i need is; $.Time Series (15min)[*].[1. open]

Which results in this output

[
  "206.8000",
  "206.5310"
]

How do I get the top value into an Item?

You could convert to String and select only the things you are interested in. I also looked at your jsonpath. It´s correct, but need some minor changes to work in my example. I´ve added some escape characters in the json output to get the example to work…

val String jsonString = new String("{\"Meta Data\":{\"1. Information\":\"Intraday (15min) prices and volumes\",\"2. Symbol\":\"LON:TW\",\"3. Last Refreshed\":\"2017-12-29 07:30:00\",\"4. Interval\":\"15min\",\"5. Output Size\":\"Compact\",\"6. Time Zone\":\"US/Eastern\"},\"Time Series (15min)\":{\"2017-12-29 07:30:00\":{\"1. open\":\"206.8000\",\"2. high\":\"208.3000\",\"3. low\":\"206.8000\",\"4. close\":\"208.1000\",\"5. volume\":\"393996\"},\"2017-12-29 07:15:00\":{\"1. open\":\"206.5310\",\"2. high\":\"207.0000\",\"3. low\":\"206.5000\",\"4. close\":\"206.8000\",\"5. volume\":\"316518\"}}}")
val String result = transform("JSONPATH","$.['Time Series (15min)'].[*].['1. open']",jsonString)
val String first = result.subSequence(3,11)
logInfo("First value :", first)

The result is

[smarthome.model.script.First value :] - 206.8000

It’s a poorly designed piece of JSON. Had the time series been an array it would have been trivial. I don’t think you can do it in one line of JSONPATH as it is. I would probably use JavaScript to retrieve it.

1 Like

Thanks I’ll have to have a play around and find a solution.

I’ve made a small amount of progress with this and need further advice.

Here’s the rule I’ve created that works well, however as you can see it’s scheduled and that’s not beneficial as share prices move all the time. I’m looking for a way to check the URL and then to fire off the rule below.

rule "Get Current SL Share Price"
when
        Time cron "0 2 8 * * ?"
then {
        val String jsonString = sendHttpGetRequest('https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=LON:SL&interval=15min&outputsize=compact&apik
ey=MYAPIKEYHERE')
        if(jsonString != null) {
                val String result = transform("JSONPATH","$.['Time Series (15min)'].[*].['1. open']",jsonString)
                val String first = result.subSequence(2,10)
                postUpdate(SL_Share_Price, first)
        } else {
                postUpdate(SL_Share_Price, "0.000")
        }
}
end

I’ve tried using the http binding to update a String as below, but it’s producing an error.

String        TW_Status { http="<[https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=LON:TW&interval=15min&outputsize=compact&apikey=MYAPIKEYHERE]" }

Any ideas.

Thanks,

Garry

According to the documentation, you can refresh by using the following syntax

http="<[<url>:<refreshintervalinmilliseconds>:<transformationrule>]"

You could do the json path in the items conf. to check for updates every second like below using the http binding

String  stockPriceRaw "Raw stockprice %s" {http="<[https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=LON:SL&interval=15min&outputsize=compact&apik
ey=MYAPIKEYHERE:1000:JSONPATH($.['Time Series (15min)'].[*].['1. open'])]" }

Then you could create a rule to do

when item stockPriceRaw changed
then
      // do the sub sequence stuff here and update the SL_Share_Price item
end

In addition, you could change the way you get the first element of the json result. You could do the following to get the first element instead of using sub sequence.

SL_Share_Price = transform("JSONPATH","$[0]",stockPriceRaw)

This will get the first element (206.8000) of

[
  "206.8000",
  "206.5310"
]

Prease note that I don’t have access to my OpenHab environment when writing this, so there could be some typo’s

Definitely made progress with your suggestion.

The item produces the full list of share data, however I get this error every time and the share price item isn’t updated.

Rule 'TW Share Price': An error occurred during the script execution: The name '<XFeatureCallImplCustom>.subSequence(<XNumberLiteralImpl>,<XNumberLiteralImpl>)' cannot be resolved to an item or type.

Any ideas where I need to look to resolve this?

String  TW_Status { http="<[https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=LON:TW&interval=15min&outputsize=compact&apikey=MYAPIKEYHERE:60000:JSONPATH($.['Time Series (15min)'].[*].['1. open'])]" }
rule "TW Share Price"
when
        Item TW_Status received update
then
        val String current = TW_Status.subSequence(2,10)
        postUpdate(TW_Share_Price, current)
end

I think the error is a result of you trying to apply subSequence to an Item while subSequence is a method of the String class.

Try this for your rule

rule "TW Share Price"
when
        Item TW_Status received update
then
        val String current = TW_Status.state.toString()
        TW_Share_Price.postUpdate(current.subSequence(2,10))
end

For completeness;

Items:

String  TW_Status { http="<[https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=LON:TW&interval=15min&outputsize=compact&apikey=MYAPIKEY:900000:JSONPATH($.['Time Series (15min)'].[*].['1. open'])]" }
String  TC_Status { http="<[https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=LON:TCG&interval=15min&outputsize=compact&apikey=MYAPIKEY:900000:JSONPATH($.['Time Series (15min)'].[*].['1. open'])]" }
String  SL_Status { http="<[https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=LON:SLA&interval=15min&outputsize=compact&apikey=MYAPIKEY:900000:JSONPATH($.['Time Series (15min)'].[*].['1. open'])]" }
Number  TW_Current_Price <line>
Number  TC_Current_Price <line>
Number  SL_Current_Price <line>
Number  TW_Previous_Price <line>
Number  TC_Previous_Price <line>
Number  SL_Previous_Price <line>

Rules:

rule "Taylor Wimpey Share Price"
when
        Item TW_Status received update
then
        val String previous = TW_Current_Price.state.toString()
        val String current = TW_Status.state.toString()
        TW_Previous_Price.postUpdate(previous)
        TW_Current_Price.postUpdate(current.subSequence(2,10))
end

rule "Thomas Cook  Share Price"
when
        Item TC_Status received update
then
        val String previous = TC_Current_Price.state.toString()
        val String current = TC_Status.state.toString()
        TC_Previous_Price.postUpdate(previous)
        TC_Current_Price.postUpdate(current.subSequence(2,10))
end

rule "Standard Life Share Price"
when
        Item SL_Status received update
then
        val String previous = SL_Current_Price.state.toString()
        val String current = SL_Status.state.toString()
        SL_Previous_Price.postUpdate(previous)
        SL_Current_Price.postUpdate(current.subSequence(2,10))
end

Sitemap:

sitemap Shares label="Shares"
{
        Frame label="Taylor Wimpey" {
                Text item=TW_Current_Price label="Latest Price [%.2f]" valuecolor=[TW_Current_Price>TW_Previous_Price="green", <TW_Previous_Price="red"]
                Text item=TW_Previous_Price label="Previous Price [%.2f]"
        }
        Frame label="Thomas Cook" {
                Text item=TC_Current_Price label="Latest Price [%.2f]"
                Text item=TC_Previous_Price label="Previous Price [%.2f]"
        }
        Frame label="Standard Life" {
                Text item=SL_Current_Price label="Latest Price [%.2f]"
                Text item=SL_Previous_Price label="Previous Price [%.2f]"
        }
}

Now that I’ve upgrade to OH 2.3.0 the solution above doesn’t work, I believe that something has changed in the way that the transformation now works.

After a little searching of the forum I’ve found a possible alternative but need a little help getting the syntax sorted out.

val csv = sendHttpGetRequest("url to CSV DATA").split("\n")
val lastLine = csv.get(csv.size-1).split(",")

val T = lastLine.get(0).trim
val TimeStamp = lastLine.get(1).trim
val Price = lastLine.get(2).trim

The above works, but I’m looking for the first line not the last line. Here’s the content of the CSV data.

timestamp,open,high,low,close,volume
21/12/2018 11:30,134.45,135.05,134.45,134.95,465833

Any pointers will be greatly appreciated.

val lastLine = csv.get(csv.size-1)

If you’re looking for the first line, why are you asking for the last line?

That’s my point, I have the syntax to obtain the lastline but not the first or even the second line. I found the code while searching, now I need a little advise on changing to meet my needs.

The term in parentheses determines which line is pulled from the csv:

csv.size is the total number of lines in the input. We subtract 1 to get the last line because lines are numbered starting from 0. If you want the first line, just use 0:

val firstLine = csv.get(0)