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,Code : #
def create_session_stream(self, username, adapter_set, password=None,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 ############
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)
 Identifiants
Enfin, entrez vos identifants dans le fichier trading_ig_config.py
Lancez le tout avec la commande suivante:
Code : #
python sample.py




 
 
