Python code to get live price

11 posts / 0 new
Last post
femto
Python code to get live price

Hello,

I'm trying to get live price using Python and IG Markets LightStreamer streaming API.
Login code (with CST and XST) is based on https://github.com/femtotrader/ig-markets-rest-api-python-library which works fine.

Here is my code:

main.py
======


#!/usr/bin/env python
#-*- coding:utf-8 -*-

"""
IG Markets streaming API Library for Python
https://labs.ig.com/streaming-api-guide
By FemtoTrader - 2014 - femto dot trader at gmail dot com

see also:
for IG Markets REST API
https://github.com/lewisbarber/ig-markets-rest-api-python-library
"""

import lightstreamer
from ig_service_config import *
import requests
import json
import logging
import logging.config
import time

class IGStreamingService:
CLIENT_TOKEN = None
SECURITY_TOKEN = None

BASIC_HEADERS = None
LOGGED_IN_HEADERS = None
DELETE_HEADERS = None

D_BASE_URL = {
'live': 'https://api.ig.com/gateway/deal',
'demo': 'https://demo-api.ig.com/gateway/deal'
}

API_KEY = None
IG_USERNAME = None
IG_PASSWORD = None

D_LIGHTSTREAMER_URL = {
'live': 'https://apd.marketdatasystems.com',
'demo': 'https://demo-apd.marketdatasystems.com',
}

def __init__(self, username, password, api_key, acc_type="demo"):
"""Constructor, calls the method required to connect to the API (accepts acc_type = LIVE or DEMO)"""
self.API_KEY = api_key
self.IG_USERNAME = username
self.IG_PASSWORD = password

try:
self.BASE_URL = self.D_BASE_URL[acc_type.lower()]
except:
raise(Exception("Invalid account type specified, please provide LIVE or DEMO."))

self.BASIC_HEADERS = {
'X-IG-API-KEY': self.API_KEY,
'Content-Type': 'application/json',
'Accept': 'application/json; charset=UTF-8'
}

self.parse_response = self.parse_response_with_exception

try:
self.LIGHTSTREAMER_URL = self.D_LIGHTSTREAMER_URL[acc_type.lower()]
except:
raise(Exception("Invalid account type specified, please provide LIVE or DEMO."))

self.create_session()
#logging.info(self.LOGGED_IN_HEADERS)
password_cst_xst = self.password_cst_xst()

logging.info("Connecting as %s" % self.IG_USERNAME)
logging.info("Connecting to lightstreamer server %s password %s" % (self.LIGHTSTREAMER_URL, password_cst_xst))

self.client = lightstreamer.LsClient(self.LIGHTSTREAMER_URL)
self.client.on_state.listen(self.on_state)

self.client.create_session(adapter_set='my_adapter_set',
username=self.IG_USERNAME, password=password_cst_xst)

table = lightstreamer.Table(self.client,
data_adapter='PriceInfoAdapter',
mode=lightstreamer.MODE_MERGE,
item_ids='MARKET:IX.D.FTSE.DAILY.IP', #'MARKET:IX.D.FTSE.DAILY.IP|MARKET:MT.D.GC.MONTH1.IP'
schema='BID'#, # 'BID|OFFER
#item_factory=lambda row: tuple(float(v) for v in row)
)

table.on_update.listen(self.on_price_changed)

try:
while(True):
time.sleep(1)
except:
logging.info("Quit")
#self.client.unsubscribe(subscription);
self.client.destroy()

def on_price_changed(self, data):
print("Price changed: %s" % data)

def on_state(self, state):
logging.info("New state: %s" % state)

def on_subscription(self):
logging.info('subscribed')

def on_unsubscription(self):
logging.info('unsubscribed')

def on_subscription_error(self, code, message):
logging.info("subscription failure: %s message: %s" % (code, message))

