Как использовать Python и Ally API для торговли акциями

Пришло время автоматизировать?

Фондовый рынок остается горячим, даже после небольшой коррекции в марте. Согласно исследованию, проведенному SentimenTrader.com, активность розничных трейдеров снизилась с пикового значения в январе. Однако поведение мелких трейдеров (торгует 10 контрактами или меньше) по-прежнему более агрессивно, чем за последние 20 лет.

В последнее время я изучаю, как автоматизировать некоторые из моих стратегий торговли акциями. У одного из брокеров, которые я использую, Ally Financial, есть API, который довольно прост и может использоваться для некоторой алгоритмической торговли. Если вы никогда раньше не использовали Ally, у меня есть краткая шпаргалка, чтобы вы начали получать данные о ценах с помощью API. В этой статье я добавлю к этому шпаргалку и объясню, как собирать новости, а также размещать и отменять заказы на акции с помощью Python.

Управляйте своими ключами API

Ключи API необходимы для взаимодействия с Ally Financial API. Чтобы получить ключи API и разработать с помощью Ally Financial API, следуйте их инструкциям по началу работы здесь:



Если вы новичок в управлении ключами API, обязательно сохраните их в файл или базу данных, а не кодируйте жестко. В этом примере я сохраню свои ключи в файле с именем config.py. Ключи API могут быть очень ценными и должны быть защищены. Если вы обеспокоены утечкой вашего ключа, Ally Financial позволяет вам восстановить новый. Если вы используете файл config.py, a добавьте файл конфигурации в свой файл gitignore, чтобы предотвратить его отправку и в ваше хранилище!

Импортировать зависимости

Для подключения к API используется OAuth1. Можно передать учетные данные как часть URL-адреса при выполнении запроса, но я предпочитаю использовать библиотеку requests_oauthlib.

При необходимости используйте pip install requests-oauthlib.

Обратите внимание, что я использую файл конфигурации для импорта api_key, secret, oath_token, oath_secret. Таким образом, мне не нужно включать эти личные токены в код.

from requests_oauthlib import OAuth1
import requests
from config import api_key, secret, oath_token, oath_secret
#authentication 
auth = OAuth1(api_key, secret, oath_token, oath_secret)

Я передаю импортированные токены в OAuth1. Это будет использоваться, когда мы сделаем запрос к API.

Сбор новостей рынка

Сбор новостей рынка может быть полезен для таких действий, как анализ настроений. У Ally Financial API есть две конечные точки API для новостей. Конечная точка поиска используется для поиска идентификаторов статей и возвращает заголовок статьи. Конечная точка новостей позволяет ввести идентификатор статьи и возвращает заголовок и статью. Доступные статьи имеют максимальный возраст 180 дней.

Документация по конечным точкам поиска
Документация по конечным точкам новостей

#the search endpoint
v1/market/news/search.json?
#the news endpoint. 
v1/market/news/{article ID}.json

Я создал простую функцию, которая использует конечную точку поиска для сбора идентификаторов статей, а затем использует цикл for для вызова конечной точки новостей с каждым идентификатором, возвращаемым из поиска. Функция возвращает заголовок, идентификатор и историю статьи.

def get_news(sym, maxhits):
    baseurl = 'https://devapi.invest.ally.com'
    maxhits = str(maxhits)
    stories = []
    url =f'{baseurl}/v1/market/news/search.json?symbols={sym}&maxhits={maxhits}'
    
    response = requests.get(url, auth = auth).json()
    ids = [response['response']['articles']['article'][n]['id'] 
           for n in range(0, len(response['response']['articles']['article']))]
    
    for v in ids:
        url = f'{baseurl}/v1/market/news/{v}.json'
        response = requests.get(url, auth = auth).json()
        stories.append(response['response']['article'])
    return stories
get_news('spy', 5)

Обратите внимание, что функция принимает два параметра: sym и maxhits. Введите тикер акции как символ. Введите максимальное количество результатов, которые вы хотите вернуть, в параметре maxhits. Например, get_news (‘spy’, 5) вернет до 5 статей о биржевом символе SPY.

