ProRealTime
Zone de développement des applications API, des logiciels et utilitaires développés par les membres du forum

[PYTHON] Script API IG STREAM + REST

par cimourdain » 14 sept. 2015 21:50

Bonjour à tous,

J'ai pas mal galéré, mais grace au forum j'ai trouvé quelques réponses que je cherchais donc je trouve assez normal de partager mon travail.

remarque préliminaire: je ne suis pas un développeur, je suis ouvert à tout vos commentaires pour améliorer ce qui suit.

Le script ci-dessous permet de lire les cours (API STREAM) et de passer les ordres (API REST) en pyhton.


1 - Téléchargez le script de l'API Stream de FemoTrader
https://github.com/femtotrader/ig-markets-stream-api-python-library
Dézippez le
dans le fichier igls.py, remplacez (ligne 589):

Code : #

def create_session(self, username, adapter_set, password=None,
par

Code : #

def create_session_stream(self, username, adapter_set, password=None,
2 - Script de l'API REST
Ci-dessous le script de l'API rest (source: https://github.com/femtotrader/ig-markets-rest-api-python-library), nommez le ig_service_rest.py dans le dossier dans lequel vous avez dézippé le script de l'API STREAM à l'étape précédente

Remarque: le forum censure les insultes, remplacez "(mot censuré merci de rester poli)" par p*d* (sans les étoiles)

Code : #

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

"""
IG Markets REST API Library for Python
http://labs.ig.com/rest-trading-api-reference
By Lewis Barber - 2014 - http://uk.linkedin.com/in/lewisbarber/
"""

import requests
import json
import logging
import traceback
import pandas as (mot censuré merci de rester poli)

class IGService:

    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

    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

        self.return_dataframe = True

        #self.create_session()


    ########## 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 ############


    ########## ACCOUNT ##########

    def fetch_accounts(self):
        """Returns a list of accounts belonging to the logged-in client"""
        response = requests.get(self.BASE_URL + '/accounts', headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['accounts'])
        return(data)

    def fetch_account_activity_by_period(self, milliseconds):
        """Returns the account activity history for the last specified period"""
        response = requests.get(self.BASE_URL + '/history/activity/%s' % milliseconds, headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['activities'])
        return(data)

    def fetch_transaction_history_by_type_and_period(self, milliseconds, trans_type):
        """Returns the transaction history for the specified transaction type and period"""
        response = requests.get(self.BASE_URL + '/history/transactions/%s/%s' % (trans_type, milliseconds), headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['transactions'])
        return(data)

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



    ########## DEALING ##########

    def fetch_deal_by_deal_reference(self, deal_reference):
        """Returns a deal confirmation for the given deal reference"""
        response = requests.get(self.BASE_URL + '/confirms/%s' % deal_reference, headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        return(data)

    def fetch_open_positions(self):
        """Returns all open positions for the active account"""
        response = requests.get(self.BASE_URL  + '/positions', headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        '''
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['positions'])
        '''
        return(data)

    def close_open_position(self, deal_id, direction, epic, expiry, level, order_type, quote_id, size):
        """Closes one or more OTC positions"""
        params = { 
            'dealId': deal_id, 
            'direction': direction, 
            'epic': epic, 
            'expiry': expiry, 
            'level': level,
            'orderType': order_type,
            'quoteId': quote_id,
            'size': size
        }

        response = requests.post(self.BASE_URL + '/positions/otc', data=json.dumps(params), headers=self.DELETE_HEADERS)

        if response.status_code == 200:
            deal_reference = json.loads(response.text)['dealReference']
            return(self.fetch_deal_by_deal_reference(deal_reference))
        else:
            return(response.text)

    def create_open_position(self, currency_code, direction, epic, expiry, force_open, 
        guaranteed_stop, level, limit_distance, limit_level, order_type, quote_id, size, 
        stop_distance, stop_level):
        """Creates an OTC position"""
        params = { 
            'currencyCode': currency_code, 
            'direction': direction, 
            'epic': epic, 
            'expiry': expiry, 
            'forceOpen': force_open, 
            'guaranteedStop': guaranteed_stop,
            'level': level,
            'limitDistance': limit_distance,
            'limitLevel': limit_level,
            'orderType': order_type,
            'quoteId': quote_id,
            'size': size,
            'stopDistance': stop_distance,
            'stopLevel': stop_level
        }
        print params
        response = requests.post(self.BASE_URL + '/positions/otc', data=json.dumps(params), headers=self.LOGGED_IN_HEADERS)
        print response
        if response.status_code == 200:
            deal_reference = json.loads(response.text)['dealReference']
            return(self.fetch_deal_by_deal_reference(deal_reference))
        else:
            return(response.text) # parse_response ?

    def update_open_position(self, limit_level, stop_level, deal_id):
        """Updates an OTC position"""
        params = {
            'limitLevel': limit_level,
            'stopLevel': stop_level
        }

        response = requests.put(self.BASE_URL + '/positions/otc/%s' % deal_id, data=json.dumps(params), headers=self.LOGGED_IN_HEADERS)

        if response.status_code == 200:
            deal_reference = json.loads(response.text)['dealReference']
            return(self.fetch_deal_by_deal_reference(deal_reference))
        else:
            return(response.text) # parse_response ?

    def fetch_working_orders(self):
        """Returns all open working orders for the active account"""
        response = requests.get(self.BASE_URL  + '/workingorders', headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['workingOrders'])
        return(data)

    def create_working_order(self, currency_code, direction, epic, expiry, good_till_date, 
        guaranteed_stop, level, limit_distance, limit_level, size, stop_distance, stop_level,
        time_in_force, order_type):
        """Creates an OTC working order"""
        params = { 
            'currencyCode': currency_code, 
            'direction': direction, 
            'epic': epic, 
            'expiry': expiry, 
            'goodTillDate': good_till_date, 
            'guaranteedStop': guaranteed_stop,
            'level': level,
            'limitDistance': limit_distance,
            'limitLevel': limit_level,
            'size': size,
            'stopDistance': stop_distance,
            'stopLevel': stop_level,
            'timeInForce': time_in_force,
            'type': order_type
        }

        response = requests.post(self.BASE_URL + '/workingorders/otc', data=json.dumps(params), headers=self.LOGGED_IN_HEADERS)

        if response.status_code == 200:
            deal_reference = json.loads(response.text)['dealReference']
            return(self.fetch_deal_by_deal_reference(deal_reference))
        else:
            return(response.text) # parse_response ?

    def delete_working_order(self, deal_id):
        """Deletes an OTC working order"""
        response = requests.post(self.BASE_URL + '/workingorders/otc/%s' % deal_id, data=json.dumps({}), headers=self.DELETE_HEADERS)

        if response.status_code == 200:
            deal_reference = json.loads(response.text)['dealReference']
            return(self.fetch_deal_by_deal_reference(deal_reference))
        else:
            return(response.text) # parse_response ?

    def update_working_order(self, good_till_date, level, limit_distance, limit_level, 
        stop_distance, stop_level, time_in_force, order_type, deal_id):
        """Updates an OTC working order"""
        params = {
            'goodTillDate': good_till_date,
            'limitDistance': limit_distance,
            'level': level,
            'limitLevel': limit_level,
            'stopDistance': stop_distance,
            'stopLevel': stop_level,
            'timeInForce': time_in_force,
            'type': order_type
        }

        response = requests.put(self.BASE_URL + '/workingorders/otc/%s' % deal_id, data=json.dumps(params), headers=self.LOGGED_IN_HEADERS)

        if response.status_code == 200:
            deal_reference = json.loads(response.text)['dealReference']
            return(self.fetch_deal_by_deal_reference(deal_reference))
        else:
            return(response.text) # parse_response ?

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



    ########## MARKETS ##########

    def fetch_client_sentiment_by_instrument(self, market_id):
        """Returns the client sentiment for the given instrument's market"""
        response = requests.get(self.BASE_URL + '/clientsentiment/%s' % market_id, headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        return(data)

    def fetch_related_client_sentiment_by_instrument(self, market_id):
        """Returns a list of related (also traded) client sentiment for the given instrument's market"""
        response = requests.get(self.BASE_URL + '/clientsentiment/related/%s' % market_id, headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['clientSentiments'])
        return(data)

    def fetch_top_level_navigation_nodes(self):
        """Returns all top-level nodes (market categories) in the market navigation hierarchy."""
        response = requests.get(self.BASE_URL + '/marketnavigation', headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data['markets'] = (mot censuré merci de rester poli).DataFrame(data['markets'])
            data['nodes'] = (mot censuré merci de rester poli).DataFrame(data['nodes'])
        return(data)

    def fetch_sub_nodes_by_node(self, node):
        """Returns all sub-nodes of the given node in the market navigation hierarchy"""
        response = requests.get(self.BASE_URL + '/marketnavigation/%s' % node, headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data['markets'] = (mot censuré merci de rester poli).DataFrame(data['markets'])
            data['nodes'] = (mot censuré merci de rester poli).DataFrame(data['nodes'])
        return(data)

    def fetch_market_by_epic(self, epic):
        """Returns the details of the given market"""
        response = requests.get(self.BASE_URL + '/markets/%s' % epic, headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        return(data)

    def search_markets(self, search_term):
        """Returns all markets matching the search term"""
        response = requests.get(self.BASE_URL + '/markets?searchTerm=%s' % search_term, headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['markets'])
        return(data)

    def fetch_historical_prices_by_epic_and_date_range(self, epic, resolution, start_date, end_date):
        """Returns a list of historical prices for the given epic, resolution, multiplier and date range"""
        response = requests.get(self.BASE_URL + "/prices/{epic}/{resolution}/?startdate={start_date}&enddate={end_date}".format(epic=epic, resolution=resolution, start_date=start_date, end_date=end_date), headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data['prices'] = (mot censuré merci de rester poli).DataFrame(data['prices'])
        return(data)

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



    ######### WATCHLISTS ########

    def fetch_all_watchlists(self):
        """Returns all watchlists belonging to the active account"""
        response = requests.get(self.BASE_URL + '/watchlists', headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['watchlists'])
        return(data)

    def create_watchlist(self, name, epics):
        """Creates a watchlist"""
        params = { 
            'name': name, 
            'epics': epics
        }

        response = requests.post(self.BASE_URL + '/watchlists', data=json.dumps(params), headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        return(data)

    def delete_watchlist(self, watchlist_id):
        """Deletes a watchlist"""
        response = requests.post(self.BASE_URL + '/watchlists/%s' % watchlist_id, data=json.dumps({}), headers=self.DELETE_HEADERS)
        return(response.text)


    def fetch_watchlist_markets(self, watchlist_id):
        """Returns the given watchlist's markets"""
        response = requests.get(self.BASE_URL + '/watchlists/%s' % watchlist_id, headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        if self.return_dataframe:
            data = (mot censuré merci de rester poli).DataFrame(data['markets'])
        return(data)


    def add_market_to_watchlist(self, watchlist_id, epic):
        """Adds a market to a watchlist"""
        params = { 
            'epic': epic
        }

        response = requests.put(self.BASE_URL + '/watchlists/%s' % watchlist_id, data=json.dumps(params), headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        return(data)

    def remove_market_from_watchlist(self, watchlist_id, epic):
        """Remove an market from a watchlist"""
        response = requests.post(self.BASE_URL + '/watchlists/%s/%s' % (watchlist_id, epic), data=json.dumps({}), headers=self.DELETE_HEADERS)
        return(response.text)

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



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

    def logout(self):
        """Log out of the current session"""
        requests.post(self.BASE_URL + '/session', data=json.dumps({}), headers=self.DELETE_HEADERS)

    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)

    def switch_account(self, account_id, default_account):
        """Switches active accounts, optionally setting the default account"""
        params = { 
            'accountId': account_id, 
            'defaultAccount': default_account
        }

        response = requests.put(self.BASE_URL + '/session', data=json.dumps(params), headers=self.LOGGED_IN_HEADERS)
        self._set_headers(response.headers, False)
        data = self.parse_response(response.text)
        return(data)

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



    ########## GENERAL ##########
    
    def get_client_apps(self):
        """Returns a list of client-owned applications"""
        response = requests.get(self.BASE_URL + '/operations/application', headers=self.LOGGED_IN_HEADERS)

        return self.parse_response(response.text)

    def update_client_app(self, allowance_account_overall, allowance_account_trading, api_key, status):
        """Updates an application"""
        params = { 
            'allowanceAccountOverall': allowance_account_overall, 
            'allowanceAccountTrading': allowance_account_trading, 
            'apiKey': api_key, 
            'status': status
        }

        response = requests.put(self.BASE_URL + '/operations/application', data=json.dumps(params), headers=self.LOGGED_IN_HEADERS)
        data = self.parse_response(response.text)
        return(data)

    def disable_client_app_key(self):
        """Disables the current application key from processing further requests. 
        Disabled keys may be reenabled via the My Account section on the IG Web Dealing Platform."""
        response = requests.put(self.BASE_URL + '/operations/application/disable', data=json.dumps({}), headers=self.LOGGED_IN_HEADERS)
        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'
        }

    ############ END ############
3 - Editez le fichier sample.py pour y mettre le contenu suivant:

Code : #

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

# IG API Trader

import igls,requests,json, time
from trading_ig_config import config

from ig_service_rest import IGService
#from ig_service_config import * # defines username, password, api_key, acc_type, acc_number
import pandas as (mot censuré merci de rester poli)



global position
position = {
    "epic": "IX.D.DAX.IMF.IP",
    "expiry": "-",
    "direction": "SELL",
    "size": "1",
    "orderType": "MARKET",
    "timeInForce": None,
    "level": None,
    "guaranteedStop": "false",
    "stopLevel": None,
    "stopDistance": "10",
    "trailingStop": None,
    "trailingStopIncrement": None,
    "forceOpen": "true",
    "limitLevel": None,
    "limitDistance": "10",
    "quoteId": None,
    "currencyCode": "EUR"
    }


if config.acc_type.upper() == "DEMO":
    BASEURL = 'https://demo-api.ig.com/gateway/deal'
elif config.acc_type.upper() == "LIVE":
    BASEURL = 'https://api.ig.com/gateway/deal'
else:
    raise(NotImplementedError("acc_type is %r but it should be either 'DEMO' or 'LIVE'" % config.acc_type))

headers = {
    'content-type': 'application/json; charset=UTF-8',
    'Accept': 'application/json; charset=UTF-8',
    'X-IG-API-KEY': config.api_key
}

payload = {
    'identifier': config.username,
    'password': config.password
}

# Tell the user when the Lighstreamer connection state changes
def on_state(state):
    print('New state:', state)
    igls.LOG.debug("New state: %s" % state)

    
'''
Fonction pour ajouter une forme à la liste des formes
'''
def AddShape(shape, ratio, list):
    ratio = RestricRatio(ratio)
    ratio = round(ratio, 2)
    if ratio == 99:
        print list
    if list == []:
        list.append([shape, ratio])
    elif list[-1] <> [shape, ratio]:
        list.append([shape, ratio])
    return list

'''
Fonction pour identifier le ratio de la mèche selon un découpage Fibonacci
'''
def RestricRatio(ratio):
    #23,6%, 38,2%, 50%, 61,8% et 78,6%.
    new_ratio = 0
    if ratio == 0:
        new_ratio = 0
    elif ratio > 0 and ratio <= 0.236:
        new_ratio = 1
    elif ratio > 0.236 and ratio <= 0.382:
        new_ratio = 2
    elif ratio > 0.382 and ratio <= 0.5:
        new_ratio = 3
    elif ratio > 0.5 and ratio <= 0.618:
        new_ratio = 4
    elif ratio > 0.618 and ratio <= 0.786:
        new_ratio = 5
    elif ratio > 0.786 and ratio <=1 :
        new_ratio = 10
    elif ratio > 1:
        new_ratio = 20
    else :
        new_ratio = 99
    return new_ratio
    
# Process a lighstreamer price update
def processPriceUpdate(item, myUpdateField):
    ig_service.LOGGED_IN_HEADERS['version'] = '2'
    
    #Affichage du nouveau tick
    curr_time = "2015-09-09 " + myUpdateField[0]
    curr_time = time.strptime(curr_time, "%Y-%m-%d %H:%M:%S")
    buy_price = float(myUpdateField[2])
    sell_price = float(myUpdateField[1])
    curr_price = (buy_price - sell_price)/2 + sell_price

    print 'time:', time.strftime("%H:%M:%S", curr_time),': ',  str(curr_price)

    #Ouverture d'une position SELL
    position['direction'] = "SELL"
    r2 = requests.post(ig_service.BASE_URL + '/positions/otc', data=json.dumps(position), headers=ig_service.LOGGED_IN_HEADERS)
    r3 = ig_service.fetch_open_positions()
    deal_id = r3['positions'][0]['position']['dealId']
    print "Position ouverte avec le deal ID :", deal_id 
    
    #fermeture d'une position
    close_sens = "BUY"
    ig_service.close_open_position( deal_id , close_sens, None, None, None, "MARKET", None, "1")


# Process an update of the users trading account balance
def processBalanceUpdate(item, myUpdateField):
    print("balance update = %s" % myUpdateField)

if __name__ == '__main__':
    '''
    STREAM SESSION
    '''
    url = BASEURL + "/session"
    r = requests.post(url, data=json.dumps(payload), headers=headers, verify=False)
    print "r:", r
    cst = r.headers['CST']
    xsecuritytoken = r.headers['x-security-token']
    fullheaders = {
        'content-type': 'application/json; charset=UTF-8',
        'Accept': 'application/json; charset=UTF-8',
        'X-IG-API-KEY': config.api_key,
        'CST': cst,
        'X-SECURITY-TOKEN': xsecuritytoken
    }

    body = r.json()
    lightstreamerEndpoint = body[u'lightstreamerEndpoint']
    clientId = body[u'clientId']
    accounts = body[u'accounts']

    # Depending on how many accounts you have with IG the '0' may need to change to select the correct one (spread bet, cfd à risque limité account etc)
    accountId = accounts[0][u'accountId']

    client = igls.LsClient(lightstreamerEndpoint+"/lightstreamer/")
    client.on_state.listen(on_state)
    client.create_session_stream(username=accountId, password='CST-'+cst+'|XST-'+xsecuritytoken, adapter_set='')

    priceTable = igls.Table(client,
        mode=igls.MODE_MERGE,
        item_ids='L1:IX.D.DAX.IMF.IP',#'L1:CS.D.GBPUSD.cfd à risque limité.IP',
        schema='UPDATE_TIME BID OFFER CHANGE MARKET_STATE',
        item_factory=lambda row: tuple(float(v) for v in row)
    )

    priceTable.on_update.listen(processPriceUpdate)
    '''
    REST SESSION
    '''
    ig_service = IGService(config.username, config.password, config.api_key, config.acc_type)
    print ig_service.create_session()

    balanceTable = igls.Table(client,
        mode=igls.MODE_MERGE,
        item_ids='ACCOUNT:'+accountId,
        schema='AVAILABLE_CASH',
        item_factory=lambda row: tuple(string(v) for v in row))

    balanceTable.on_update.listen(processBalanceUpdate)


    while True:
        time.sleep(10)
 
Positionnez votre stratégie dans la fonction processPriceUpdate

Identifiants
Enfin, entrez vos identifants dans le fichier trading_ig_config.py

Lancez le tout avec la commande suivante:

Code : #

python sample.py
Voila voila comment je m'en suis sorti, j'espère que ça pourra en aider certains.

Re: [PYTHON] Script API IG STREAM + REST

par Benoist Rousseau » 14 sept. 2015 22:13

Merci à toi juste préciser que le forum remplace des mots automatiquement pour éviter les injures donc ça a modifier le code

Il faut regarder le code et quand il y a
import pandas as (mot censuré merci de rester poli)

ce mot est sans Astérix p*d*

Re: [PYTHON] Script API IG STREAM + REST

par cimourdain » 15 sept. 2015 10:05

merci j'avais pas vu, j'ai rajouté la remarque dans mon premier post

Re: [PYTHON] Script API IG STREAM + REST

par Benoist Rousseau » 16 sept. 2015 00:11

j'ai vérifié ton code, je suis tombé dessus ;)

Re: [PYTHON] Script API IG STREAM + REST

par FemtoTrader » 30 sept. 2015 09:51

Bonjour,

ça fait toujours plaisir de savoir que mon code est utilisé par des francophones !

N'hésitez pas à contribuer sur GitHub.

Pour l'API REST
https://github.com/femtotrader/ig-markets-rest-api-python-library

Pour l'API Stream
https://github.com/femtotrader/ig-markets-stream-api-python-library

Cordialement

Re: [PYTHON] Script API IG STREAM + REST

par falex » 30 sept. 2015 10:24

Tiens te voilà !

Re: [PYTHON] Script API IG STREAM + REST

par beni » 30 sept. 2015 10:26

Salut femto !

Il est même largement utilisé ! En tout cas par moi :mrgreen:

Un grand merci à toi, sans ça je crois que je ne me serai jamais lancé :mercichinois:

Re: [PYTHON] Script API IG STREAM + REST

par falex » 30 sept. 2015 10:37

Suite à la correction d'IGLS sur le flux Ls en mode "silent", je n'ai jamais réussi avec les serveurs d'ig. Chaque fois je n'avais aucun ticks, donc soit j'ai crien compris au fonctionnement du silent, soit ...

Re: [PYTHON] Script API IG STREAM + REST

par FemtoTrader » 30 sept. 2015 10:41

N'hésitez pas à améliorer les 2 bibliothèques

Elles sont désormais dans une organisation

https://github.com/ig-python

dont le but est d'utiliser l'API REST et Stream de ig à l'aide du langage Python

https://github.com/ig-python/ig-markets-rest-api-python-library
https://github.com/ig-python/ig-markets-stream-api-python-library


Forkez, faites une branche qui permet d'améliorer ou corriger une seule chose
Faites des Pull Request pour pouvoir fusionner ces améliorations.
Une fois la modif fusionnée vous pouvez supprimer la branche.
et synchoniser votre fork.

ça sera quand même beaucoup plus clair que faire des modif dans un forum.

Si vous ne connaissez pas trop git je peux aider.

La bibliothèque Stream pour l'instant ne tourne qu'avec Python 2 pas Python 3
c'est un point qu'il faut aussi améliorer.
https://github.com/Weswit/Lightstreamer-example-StockList-client-python permet désormais de
tourner avec Python 3 (depuis quelques jours) donc on doit pouvoir identifier ce qui coince

Est-ce que ceux qui ont trouvé des choses à faire sur les 2 libs peuvent les résumer ici et me donner leur nom d'utilisateur GitHub ?

Merci

Re: [PYTHON] Script API IG STREAM + REST

par falex » 30 sept. 2015 10:53

Avec béni on a un poil changé igks pour remonter une information supplémentaire à chaque message reçu : l'epic.

Typiquement dans la L3 si tu as des positions sur plusieurs sous-jacent, il était impossible de savoir quel ticks appartient à qui.

Nouveau code :

Code : #

def _dispatch_update(self, item_id, item):
        """Called by LsClient to dispatch a table update line."""
        if item == 'EOS':
            self.on_end_of_snapshot.fire()
            return
        last = dict(enumerate(self._last_item_map.get(item_id, [])))
        fields = [_decode_field(s, last.get(i)) for i, s in enumerate(item)]
        self._last_item_map[item_id] = fields
        #self.items[item_id] = self.item_factory(fields)
        #self.on_update.fire(item_id, self.items[item_id])


        self.on_update.fire(item_id, fields, self.item_ids)
Seule la dernière ligne a été modifié avec l'ajout d'un champ self.item_ids dans le retour

Re: [PYTHON] Script API IG STREAM + REST

par FemtoTrader » 30 sept. 2015 10:56

ok

tu connais Git, GitHub ?

Re: [PYTHON] Script API IG STREAM + REST

par falex » 30 sept. 2015 10:59

Oui ... et non.

Je te donne ma modification et tu peux faire un update si tu veux. Je n'utiliserais pas github trop compliqué dans mon cas.

Re: [PYTHON] Script API IG STREAM + REST

par FemtoTrader » 30 sept. 2015 11:22

ça n''est pas compliqué du tout ! (enfin pas aussi compliqué qu'on ne le pense)

On peut faire ça par l'interface web
(surtout si tu n'as qu'un fichier à modifier)

Va sur le projet
https://github.com/ig-python/ig-markets-stream-api-python-library

Clique sur fork
Ta version du projet est sur
https://github.com/ton_username/ig-markets-stream-api-python-library

Reviens sur
https://github.com/ig-python/ig-markets-stream-api-python-library

Va sur le fichier à modifier

Clique sur edit ("Edit the file in your fork of this project")

Colle ton code.

ça permet du coup d'identifier rapidement la différence entre le code d'origine
et la modif (diff)

Tu valide en allant sur
"Create a new branch for this commit and start a pull request"
Donne un nom à la branche
et voilà...

Re: [PYTHON] Script API IG STREAM + REST

par FemtoTrader » 30 sept. 2015 11:40

Tu peux ensuite aller dans ton projet.
Choisir ta nouvelle branche. Editer le code, ...
ton Pull Request sur le projet upstream sera automatiquement mis à jour
avec tes nouveaux commits

Re: [PYTHON] Script API IG STREAM + REST

par FemtoTrader » 30 sept. 2015 11:59

Il ne doit pas y avoir que

self.on_update.fire(item_id, fields)

qui devient

self.on_update.fire(item_id, fields, self.item_ids)

car si je fais cette modif je n'ai plus la MAJ des ticks

Re: [PYTHON] Script API IG STREAM + REST

par falex » 30 sept. 2015 12:09

heu attend j'ai du aussi modifier l'appel je regarde (dans mon main.py)

Ah bizarre. Justement je n'ai pas modifié l'appel de la fonction.

Dans la L3 souscription à une table est la même depuis le début :

Code : #

# Binding sur les differents flux de stream et fonctions associés
        priceTable = igls.Table(client,
                                mode=igls.MODE_MERGE,
                                item_ids='MARKET:%s' % personal.epic,
                                schema="OFFER BID",
                                )

priceTable.on_update.listen(events.process_price_update)
Avec la modification je renvoi simplement item_ids comme champs supplémentaire de retour en plus du Offer et du Bid.

Si tu fais un "print" bestiale de chaque message reçu ça passe ?

---

La fonction qui est bindé avec la table reçoit 3 arguments au lieux de 2 :

Code : #

def process_price_update(item, myUpdateField, item_ids):
---

Je viens de faire un "compare" entre igls utilisé au début et la dernière mouture, y'a bien jsute le champ self.item_ids qui a bougé, tout le reste est 100% identique.

Re: [PYTHON] Script API IG STREAM + REST

par FemtoTrader » 30 sept. 2015 13:03

Hum ... il y a quand même un coac

si tu t'abonne à plusieurs flux (GBPUSD et USDJPY par exemple)

Code : #

    priceTable = igls.Table(client,
        mode=igls.MODE_MERGE,
        item_ids='L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP',
        schema='UPDATE_TIME BID OFFER CHANGE MARKET_STATE',
        item_factory=lambda row: tuple(float(v) for v in row)
    )

    priceTable.on_update.listen(process_price_update)
je m'attendais à récupérer aussi l'epic dans process_price_update.

comment faire en sorte que process_price_update reçoive les prix de plusieurs actifs
ainsi que l'epic en question?

par ce que je me retrouve avec

Code : #

(py2)pc:ig-markets-stream-api-python-library femto$ python sample.py
('New state:', 'connecting Lightstreamer session')
('New state:', 'connected Lightstreamer session')
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:33', u'120.283', u'120.291', u'0.550', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:15', u'1.52037', u'1.52057', u'0.00550', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:34', u'1.52041', u'1.52051', u'0.00549', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:35', u'1.52042', u'1.52052', u'0.00550', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:36', u'1.52037', u'1.52057', u'0.00550', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:36', u'1.52041', u'1.52051', u'0.00549', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:37', u'120.284', u'120.292', u'0.551', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:37', u'120.285', u'120.293', u'0.552', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:37', u'120.287', u'120.295', u'0.554', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:37', u'120.288', u'120.296', u'0.555', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:37', u'120.289', u'120.297', u'0.556', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:39', u'120.290', u'120.298', u'0.557', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:39', u'1.52037', u'1.52057', u'0.00550', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:39', u'120.289', u'120.297', u'0.556', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:41', u'120.288', u'120.296', u'0.555', u'TRADEABLE']
price update for L1:CS.D.GBPUSD.cfd à risque limité.IP L1:CS.D.USDJPY.cfd à risque limité.IP= [u'11:53:41', u'120.289', u'120.297', u'0.556', u'TRADEABLE']
quand je fais

Code : #

def process_price_update(item, myUpdateField, item_ids):
    print("price update for %s= %s" % (item_ids, myUpdateField))
je voudrais que tout arrive dans la même fonction pour ensuite utiliser un système de messaging interne (RabbitMQ par exemple) mais pour ça j'ai besoin du prix et de l'epic.

Re: [PYTHON] Script API IG STREAM + REST

par falex » 30 sept. 2015 13:10

Oulà attend, tu es sur que tu peux souscrire à deux flux de prix avec cette syntaxe ?
J'ai pas trouvé comment avoir plusieurs epic dans le mêms flux LS.

D'autre part, il ne me semble pas pertinent de sosucrire ainsi, la fréquence des ticks étant "propre" à chaque sous-jacent. Qu'est censé te renvoyer ig si tu as un tick pour GBPUSD et pas de tick au même moment pour USDJPY ???

Pour chaque epic, je lance une souscription de table, comme décrit précedement.

---

Comme je souscrit à un seul epic par Table,
J'extrait la droit de L1: pour avoir l'epic correspondant.
J'ai aussi codé le cas où le mot clef serait MARKET ou CHART

...

En étudiant ton cas d'usage, ça confirme bien ce que je pense, ce n'est pas forcément pertinent de souscrire à plusieurs epic dans le même flux, à cause des problèmes d'identifications de l'epic

Re: [PYTHON] Script API IG STREAM + REST

par FemtoTrader » 30 sept. 2015 13:32

Si tu as un tick pour GBPUSD ça lance la fonction (même si tu n'a pas de tick pour USDJPY)

Le problème c'est comment faire en sorte de récupérer dans une seule fonction les prix et l'epic ???

Re: [PYTHON] Script API IG STREAM + REST

par Nomade » 30 sept. 2015 14:15

Si je comprend ce que tu veux faire :

avec plusieurs souscriptions en ecoutant chacune, par ex dans main de L3:

Code : #

        priceTable = igls.Table(globalvar.client_ls,
                                mode=igls.MODE_MERGE,
                                item_ids='MARKET:CS.D.GBPUSD.cfd à risque limité.IP ',
                                schema="OFFER BID",
                                )
        priceTable2 = igls.Table(globalvar.client_ls,
                                mode=igls.MODE_MERGE,
                                item_ids='MARKET:CS.D.USDJPY.cfd à risque limité.IP',
                                schema="OFFER BID",
                                )
        priceTable.on_update.listen(events.process_price_update)
        priceTable2.on_update.listen(events.process_price_update)
avec dans process_price_update:

Code : #

    print(item, myUpdateField, item_ids)
tu as en output
(1, [u'1.51771', u'1.51761'], 'MARKET:CS.D.GBPUSD.cfd à risque limité.IP ')
(1, [u'1.51772', u'1.51762'], 'MARKET:CS.D.GBPUSD.cfd à risque limité.IP ')
(1, [u'120.244', u'120.236'], 'MARKET:CS.D.USDJPY.cfd à risque limité.IP')
(1, [u'1.51773', u'1.51763'], 'MARKET:CS.D.GBPUSD.cfd à risque limité.IP ')
(1, [u'120.241', u'120.233'], 'MARKET:CS.D.USDJPY.cfd à risque limité.IP')
(1, [u'1.51774', u'1.51764'], 'MARKET:CS.D.GBPUSD.cfd à risque limité.IP ')

Sujets similaires
API REST / STREAM
Fichier(s) joint(s) par FemtoTrader » 22 sept. 2014 10:35 (178 Réponses)
ig rest api - heure des ouvertures et clotures quotidiennes
par falex » 22 avr. 2015 14:50 (3 Réponses)
IG API : Augmentation du nombre de requet REST
par falex » 04 août 2016 10:31 (9 Réponses)
Aide pour API REST IG
par DarkPoule » 16 mai 2021 00:36 (46 Réponses)
Récupérer les cours avec l'API IG Market et Python
par Amarantine » 24 juil. 2016 12:09 (55 Réponses)
Des API pout télécharger les données en python
par hamza123 » 19 mai 2017 10:18 (2 Réponses)
API IB TWS comment stream flux future Dax
par shuraver » 28 mai 2020 22:07 (3 Réponses)
REST , maj
par brucy » 21 août 2015 18:23 (1 Réponses)
Andlil 100% sans mouchard publicitaire ou script de suivi
Fichier(s) joint(s) par frigolite » 19 avr. 2015 14:19 (6 Réponses)
Script logiciel pour automatiser des actions sur le web
par Uncharted » 17 nov. 2015 23:36 (19 Réponses)