#def on_item_update(self, updateInfo):
# # Lightstreamer published some data
# epic = updateInfo.getItemName().split(":")[1]
# for (field_name, field_pos, value) in X:
# logging.info("Field: %s Value: %s" % (field_name, value)
# # Alternatively, if the field is JSON, such as in a confirm message:
# confirm = JSON.parse(value)
# logging.info("json: %s" % confirm.dealId)

########## PARSE_RESPONSE ##########

def parse_response_without_exception(self, *args, **kwargs):
"""Parses JSON response
returns dict
no exception raised when error occurs"""
response = json.loads(*args, **kwargs)
return(response)

def parse_response_with_exception(self, *args, **kwargs):
"""Parses JSON response
returns dict
exception raised when error occurs"""
response = json.loads(*args, **kwargs)
if 'errorCode' in response:
raise(Exception(response['errorCode']))
return(response)

############ END ############

########### LOGIN ###########

def create_session(self):
"""Creates a trading session, obtaining session tokens for subsequent API access"""
params = {
'identifier': self.IG_USERNAME,
'password': self.IG_PASSWORD
}

response = requests.post(self.BASE_URL + '/session', data=json.dumps(params), headers=self.BASIC_HEADERS)
self._set_headers(response.headers, True)
data = self.parse_response(response.text)
return(data)

############ END ############

########## PRIVATE ##########

def _set_headers(self, response_headers, update_cst):
"""Sets headers"""
if update_cst == True:
self.CLIENT_TOKEN = response_headers['CST']

try:
self.SECURITY_TOKEN = response_headers['X-SECURITY-TOKEN']
except:
self.SECURITY_TOKEN = None

self.LOGGED_IN_HEADERS = {
'X-IG-API-KEY': self.API_KEY,
'X-SECURITY-TOKEN': self.SECURITY_TOKEN,
'CST': self.CLIENT_TOKEN,
'Content-Type': 'application/json',
'Accept': 'application/json; charset=UTF-8'
}

self.DELETE_HEADERS = {
'X-IG-API-KEY': self.API_KEY,
'X-SECURITY-TOKEN': self.SECURITY_TOKEN,
'CST': self.CLIENT_TOKEN,
'Content-Type': 'application/json',
'Accept': 'application/json; charset=UTF-8',
'_method': 'DELETE'
}

def password_cst_xst(self):
return("CST-%s|XST-%s" % (self.LOGGED_IN_HEADERS['CST'], self.LOGGED_IN_HEADERS['X-SECURITY-TOKEN']))

############ END ############

def main():
logging.info("IG Markets - API streaming")
ig_stream = IGStreamingService(username, password, api_key, acc_type)

if __name__ == '__main__':
logging.config.fileConfig("logging.conf")
logger = logging.getLogger("IG_MARKETS_STREAM")
main()

with

logging.conf
=========

[loggers]
keys=root,IG_MARKETS_STREAM

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_IG_MARKETS_STREAM]
level=DEBUG
handlers=consoleHandler
qualname=IG_MARKETS_STREAM
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

and
ig_service_config.py
===============

#!/usr/bin/env python
#-*- coding:utf-8 -*-

username = "YOUR_USERNAME"
password = "YOUR_PASSWORD"
api_key = "YOUR_API_KEY"
acc_type = "DEMO" # LIVE / DEMO
acc_number = "ABC123"

Unfortunately I get

$ python main.py
2014-12-01 18:33:20,257 - root - INFO - IG Markets - API streaming
2014-12-01 18:33:20,739 - root - INFO - Connecting as demo-****
2014-12-01 18:33:20,740 - root - INFO - Connecting to lightstreamer server https://demo-apd.marketdatasystems.com password CST-***|XST-***
2014-12-01 18:33:20,740 - lightstreamer.LsClient - DEBUG - New state: 'connecting'
2014-12-01 18:33:20,741 - root - INFO - New state: connecting
2014-12-01 18:33:20,741 - lightstreamer.LsClient - DEBUG - New state: 'disconnected'
2014-12-01 18:33:20,741 - root - INFO - New state: disconnected