Создание и отмена приказов на покупку или продажу акций

Для размещения ордеров на покупку, продажу и короткие продажи требуется только одна функция. Это потому, что все заказы публикуются с использованием конечной точки accounts /: id / orders.xml.

Торговая документация Ally API
Документация конечной точки ордеров

При покупке и продаже акций через API информацию о заказе необходимо подавать в формате FIXML. Протокол Financial Information eXchange (FIX) - это бесплатный и открытый стандарт, поддерживаемый тысячами компаний.

FIX стал языком глобальных финансовых рынков, который широко используется фирмами, занимающимися покупкой и продажей, торговыми платформами и даже регулирующими органами для передачи торговой информации.

- http://www.fixprotocol.org:

При вызове API FIXML передается в теле запроса. По сути, это XML. Поскольку для полезных данных запроса требуется FIXML, URI должен использовать формат XML вместо JSON.

URL=f"https://devapi.invest.ally.com/v1/accounts/{acct}/orders.xml"

Ниже приводится пример FIXML для заявки на покупку акций.

<FIXML xmlns="http://www.fixprotocol.org/FIXML-5-0-SP2">
  <Order TmInForce="1" Typ="2" Side="1" Px="13.39" Acct="60534810">
    <Instrmt SecTyp="CS" Sym="GE"/>
    <OrdQty Qty="1"/>
  </Order>
</FIXML>

Обратите внимание, что в заказе есть несколько атрибутов. Атрибуты соответствуют параметрам заказа, таким как цена, количество и тип заказа. Полный список атрибутов FIXML, используемых с Ally, можно найти в документации.

Функция создания заказа на акции

Чтобы справиться с покупкой акций, создайте функцию, которая принимает переменные для атрибутов FIXML. В FXML необходимо настроить семь атрибутов:

def create_stock_order(sym, typ, side, tif, price, acct, qty):

Sym: символ (тикер) ценной бумаги.

Тип: тип цены. Значения включают Market: 1, Limit: 2, ​​Stop: 3, Stop Limit: 4.

Сторона: Сторона рынка. Ордера на покупку для покрытия приписываются ордерам на покупку со стороной = «1». Значения включают Купить: 1, Продать: 2, ​​Продать: 5.

TmInForce: Time in Force контролирует продолжительность заказа. Значения включают дневной ордер: 0, GTC Order: 1, рыночный при закрытии: 7.

Px: цена ценной бумаги, если она необходима для выбранного Typ. Например, Px потребуется для лимитов (Тип = «2») или стоп-ограничений (Тип = «4»).

Acct: номер вашего счета. Это также необходимо в URL-адресе.

Кол-во: желаемое количество акций или контрактов.

Примечание. Поскольку функция покупает только акции, атрибут SecTyp не нужно настраивать . SecTyp: Тип безопасности. Этот атрибут обязателен. Значения включают обыкновенные акции: CS, Option: OPT.

def create_stock_order(sym, typ, side, tif, price, acct, qty):
    '''
    FIXML KEY
    Typ:  Market: "1" 
          Limit: "2" 
          Stop: "3" 
          Stop Limit: "4"
    
    Side: Buy: "1" 
          Sell: "2" 
          Sell Short: "5" 
   
    tif:  Day Order: "0" 
          GTC Order: "1" 
          Market on Close: "7" 
    '''
        url=f"https://devapi.invest.ally.com/v1/accounts/{acct}/orders.xml"
xmlns = 'xmlns="http://www.fixprotocol.org/FIXML-5-0-SP2"'
    
    order = f'TmInForce="{tif}" Typ="{typ}" Side="{side}" Px="{price}" Acct="{acct}"'
instrmt = f'SecTyp="CS" Sym="{sym}"'
    orderQty = f'Qty="{qty}"'
