Skip to content

Commit 33896d0

Browse files
author
Wasin Waeosri
committed
Merge branch 'streaming_expiration'
1. Add trkd_wsstreaming.py for TRKD Streaming example 2. Add LICENSE.md file
2 parents 6d45020 + b70a24a commit 33896d0

File tree

5 files changed

+302
-23
lines changed

5 files changed

+302
-23
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
chart.png
22
images/
3-
docs/TRKD_REST_with_Python_master.doc
3+
docs/TRKD_REST_with_Python_master.doc
4+
note.txt
5+
test.py
6+
market_price_edpgw_authentication.py
7+
.vscode/

LICENSE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2018 Refinitiv
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

README.md

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The [Thomson Reuters Knowledge Direct (TRKD) API](https://developers.thomsonreut
44

55
TRKD offers a wide range of Thomson Reuters' information and services delivered in a request-response scenario via web services using today's industry standard protocols (SOAP/XML and REST/JSON). Connectivity can be via HTTP and HTTPS, over the Internet or Delivery Direct. All data are snapshot (non-streaming) data.
66

7-
This is an example project that shows how to implement TRKD HTTP JSON Client with python. This project contains the following example scripts for each TRKD services
7+
This is an example project that shows how to implement TRKD HTTP JSON client and TRKD Streaming client with Python programming lanugage. This project contains the following example scripts for each TRKD services
88
- trkd_authen.py: An example application that shows how to authenticate with TRKD service
99
- trkd_quote.py: An example application that shows how to subscribe (all fields and specific fields) the Quote data from TRKD service
1010
- trkd_newsheadline.py: An example application that shows how to subscribe the News Headline data from TRKD service
@@ -13,13 +13,18 @@ This is an example project that shows how to implement TRKD HTTP JSON Client wit
1313
- trkd_interday.py: An example application that shows how to subscribe the Interday Time-series data from TRKD service
1414
- trkd_onlinereport.py: An example application that shows how to subscribe the Online Report data from TRKD service
1515
- trkd_chart.py: An example application that shows how to subscribe and download the Chart image data from TRKD service
16+
- trkd_wsstreaming.py: An example application that show how to subscribe the Quote data from TRKD Streming service via a WebSocket connection
17+
- requestments.txt: A requirement file contains a list of required libraries for HTTP JSON and WebSocket connections.
1618
- docs\TRKD_REST_with_Python.docx: A document that describes the trkd_authen.py and trkd_quote.py applications
1719

20+
All source code and scripts are provided under the Apache 2.0 license. Thye are provided AS IS with no warranty or guarantee of fit for purpose. See the project's LICENSE.md for details.
1821

1922
## Prerequisite
2023
The following softwares are required to use this script
2124
- Python 3
2225
- The [requests](http://docs.python-requests.org/en/master/) library
26+
- The [websocket-client](https://pypi.org/project/websocket-client/) library (*version 0.49 or greater*, for trkd_wsstreaming.py application only)
27+
- The [python-dateutil](https://pypi.org/project/python-dateutil/) library (for trkd_wsstreaming.py application only)
2328

2429
All scripts support Python 3 and not compatible with Python 2.
2530

@@ -34,46 +39,57 @@ The best way is via the pip package management tool
3439
1. export <Python_folder>\Scripts to your OS PATH environment
3540
2. call pip command to install requests
3641
```
37-
$>pip install requests
42+
$>pip install -r requestments.txt
3843
```
3944
3. If you are behind proxy, set the proxy first
4045
```
4146
export https_proxy="http://<proxy.server>:<port>"
42-
$>pip install requests
47+
$>pip install -r requestments.txt
4348
```
4449

50+
*Note*: If you aim to use only TRKD HTTP JSON services, you can just install requests library via a ```pip install requests``` command.
51+
52+
## References
53+
For further details, please check out the following resources:
54+
* [Thomson Reuters Knowledge Direct API page](https://developers.thomsonreuters.com/thomson-reuters-knowledge-direct-trkd) on the [Thomson Reuters Developer Community](https://developers.thomsonreuters.com/) web site.
55+
* [Thomson Reuters Knowledge Direct API Catalog](https://www.trkd.thomsonreuters.com/SupportSite/RequestBuilder/requestbuilder.aspx) web site.
56+
* [Elektron WebSocket API](https://developers.thomsonreuters.com/websocket-api) page on the [Thomson Reuters Developer Community](https://developers.thomsonreuters.com/) web site.
57+
4558
## Release Note
4659
- Version 1: 6 Sep 2016
47-
- trkd_authen.py
48-
- trkd_quote.py
60+
- trkd_authen.py.
61+
- trkd_quote.py.
4962
- Version 1.0.1: 7 Sep 2016
50-
- trkd_newsheadline.py
63+
- trkd_newsheadline.py.
5164
- changed code structure to separate call http request
5265
- Version 1.0.2: 19 Sep 2016
53-
- trkd_newsstory.py
66+
- trkd_newsstory.py.
5467
- version 1.0.3: 22 Sep 2016
55-
- trkd_intraday.py
56-
- trkd_interday.py
57-
- trkd_onlinereport.py
58-
- trkd_chart.py
68+
- trkd_intraday.py.
69+
- trkd_interday.py.
70+
- trkd_onlinereport.py.
71+
- trkd_chart.py.
5972
- version 1.0.4: 28 Oct 2016
60-
- docs\TRKD_REST_with_Python.docx
61-
- revise some code
73+
- docs\TRKD_REST_with_Python.docx.
74+
- revise some code.
6275
- version 1.0.5: 27 Apr 2017
63-
- revies README.md to support markdown
76+
- revies README.md to support markdown.
6477
- version 1.0.6: 3 May 2017
65-
- revies README.md
66-
- modify trkd_authen.py
67-
- modify trkd_quote.py
78+
- revies README.md.
79+
- modify trkd_authen.py.
80+
- modify trkd_quote.py.
6881
- version 1.0.7: 9 May 2017
69-
- revise README.md
70-
- modify the rest of application files
82+
- revise README.md.
83+
- modify the rest of application files.
7184
- version 1.0.7: 31 Aug 2017
7285
- revise README.md
7386
- version 1.0.8: 04 Sep 2017
74-
- Port all scripts to support Python 3
87+
- Port all scripts to support Python 3.
7588
- Fix the issue that some scripts still send request message to the old REST endpoint.
7689
- version 1.0.9: 26 Jan 2018
77-
- Add debug log for checking outgoing message (disabled by default)
90+
- Add debug log for checking outgoing message (disabled by default).
7891
- version 1.0.10: 9 Aug 2018
79-
- remove all ```is not None``` statements and make them a bit more **Pythonic**
92+
- remove all ```is not None``` statements and make them a bit more **Pythonic**.
93+
- version 1.0.11: January 2019
94+
- Add trkd_wsstreaming.py application for TRKD Streaming service.
95+
- Add License.md file

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
###### Requirements Libraries ######
2+
requests
3+
websocket-client>0.49
4+
python-dateutil

trkd_wsstreaming.py

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
'''
2+
The TRKD API sample code is provided for informational purposes only
3+
and without knowledge or assumptions of the end users development environment.
4+
We offer this code to provide developers practical and useful guidance while developing their own code.
5+
However, we do not offer support and troubleshooting of issues that are related to the use of this code
6+
in a particular environment; it is offered solely as sample code for guidance.
7+
Please see the Thomson Reuters Knowledge Direct product page at http://customers.thomsonreuters.com
8+
for additional information regarding the TRKD API.'''
9+
10+
import os
11+
import sys
12+
import requests
13+
import json
14+
import getpass
15+
import time
16+
import getopt
17+
import socket
18+
import websocket
19+
import threading
20+
from threading import Thread, Event
21+
22+
from datetime import datetime, timezone, timedelta
23+
import dateutil.parser
24+
25+
26+
# Global Default Variables
27+
ws_address = 'wss://streaming.trkd.thomsonreuters.com/WebSocket/'
28+
trkd_authen_address = 'https://api.trkd.thomsonreuters.com/api/TokenManagement/TokenManagement.svc/REST/Anonymous/TokenManagement_1/CreateServiceToken_1'
29+
ws_protocol = 'tr_json2'
30+
position = socket.gethostbyname(socket.gethostname())
31+
32+
# Global Variables
33+
web_socket_app = None
34+
web_socket_open = False
35+
username = None
36+
password = None
37+
appid = None
38+
token = None
39+
expiration = None
40+
logged_in = False
41+
42+
ric_name = 'EUR='
43+
44+
expire_time = 0
45+
time_to_relogin = 15 * 60 # 15 Minutes to Seconds
46+
47+
## ------------------------------------------ TRKD HTTP REST functions ------------------------------------------ ##
48+
49+
# Send HTTP request for all services
50+
def doSendRequest(url, requestMsg, headers):
51+
result = None
52+
try:
53+
##send request
54+
result = requests.post(url, data=json.dumps(requestMsg), headers=headers)
55+
# print('outgoing message is %s'%(json.dumps(requestMsg)))
56+
## handle error
57+
if result.status_code is not 200:
58+
print('Request fail')
59+
print('response status %s'%(result.status_code))
60+
if result.status_code == 500: ## if username or password or appid is wrong
61+
print('Error: %s'%(result.json()))
62+
result.raise_for_status()
63+
except requests.exceptions.RequestException as e:
64+
print('Exception!!!')
65+
print(e)
66+
sys.exit(1)
67+
return result
68+
69+
## Perform authentication
70+
def CreateAuthorization(username, password, appid):
71+
token = None
72+
expiration = None
73+
##create authentication request URL, message and header
74+
authenMsg = {'CreateServiceToken_Request_1': { 'ApplicationID':appid, 'Username':username,'Password':password }}
75+
headers = {'content-type': 'application/json;charset=utf-8'}
76+
print('############### Sending Authentication request message to TRKD ###############')
77+
authenResult = doSendRequest(trkd_authen_address, authenMsg, headers)
78+
if authenResult and authenResult.status_code == 200:
79+
print('Authentication success')
80+
print('response status %s'%(authenResult.status_code))
81+
print('Authentication response %s'%json.dumps(authenResult.json(), sort_keys=True, indent=2, separators=(',', ':')))
82+
##get Token
83+
token = authenResult.json()['CreateServiceToken_Response_1']['Token']
84+
expiration = authenResult.json()['CreateServiceToken_Response_1']['Expiration'] # Expiration time of this session in UTC
85+
86+
## Calcuate Expiration time
87+
expire_datetime_utc = dateutil.parser.parse(expiration) ## Parse incoming Expiration to Python datetime object (UTC)
88+
now_datetime_utc = datetime.now(timezone.utc) ## Get current machine datetime object in UTC
89+
90+
expire_time = expire_datetime_utc - now_datetime_utc ## Get time different between now and expiration time value
91+
expire_time = int(round(expire_time / timedelta(seconds=1))) ## convert it to second as a round int
92+
93+
return token, expiration, expire_time
94+
95+
## ------------------------------------------ TRKD WebSocket functions ------------------------------------------ ##
96+
97+
# Process incoming messages from TRKD Elektron WebSocket Server
98+
def process_message(message_json):
99+
""" Parse at high level and output JSON of message """
100+
message_type = message_json['Type']
101+
102+
if message_type == "Refresh":
103+
if 'Domain' in message_json:
104+
message_domain = message_json['Domain']
105+
if message_domain == "Login":
106+
process_login_response(message_json)
107+
# Ping and Pong messages are exchanged between endpoints of a connection to verify that the remote endpoint is still alive.
108+
elif message_type == "Ping":
109+
pong_json = { 'Type':'Pong' } # when either endpoint receives a Ping message, it should send a Pong message in response.
110+
web_socket_app.send(json.dumps(pong_json))
111+
print("SENT:")
112+
print(json.dumps(pong_json, sort_keys=True, indent=2, separators=(',', ':')))
113+
114+
# Process incoming Login Refresh message from TRKD Elektron WebSocket Server
115+
def process_login_response(message_json):
116+
""" Send item request """
117+
global logged_in
118+
119+
logged_in = True
120+
send_market_price_request(ric_name)
121+
122+
# Send JSON OMM Market Price Request message to TRKD Elektron WebSocket Server
123+
def send_market_price_request(ric_name):
124+
""" Create and send simple Market Price request """
125+
mp_req_json = {
126+
'ID': 2,
127+
'Key': {
128+
'Name': ric_name,
129+
},
130+
}
131+
web_socket_app.send(json.dumps(mp_req_json))
132+
print("SENT:")
133+
print(json.dumps(mp_req_json, sort_keys=True, indent=2, separators=(',', ':')))
134+
135+
# Send JSON OMM Login Request message to TRKD Elektron WebSocket Server to initiate the OMM connection
136+
def send_login_request(is_refresh_token=False):
137+
""" Generate a login request from command line data (or defaults) and send """
138+
login_json = {
139+
'ID': 1,
140+
'Domain': 'Login',
141+
'Key': {
142+
'Name': '',
143+
'NameType': 'AuthnToken',
144+
'Elements': {
145+
'ApplicationId': '',
146+
'Position': '',
147+
'AuthenticationToken': ''
148+
}
149+
}
150+
}
151+
152+
login_json['Key']['Name'] = username
153+
login_json['Key']['Elements']['ApplicationId'] = appid
154+
login_json['Key']['Elements']['Position'] = position
155+
login_json['Key']['Elements']['AuthenticationToken'] = token
156+
157+
# If the token is a refresh token, this is not our first login attempt.
158+
if is_refresh_token:
159+
login_json['Refresh'] = False
160+
161+
web_socket_app.send(json.dumps(login_json))
162+
print("SENT:")
163+
print(json.dumps(login_json, sort_keys=True, indent=2, separators=(',', ':')))
164+
165+
# Receive every messages from TRKD Elektron WebSocket Server
166+
def on_message(_, message):
167+
""" Called when message received, parse message into JSON for processing """
168+
print("RECEIVED: ")
169+
message_json = json.loads(message)
170+
print(json.dumps(message_json, sort_keys=True, indent=2, separators=(',', ':')))
171+
172+
for singleMsg in message_json:
173+
process_message(singleMsg)
174+
175+
176+
def on_error(__file__, error):
177+
""" Called when websocket error has occurred """
178+
print(error)
179+
180+
181+
def on_close(_):
182+
""" Called when websocket is closed """
183+
global web_socket_open
184+
print("WebSocket Closed")
185+
web_socket_open = False
186+
187+
# Establish a WebSocket connection success
188+
def on_open(_):
189+
""" Called when handshake is complete and websocket is open, send login """
190+
191+
print("WebSocket successfully connected!")
192+
global web_socket_open
193+
web_socket_open = True
194+
send_login_request(is_refresh_token=False)
195+
196+
## ------------------------------------------ Main App ------------------------------------------ ##
197+
198+
if __name__ == '__main__':
199+
## Get username, password and applicationid
200+
username = input('Please input username: ')
201+
## use getpass.getpass to hide user inputted password
202+
password = getpass.getpass(prompt='Please input password: ')
203+
appid = input('Please input appid: ')
204+
205+
token, expiration, expire_time = CreateAuthorization(username,password,appid)
206+
print('Token = %s'%(token))
207+
print('Expiration = %s'%(expiration))
208+
print('Expiration in next = %d seconds'%(expire_time))
209+
## if authentiacation success, continue subscribing Quote
210+
if token and expiration:
211+
print('Do WS here')
212+
# doWebSocketConnection(username, appid, token)
213+
print("Connecting to WebSocket " + ws_address + " ...")
214+
web_socket_app = websocket.WebSocketApp(ws_address, header=['User-Agent: Python'],
215+
on_message=on_message,
216+
on_error=on_error,
217+
on_close=on_close,
218+
subprotocols=[ws_protocol])
219+
web_socket_app.on_open = on_open
220+
221+
# Event loop
222+
wst = threading.Thread(target=web_socket_app.run_forever)
223+
wst.start()
224+
225+
try:
226+
while True:
227+
if (expire_time > time_to_relogin):
228+
time.sleep(expire_time - time_to_relogin) # Sleep Thread until 15 minutes before expiration
229+
else:
230+
# failt the refresh sine value too small
231+
sys.exit(1)
232+
233+
# Re-issue login request before token expiration to keep session open.
234+
token, expiration, expire_time = CreateAuthorization(username,password,appid)
235+
if not token:
236+
sys.exit(1)
237+
if logged_in:
238+
print('############### Re-new Authentication to TRKD ###############')
239+
# Resend JSON OMM Login Request message with Refresh: false to TRKD Elektron WebSocket Server
240+
send_login_request(is_refresh_token=True)
241+
except KeyboardInterrupt:
242+
web_socket_app.close()

0 commit comments

Comments
 (0)