but nothing else appears.

Any idea ?

Kind regards

Femto

colombao
Ciao Femto, I have the same

Ciao Femto, I have the same problem. With Python 3.4, using only Request library it is relatively easy to use IG REST trading API, but it seems that the streaming feature is not so simple with this language. I asked help in this thread https://labs.ig.com/node/28 .

Thanks and br, Dario

gianluca.finocchiaro
Hi All,

Hi All,

just released on GitHub a simple Python script that shows a minimal client-side implementation of the Lightstreamer Server Text mode Protocol.

https://github.com/Weswit/Lightstreamer-example-StockList-client-python

Regards,
Gianluca

colombao
Hi Gianluca, many thanks for

Hi Gianluca, many thanks for your code. It works perfectly with LighStreamer Stock_List_Demo but for me it doesn't with IG LS server.

I modified your code in order to be used with IG : http://www.yourfilelink.com/get.php?fid=1018265

Instruction

1) login into http://labs.ig.com/sample-apps/api-companion/index.html with a DEMO account and copy CST, XST e account name

2) modify lines 313-318 updating values

EXAMPLE:

############################################################################
############### SECTION TO BE UPDATED USING DATA PROVIDED BY REST TRADING API
cst = 'c39bccbe6e63b808606717650b7d60dfdf164344af2f0e07e2e5640d8721a24301111'
xst = '9959426da3f4a889a21c3ec43c7b863f9488a780e6e41e898003645a38fe4bb201111'
usr = "XAGUS"
#############################################################################

3) update lines 337-340 with an Item and a field (as indicated in https://labs.ig.com/streaming-api-reference) :

EXAMPLE:

subscription = Subscription( mode="MERGE",
    adapter="",
    items=["CHART:CS.D.EURJPY.CFD.IP:TICK"],
    fields=['OFR'] )

4) save and run the code

It connects to LS server but I don't see any update after "HIT CR TO UNSUBSCRIBE AND DISCONNECT FROM LIGHTSTREAMER"...

I'm doing something wrong?

Many thanks for your support!

gianluca.finocchiaro
Hi,

Hi,

unfortunately I was not able to generate an API Web key, it seems that my demo account is not good for such operation.
Anyway, could you post your issue on Lightstreamer Fourm? I think your problem is very easy to resolve. After that, we get back here to post the complete solution.

Thanks and regards
Gianluca

gianluca.finocchiaro
Hi Dario,

Hi Dario,

the script was meant to be very minimal, just to work in very simple cases, also from a deployment perspective. In your scenario, Lightstreamer server is behind a load balancer, so after a client establishes a new session it has to contact the correct target machine (whose server address is carried in the response as per the Lightstreamer Text Protocol) for all control requests. Here you can find more details on this.

Moreover, I can confirm that by subscribing to the items "L1:CS.D.GBPUSD.CFD.IP" and "MARKET:CS.D.GBPUSD.CFD.IP", the real-time updates are printed out on the console.

I've just modified and uploaded the demo with required fixes. Just check it out from GitHub and let me know,

Thanks for reporting such issues!
Regards,
Gianluca

colombao
Many Thanks Gianluca! It

Many Thanks Gianluca! It works my side. Great job!

br, Colombao

prajwal88
Hi,

Hi,

I have been using the python code to connect to lightsteamer in python 3.5. However there seems to be some error where even after the server connection being'OK' I only get probe messages at time. I have to restart the kernal and run it a few times before a successful connection is established with live streaming prices.

Not sure why this is so. Any help will be appreciated. Please see the code below for the receive section where I get the probe error at times.

def _receive(self):
receive = True
while receive:
log.debug("Waiting for a new message")
try:
message = self._stream_connection.readline().decode('utf-8')
print (message)
log.debug("Received message - %s" % message)
except Exception:
message = None