payload = f"<FIXML {xmlns}>\r\n <Order {order}>\r\n <Instrmt {instrmt}/>\r\n <OrdQty {orderQty}/>\r\n  </Order> \r\n </FIXML>"
headers = {
      'TKI_OVERRIDE': 'true',
      'Content-Type': 'application/xml',
     }
response = requests.post(url,auth = auth,  headers=headers, data = payload)
 
    f = xmltodict.parse(response.text.encode('utf8'))['response']['clientorderid']
    
    #orderids.append(f)
return f

Важно понимать, что эта функция разместит живой заказ. БУДЬТЕ ОСТОРОЖНЫ! Например, если бы я хотел купить 1 акцию GE за 1 доллар, функция выглядела бы так:

create_stock_order("GE", 2, 1,1, "1.00", 123456789, 1)

Функция возвращает Client OrderID. Поскольку запрос отправляется в формате XML, ответ возвращается в формате XML. Для анализа XML-ответа я использую библиотеку xmltodict. Это делает работу с XML более похожей на работу с JSON.

При необходимости используйте pip для установки библиотеки.

pip install xmltodict

Чтобы отслеживать идентификаторы заказов, добавьте их в список или сохраните в базе данных. Чтобы убедиться, что заказ был выполнен, вы можете проверить раздел Статус заказов на панели инструментов союзника. Или используйте конечную точку accounts /: id / orders API.

Отмена заказов

Подобно созданию заказов, отмена заказов также выполняется с помощью FIXML.

<FIXML xmlns="http://www.fixprotocol.org/FIXML-5-0-SP2">
  <OrdCxlReq TmInForce="0" Typ="2" Side="1" OrigID="SVI-12345678" Acct="12345678">
    <Instrmt SecTyp="CS" Sym="F"/>
    <OrdQty Qty="1"/>
  </OrdCxlReq>
</FIXML>

Обратите внимание, что в полезной нагрузке указано OrdCxlReq вместо Order. Вместо цены передайте идентификатор заказа в атрибут OrigID.

def can_stock_order(sym, typ, side, tif, acct, qty, orderids):
    '''
    FIXML KEY
    Typ:  Market: "1" 
          Limit: "2" 
          Stop: "3" 
          Stop Limit: "4"
    
    Side: Buy: "1" 
          Sell: "2" 
          Sell Short: "5" ‐ 
   
    tif:  Day Order: "0" 
          GTC Order: "1" 
          Market on Close: "7" 
    
    '''
url = f"https://devapi.invest.ally.com/v1/accounts/{acct}/orders.xml"
xmlns = 'xmlns="http://www.fixprotocol.org/FIXML-5-0-SP2"'
    order = f'TmInForce="{tif}" Typ="{typ}" Side="{side}" OrigID="{orderids}" Acct="{acct}"'
    instrmt = f'SecTyp="CS" Sym="{sym}"'
    orderQty = f'Qty="{qty}"'
payload = f"<FIXML {xmlns}>\r\n <OrdCxlReq {order}>\r\n <Instrmt {instrmt}/>\r\n <OrdQty {orderQty}/>\r\n  </OrdCxlReq> \r\n </FIXML>"
headers = {
          'TKI_OVERRIDE': 'true',
          'Content-Type': 'application/xml',
         }
response = requests.post(url,auth = auth,  headers=headers, data = payload)
    f = xmltodict.parse(response.text.encode('utf8'))['response']
    
return f

Функции «Создать заказ» и «Отменить заказ» практически идентичны. Функция возвращает словарь, содержащий информацию об успешности отмены заказа. Использовать функцию очень просто:

can_stock_order("GE", 2, 1,1, 123456789, 1, 'SVI-123456789')

Полный код

Я добавил эти функции в свою оригинальную Шпаргалку по Ally API, доступную на Github.com. Теперь вы готовы собирать рыночные котировки, выполнять заказы и искать новости рынка с помощью Ally API! С лучшим пониманием того, как создавать и отменять ордера, легче перейти к таким вещам, как алгоритмическая торговля.



Спасибо!

- Эрик Клеппен