if message is None:
receive = False
log.warning("No new message received")
elif message.startswith(PROBE_CMD):
# Skipping the PROBE message, keep on receiving messages.
log.debug("PROBE message")
elif message.startswith(ERROR_CMD):
# Terminate the receiving loop on ERROR message
receive = False
log.error("ERROR")
elif message.startswith(LOOP_CMD):
# Terminate the the receiving loop on LOOP message.
# A complete implementation should proceed with
# a rebind of the session.
log.debug("LOOP")
receive = False
elif message.startswith(SYNC_ERROR_CMD):
# Terminate the receiving loop on SYNC ERROR message.
# A complete implementation should create a new session
# and re-subscribe to all the old items and relative fields.
log.error("SYNC ERROR")
receive = False
elif message.startswith(END_CMD):
# Terminate the receiving loop on END message.
# The session has been forcibly closed on the server side.
# A complete implementation should handle the
# "cause_code" if present.
log.info("Connection closed by the server")
receive = False
elif message.startswith("Preamble"):
# Skipping Preamble message, keep on receiving messages.
log.debug("Preamble")
else:
self._forward_update_message(message.rstrip())

strangecarr
Can't get streaming prices

Hi I think I am having the same problem as the previous post, I can only get the following messages (when switch to DEBUG) when I run the script. After it connects, I get:

DEBUG:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): api.ig.com
DEBUG:requests.packages.urllib3.connectionpool:https://api.ig.com:443 "POST /gateway/deal/session HTTP/1.1" 200 329
INFO:trading_ig.stream:Starting connection with https://apd.marketdatasystems.com
DEBUG:trading_ig.lightstreamer:Waiting for a new message
----------HIT CR TO UNSUBSCRIBE AND DISCONNECT FROM LIGHTSTREAMER-----------
DEBUG:trading_ig.lightstreamer:Received message --->
DEBUG:trading_ig.lightstreamer:PROBE message
DEBUG:trading_ig.lightstreamer:Waiting for a new message
DEBUG:trading_ig.lightstreamer:Received message --->
DEBUG:trading_ig.lightstreamer:PROBE message
DEBUG:trading_ig.lightstreamer:Waiting for a new message
DEBUG:trading_ig.lightstreamer:Received message --->
DEBUG:trading_ig.lightstreamer:PROBE message

Does anyone have any advice?

edada
Hello strangecarr,

Hello strangecarr,

It seems you don't subscribe to any items, when subscribing it is written in output. Maybe you omitted this, or your subscription is malformed ?

Billy223
connection error

I am using Python trading_ig library for REST API. I can streaming for about 2 hours then has following error. I am not sure is it my internet problem.

File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 377, in _make_request
httplib_response = conn.getresponse(buffering=True)
TypeError: getresponse() got an unexpected keyword argument 'buffering'
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 560, in urlopen
body=body, headers=headers)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 379, in _make_request
httplib_response = conn.getresponse()
File "/usr/lib/python3.5/http/client.py", line 1197, in getresponse
response.begin()
File "/usr/lib/python3.5/http/client.py", line 297, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.5/http/client.py", line 266, in _read_status
raise RemoteDisconnected("Remote end closed connection without"
http.client.RemoteDisconnected: Remote end closed connection without response
File "/usr/lib/python3/dist-packages/requests/adapters.py", line 376, in send
timeout=timeout
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 610, in urlopen
_stacktrace=sys.exc_info()[2])
File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 247, in increment
raise six.reraise(type(error), error, _stacktrace)
File "/usr/lib/python3/dist-packages/six.py", line 685, in reraise
raise value.with_traceback(tb)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 560, in urlopen
body=body, headers=headers)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 379, in _make_request
httplib_response = conn.getresponse()
File "/usr/lib/python3.5/http/client.py", line 1197, in getresponse
response.begin()
File "/usr/lib/python3.5/http/client.py", line 297, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.5/http/client.py", line 266, in _read_status
raise RemoteDisconnected("Remote end closed connection without"
requests.packages.urllib3.exceptions.ProtocolError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response',))

Can anyone help me?

Log in or register to post comments