├── .editorconfig ├── .github └── workflows │ ├── code_quality_checks.yml │ ├── docs_publish.yml │ ├── pypi_publish.yml │ └── test_pypi_publish.yml ├── .gitignore ├── AUTHORS.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ctrader_open_api ├── __init__.py ├── auth.py ├── client.py ├── endpoints.py ├── factory.py ├── messages │ ├── OpenApiCommonMessages_pb2.py │ ├── OpenApiCommonModelMessages_pb2.py │ ├── OpenApiMessages_pb2.py │ ├── OpenApiModelMessages_pb2.py │ └── __init__.py ├── protobuf.py └── tcpProtocol.py ├── docs ├── authentication.md ├── client.md ├── css │ └── extra.css ├── fonts │ ├── TitilliumWeb-Black.ttf │ ├── TitilliumWeb-Bold.ttf │ ├── TitilliumWeb-BoldItalic.ttf │ ├── TitilliumWeb-ExtraLight.ttf │ ├── TitilliumWeb-ExtraLightItalic.ttf │ ├── TitilliumWeb-Italic.ttf │ ├── TitilliumWeb-Light.ttf │ ├── TitilliumWeb-LightItalic.ttf │ ├── TitilliumWeb-Regular.ttf │ ├── TitilliumWeb-SemiBold.ttf │ └── TitilliumWeb-SemiBoldItalic.ttf ├── img │ ├── favicon.ico │ └── logo.png ├── index.md └── js │ └── extra.js ├── mkdocs.yml ├── overrides └── main.html ├── poetry.lock ├── poetry.toml ├── pyproject.toml └── samples ├── ConsoleSample ├── README.md └── main.py ├── KleinWebAppSample ├── README.md ├── credentials.json ├── css │ └── site.css ├── js │ └── site.js ├── main.py ├── markup │ ├── add_accounts.xml │ └── client_area.xml ├── screenshot.png └── templates.py └── jupyter ├── README.md ├── credentials.json └── main.ipynb /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{py,rst,ini}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.{html,css,scss,json,yml}] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.github/workflows/code_quality_checks.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Code Quality Checks 3 | 4 | on: 5 | push: 6 | branches: [main, dev] 7 | pull_request: 8 | branches: [main, dev] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 3.8 18 | uses: actions/setup-python@v1 19 | with: 20 | python-version: 3.8 21 | - name: Install poetry 22 | run: pip install poetry 23 | - name: Install dependencies 24 | run: poetry update 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/docs_publish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Docs publish 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.9 17 | - run: pip install mkdocs-material 18 | - run: pip install mkdocs-minify-plugin 19 | - run: mkdocs gh-deploy --force 20 | -------------------------------------------------------------------------------- /.github/workflows/pypi_publish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: PyPI publish 3 | 4 | on: 5 | release: 6 | types: [released] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Set up Python 3.9 16 | uses: actions/setup-python@v1 17 | with: 18 | python-version: 3.9 19 | - name: Install poetry 20 | run: pip install poetry 21 | - name: Bump version number 22 | run: poetry version ${{ github.event.release.tag_name }} 23 | - name: Build package 24 | run: poetry build 25 | - name: Publish package 26 | run: poetry publish -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} 27 | -------------------------------------------------------------------------------- /.github/workflows/test_pypi_publish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test PyPI publish 3 | 4 | on: 5 | release: 6 | types: [prereleased] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Set up Python 3.9 16 | uses: actions/setup-python@v1 17 | with: 18 | python-version: 3.9 19 | - name: Install poetry 20 | run: pip install poetry 21 | - name: Bump version number 22 | run: poetry version ${{ github.event.release.tag_name }} 23 | - name: Build package 24 | run: poetry build 25 | - name: Publish package 26 | run: poetry publish -r testpypi -u ${{ secrets.TEST_PYPI_USERNAME }} -p ${{ secrets.TEST_PYPI_PASSWORD }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | /.vs 131 | /samples/ConsoleSample/ConsoleSample-dev.py 132 | /samples/KleinWebAppSample/credentials-dev.json 133 | /samples/jupyter/credentials-dev.json 134 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | 4 | ## Development Lead 5 | 6 | * Spotware 7 | 8 | ## Contributors 9 | 10 | None yet. Why not be the first? 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome, and they are greatly appreciated! Every little bit 4 | helps, and credit will always be given. 5 | 6 | You can contribute in many ways: 7 | 8 | ## Types of Contributions 9 | 10 | ### Report Bugs 11 | 12 | Report bugs at https://github.com/spotware/spotware_openApiPy/issues. 13 | 14 | If you are reporting a bug, please include: 15 | 16 | * Your operating system name and version. 17 | * Any details about your local setup that might be helpful in troubleshooting. 18 | * Detailed steps to reproduce the bug. 19 | 20 | ### Fix Bugs 21 | 22 | Look through the GitHub issues for bugs. Anything tagged with "bug" and "help 23 | wanted" is open to whoever wants to implement it. 24 | 25 | ### Implement Features 26 | 27 | Look through the GitHub issues for features. Anything tagged with "enhancement" 28 | and "help wanted" is open to whoever wants to implement it. 29 | 30 | ### Write Documentation 31 | 32 | OpenApiPy could always use more documentation, whether as part of the 33 | official OpenApiPy docs, in docstrings, or even on the web in blog posts, 34 | articles, and such. 35 | 36 | ### Submit Feedback 37 | 38 | The best way to send feedback is to file an issue at https://github.com/spotware/spotware_openApiPy/issues. 39 | 40 | If you are proposing a feature: 41 | 42 | * Explain in detail how it would work. 43 | * Keep the scope as narrow as possible, to make it easier to implement. 44 | * Remember that this is a volunteer-driven project, and that contributions 45 | are welcome :) 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021, Spotware 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenApiPy 2 | 3 | 4 | [![PyPI version](https://badge.fury.io/py/ctrader-open-api.svg)](https://badge.fury.io/py/ctrader-open-api) 5 | ![versions](https://img.shields.io/pypi/pyversions/ctrader-open-api.svg) 6 | [![GitHub license](https://img.shields.io/github/license/spotware/OpenApiPy.svg)](https://github.com/spotware/OpenApiPy/blob/main/LICENSE) 7 | 8 | A Python package for interacting with cTrader Open API. 9 | 10 | This package uses Twisted and it works asynchronously. 11 | 12 | - Free software: MIT 13 | - Documentation: https://spotware.github.io/OpenApiPy/. 14 | 15 | 16 | ## Features 17 | 18 | * Works asynchronously by using Twisted 19 | 20 | * Methods return Twisted deferreds 21 | 22 | * It contains the Open API messages files so you don't have to do the compilation 23 | 24 | * Makes handling request responses easy by using Twisted deferreds 25 | 26 | ## Insallation 27 | 28 | ``` 29 | pip install ctrader-open-api 30 | ``` 31 | 32 | # Usage 33 | 34 | ```python 35 | 36 | from ctrader_open_api import Client, Protobuf, TcpProtocol, Auth, EndPoints 37 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import * 38 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import * 39 | from ctrader_open_api.messages.OpenApiMessages_pb2 import * 40 | from ctrader_open_api.messages.OpenApiModelMessages_pb2 import * 41 | from twisted.internet import reactor 42 | 43 | hostType = input("Host (Live/Demo): ") 44 | host = EndPoints.PROTOBUF_LIVE_HOST if hostType.lower() == "live" else EndPoints.PROTOBUF_DEMO_HOST 45 | client = Client(host, EndPoints.PROTOBUF_PORT, TcpProtocol) 46 | 47 | def onError(failure): # Call back for errors 48 | print("Message Error: ", failure) 49 | 50 | def connected(client): # Callback for client connection 51 | print("\nConnected") 52 | # Now we send a ProtoOAApplicationAuthReq 53 | request = ProtoOAApplicationAuthReq() 54 | request.clientId = "Your application Client ID" 55 | request.clientSecret = "Your application Client secret" 56 | # Client send method returns a Twisted deferred 57 | deferred = client.send(request) 58 | # You can use the returned Twisted deferred to attach callbacks 59 | # for getting message response or error backs for getting error if something went wrong 60 | # deferred.addCallbacks(onProtoOAApplicationAuthRes, onError) 61 | deferred.addErrback(onError) 62 | 63 | def disconnected(client, reason): # Callback for client disconnection 64 | print("\nDisconnected: ", reason) 65 | 66 | def onMessageReceived(client, message): # Callback for receiving all messages 67 | print("Message received: \n", Protobuf.extract(message)) 68 | 69 | # Setting optional client callbacks 70 | client.setConnectedCallback(connected) 71 | client.setDisconnectedCallback(disconnected) 72 | client.setMessageReceivedCallback(onMessageReceived) 73 | # Starting the client service 74 | client.startService() 75 | # Run Twisted reactor 76 | reactor.run() 77 | 78 | ``` 79 | 80 | Please check documentation or samples for a complete example. 81 | 82 | ## Dependencies 83 | 84 | * Twisted 85 | * Protobuf 86 | -------------------------------------------------------------------------------- /ctrader_open_api/__init__.py: -------------------------------------------------------------------------------- 1 | """Top-level package for Spotware OpenApiPy.""" 2 | from .client import Client 3 | from .protobuf import Protobuf 4 | from .tcpProtocol import TcpProtocol 5 | from .auth import Auth 6 | from .endpoints import EndPoints 7 | __author__ = """Spotware""" 8 | __email__ = 'connect@spotware.com' 9 | -------------------------------------------------------------------------------- /ctrader_open_api/auth.py: -------------------------------------------------------------------------------- 1 | from ctrader_open_api.endpoints import EndPoints 2 | import requests 3 | 4 | class Auth: 5 | def __init__(self, appClientId, appClientSecret, redirectUri): 6 | self.appClientId = appClientId 7 | self.appClientSecret = appClientSecret 8 | self.redirectUri = redirectUri 9 | def getAuthUri(self, scope = "trading", baseUri = EndPoints.AUTH_URI): 10 | return f"{baseUri}?client_id={self.appClientId}&redirect_uri={self.redirectUri}&scope={scope}" 11 | def getToken(self, authCode, baseUri = EndPoints.TOKEN_URI): 12 | request = requests.get(baseUri, params= 13 | {"grant_type": "authorization_code", 14 | "code": authCode, 15 | "redirect_uri": self.redirectUri, 16 | "client_id": self.appClientId, 17 | "client_secret": self.appClientSecret}) 18 | return request.json() 19 | def refreshToken(self, refreshToken, baseUri = EndPoints.TOKEN_URI): 20 | request = requests.get(baseUri, params= 21 | {"grant_type": "refresh_token", 22 | "refresh_token": refreshToken, 23 | "client_id": self.appClientId, 24 | "client_secret": self.appClientSecret}) 25 | return request.json() 26 | -------------------------------------------------------------------------------- /ctrader_open_api/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from twisted.internet.endpoints import clientFromString 4 | from twisted.application.internet import ClientService 5 | from ctrader_open_api.protobuf import Protobuf 6 | from ctrader_open_api.factory import Factory 7 | from twisted.internet import reactor, defer 8 | 9 | class Client(ClientService): 10 | def __init__(self, host, port, protocol, retryPolicy=None, clock=None, prepareConnection=None, numberOfMessagesToSendPerSecond=5): 11 | self._runningReactor = reactor 12 | self.numberOfMessagesToSendPerSecond = numberOfMessagesToSendPerSecond 13 | endpoint = clientFromString(self._runningReactor, f"ssl:{host}:{port}") 14 | factory = Factory.forProtocol(protocol, client=self) 15 | super().__init__(endpoint, factory, retryPolicy=retryPolicy, clock=clock, prepareConnection=prepareConnection) 16 | self._events = dict() 17 | self._responseDeferreds = dict() 18 | self.isConnected = False 19 | 20 | def startService(self): 21 | if self.running: 22 | return 23 | ClientService.startService(self) 24 | 25 | def stopService(self): 26 | if self.running and self.isConnected: 27 | ClientService.stopService(self) 28 | 29 | def _connected(self, protocol): 30 | self.isConnected = True 31 | if hasattr(self, "_connectedCallback"): 32 | self._connectedCallback(self) 33 | 34 | def _disconnected(self, reason): 35 | self.isConnected = False 36 | self._responseDeferreds.clear() 37 | if hasattr(self, "_disconnectedCallback"): 38 | self._disconnectedCallback(self, reason) 39 | 40 | def _received(self, message): 41 | if hasattr(self, "_messageReceivedCallback"): 42 | self._messageReceivedCallback(self, message) 43 | if (message.clientMsgId is not None and message.clientMsgId in self._responseDeferreds): 44 | responseDeferred = self._responseDeferreds[message.clientMsgId] 45 | self._responseDeferreds.pop(message.clientMsgId) 46 | responseDeferred.callback(message) 47 | 48 | def send(self, message, clientMsgId=None, responseTimeoutInSeconds=5, **params): 49 | if type(message) in [str, int]: 50 | message = Protobuf.get(message, **params) 51 | responseDeferred = defer.Deferred(self._cancelMessageDiferred) 52 | if clientMsgId is None: 53 | clientMsgId = str(id(responseDeferred)) 54 | if clientMsgId is not None: 55 | self._responseDeferreds[clientMsgId] = responseDeferred 56 | responseDeferred.addErrback(lambda failure: self._onResponseFailure(failure, clientMsgId)) 57 | responseDeferred.addTimeout(responseTimeoutInSeconds, self._runningReactor) 58 | protocolDiferred = self.whenConnected(failAfterFailures=1) 59 | protocolDiferred.addCallbacks(lambda protocol: protocol.send(message, clientMsgId=clientMsgId, isCanceled=lambda: clientMsgId not in self._responseDeferreds), responseDeferred.errback) 60 | return responseDeferred 61 | 62 | def setConnectedCallback(self, callback): 63 | self._connectedCallback = callback 64 | 65 | def setDisconnectedCallback(self, callback): 66 | self._disconnectedCallback = callback 67 | 68 | def setMessageReceivedCallback(self, callback): 69 | self._messageReceivedCallback = callback 70 | 71 | def _onResponseFailure(self, failure, msgId): 72 | if (msgId is not None and msgId in self._responseDeferreds): 73 | self._responseDeferreds.pop(msgId) 74 | return failure 75 | 76 | def _cancelMessageDiferred(self, deferred): 77 | deferredIdString = str(id(deferred)) 78 | if (deferredIdString in self._responseDeferreds): 79 | self._responseDeferreds.pop(deferredIdString) 80 | -------------------------------------------------------------------------------- /ctrader_open_api/endpoints.py: -------------------------------------------------------------------------------- 1 | class EndPoints: 2 | AUTH_URI = "https://openapi.ctrader.com/apps/auth" 3 | TOKEN_URI = "https://openapi.ctrader.com/apps/token" 4 | PROTOBUF_DEMO_HOST = "demo.ctraderapi.com" 5 | PROTOBUF_LIVE_HOST = "live.ctraderapi.com" 6 | PROTOBUF_PORT = 5035 7 | -------------------------------------------------------------------------------- /ctrader_open_api/factory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from twisted.internet.protocol import ClientFactory 4 | 5 | class Factory(ClientFactory): 6 | def __init__(self, *args, **kwargs): 7 | super().__init__() 8 | self.client = kwargs['client'] 9 | self.numberOfMessagesToSendPerSecond = self.client.numberOfMessagesToSendPerSecond 10 | def connected(self, protocol): 11 | self.client._connected(protocol) 12 | def disconnected(self, reason): 13 | self.client._disconnected(reason) 14 | def received(self, message): 15 | self.client._received(message) 16 | -------------------------------------------------------------------------------- /ctrader_open_api/messages/OpenApiCommonMessages_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: OpenApiCommonMessages.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import builder as _builder 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from ctrader_open_api.messages import OpenApiCommonModelMessages_pb2 as OpenApiCommonModelMessages__pb2 15 | 16 | 17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bOpenApiCommonMessages.proto\x1a OpenApiCommonModelMessages.proto\"I\n\x0cProtoMessage\x12\x13\n\x0bpayloadType\x18\x01 \x02(\r\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63lientMsgId\x18\x03 \x01(\t\"\x8b\x01\n\rProtoErrorRes\x12\x31\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x11.ProtoPayloadType:\tERROR_RES\x12\x11\n\terrorCode\x18\x02 \x02(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x1f\n\x17maintenanceEndTimestamp\x18\x04 \x01(\x04\"N\n\x13ProtoHeartbeatEvent\x12\x37\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x11.ProtoPayloadType:\x0fHEARTBEAT_EVENTBB\n\"com.xtrader.protocol.proto.commonsB\x17\x43ontainerCommonMessagesP\x01\xa0\x01\x01') 18 | 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OpenApiCommonMessages_pb2', globals()) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'\n\"com.xtrader.protocol.proto.commonsB\027ContainerCommonMessagesP\001\240\001\001' 25 | _PROTOMESSAGE._serialized_start=65 26 | _PROTOMESSAGE._serialized_end=138 27 | _PROTOERRORRES._serialized_start=141 28 | _PROTOERRORRES._serialized_end=280 29 | _PROTOHEARTBEATEVENT._serialized_start=282 30 | _PROTOHEARTBEATEVENT._serialized_end=360 31 | # @@protoc_insertion_point(module_scope) 32 | -------------------------------------------------------------------------------- /ctrader_open_api/messages/OpenApiCommonModelMessages_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: OpenApiCommonModelMessages.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import builder as _builder 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n OpenApiCommonModelMessages.proto*I\n\x10ProtoPayloadType\x12\x11\n\rPROTO_MESSAGE\x10\x05\x12\r\n\tERROR_RES\x10\x32\x12\x13\n\x0fHEARTBEAT_EVENT\x10\x33*\xf0\x01\n\x0eProtoErrorCode\x12\x11\n\rUNKNOWN_ERROR\x10\x01\x12\x17\n\x13UNSUPPORTED_MESSAGE\x10\x02\x12\x13\n\x0fINVALID_REQUEST\x10\x03\x12\x11\n\rTIMEOUT_ERROR\x10\x05\x12\x14\n\x10\x45NTITY_NOT_FOUND\x10\x06\x12\x16\n\x12\x43\x41NT_ROUTE_REQUEST\x10\x07\x12\x12\n\x0e\x46RAME_TOO_LONG\x10\x08\x12\x11\n\rMARKET_CLOSED\x10\t\x12\x1b\n\x17\x43ONCURRENT_MODIFICATION\x10\n\x12\x18\n\x14\x42LOCKED_PAYLOAD_TYPE\x10\x0b\x42M\n(com.xtrader.protocol.proto.commons.modelB\x1c\x43ontainerCommonModelMessagesP\x01\xa0\x01\x01') 17 | 18 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) 19 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OpenApiCommonModelMessages_pb2', globals()) 20 | if _descriptor._USE_C_DESCRIPTORS == False: 21 | 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n(com.xtrader.protocol.proto.commons.modelB\034ContainerCommonModelMessagesP\001\240\001\001' 24 | _PROTOPAYLOADTYPE._serialized_start=36 25 | _PROTOPAYLOADTYPE._serialized_end=109 26 | _PROTOERRORCODE._serialized_start=112 27 | _PROTOERRORCODE._serialized_end=352 28 | # @@protoc_insertion_point(module_scope) 29 | -------------------------------------------------------------------------------- /ctrader_open_api/messages/OpenApiMessages_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: OpenApiMessages.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import builder as _builder 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from ctrader_open_api.messages import OpenApiModelMessages_pb2 as OpenApiModelMessages__pb2 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15OpenApiMessages.proto\x1a\x1aOpenApiModelMessages.proto\"\x8c\x01\n\x19ProtoOAApplicationAuthReq\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_APPLICATION_AUTH_REQ\x12\x10\n\x08\x63lientId\x18\x02 \x02(\t\x12\x14\n\x0c\x63lientSecret\x18\x03 \x02(\t\"d\n\x19ProtoOAApplicationAuthRes\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_APPLICATION_AUTH_RES\"\x8e\x01\n\x15ProtoOAAccountAuthReq\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_ACCOUNT_AUTH_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x13\n\x0b\x61\x63\x63\x65ssToken\x18\x03 \x02(\t\"y\n\x15ProtoOAAccountAuthRes\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_ACCOUNT_AUTH_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xb5\x01\n\x0fProtoOAErrorRes\x12<\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x12PROTO_OA_ERROR_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x01(\x03\x12\x11\n\terrorCode\x18\x03 \x02(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x1f\n\x17maintenanceEndTimestamp\x18\x05 \x01(\x03\"z\n\x1cProtoOAClientDisconnectEvent\x12J\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType: PROTO_OA_CLIENT_DISCONNECT_EVENT\x12\x0e\n\x06reason\x18\x02 \x01(\t\"\xa9\x01\n$ProtoOAAccountsTokenInvalidatedEvent\x12S\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:)PROTO_OA_ACCOUNTS_TOKEN_INVALIDATED_EVENT\x12\x1c\n\x14\x63tidTraderAccountIds\x18\x02 \x03(\x03\x12\x0e\n\x06reason\x18\x03 \x01(\t\"S\n\x11ProtoOAVersionReq\x12>\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x14PROTO_OA_VERSION_REQ\"d\n\x11ProtoOAVersionRes\x12>\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x14PROTO_OA_VERSION_RES\x12\x0f\n\x07version\x18\x02 \x02(\t\"\xb1\x05\n\x12ProtoOANewOrderReq\x12@\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x16PROTO_OA_NEW_ORDER_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x02(\x03\x12$\n\torderType\x18\x04 \x02(\x0e\x32\x11.ProtoOAOrderType\x12$\n\ttradeSide\x18\x05 \x02(\x0e\x32\x11.ProtoOATradeSide\x12\x0e\n\x06volume\x18\x06 \x02(\x03\x12\x12\n\nlimitPrice\x18\x07 \x01(\x01\x12\x11\n\tstopPrice\x18\x08 \x01(\x01\x12:\n\x0btimeInForce\x18\t \x01(\x0e\x32\x13.ProtoOATimeInForce:\x10GOOD_TILL_CANCEL\x12\x1b\n\x13\x65xpirationTimestamp\x18\n \x01(\x03\x12\x10\n\x08stopLoss\x18\x0b \x01(\x01\x12\x12\n\ntakeProfit\x18\x0c \x01(\x01\x12\x0f\n\x07\x63omment\x18\r \x01(\t\x12\x19\n\x11\x62\x61seSlippagePrice\x18\x0e \x01(\x01\x12\x18\n\x10slippageInPoints\x18\x0f \x01(\x05\x12\r\n\x05label\x18\x10 \x01(\t\x12\x12\n\npositionId\x18\x11 \x01(\x03\x12\x15\n\rclientOrderId\x18\x12 \x01(\t\x12\x18\n\x10relativeStopLoss\x18\x13 \x01(\x03\x12\x1a\n\x12relativeTakeProfit\x18\x14 \x01(\x03\x12\x1a\n\x12guaranteedStopLoss\x18\x15 \x01(\x08\x12\x18\n\x10trailingStopLoss\x18\x16 \x01(\x08\x12<\n\x11stopTriggerMethod\x18\x17 \x01(\x0e\x32\x1a.ProtoOAOrderTriggerMethod:\x05TRADE\"\x9c\x03\n\x15ProtoOAExecutionEvent\x12\x42\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x18PROTO_OA_EXECUTION_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12,\n\rexecutionType\x18\x03 \x02(\x0e\x32\x15.ProtoOAExecutionType\x12\"\n\x08position\x18\x04 \x01(\x0b\x32\x10.ProtoOAPosition\x12\x1c\n\x05order\x18\x05 \x01(\x0b\x32\r.ProtoOAOrder\x12\x1a\n\x04\x64\x65\x61l\x18\x06 \x01(\x0b\x32\x0c.ProtoOADeal\x12:\n\x14\x62onusDepositWithdraw\x18\x07 \x01(\x0b\x32\x1c.ProtoOABonusDepositWithdraw\x12\x30\n\x0f\x64\x65positWithdraw\x18\x08 \x01(\x0b\x32\x17.ProtoOADepositWithdraw\x12\x11\n\terrorCode\x18\t \x01(\t\x12\x15\n\risServerEvent\x18\n \x01(\x08\"\x8a\x01\n\x15ProtoOACancelOrderReq\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_CANCEL_ORDER_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x0f\n\x07orderId\x18\x03 \x02(\x03\"\xc6\x03\n\x14ProtoOAAmendOrderReq\x12\x42\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x18PROTO_OA_AMEND_ORDER_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x0f\n\x07orderId\x18\x03 \x02(\x03\x12\x0e\n\x06volume\x18\x04 \x01(\x03\x12\x12\n\nlimitPrice\x18\x05 \x01(\x01\x12\x11\n\tstopPrice\x18\x06 \x01(\x01\x12\x1b\n\x13\x65xpirationTimestamp\x18\x07 \x01(\x03\x12\x10\n\x08stopLoss\x18\x08 \x01(\x01\x12\x12\n\ntakeProfit\x18\t \x01(\x01\x12\x18\n\x10slippageInPoints\x18\n \x01(\x05\x12\x18\n\x10relativeStopLoss\x18\x0b \x01(\x03\x12\x1a\n\x12relativeTakeProfit\x18\x0c \x01(\x03\x12\x1a\n\x12guaranteedStopLoss\x18\r \x01(\x08\x12\x18\n\x10trailingStopLoss\x18\x0e \x01(\x08\x12<\n\x11stopTriggerMethod\x18\x0f \x01(\x0e\x32\x1a.ProtoOAOrderTriggerMethod:\x05TRADE\"\xb8\x02\n\x1bProtoOAAmendPositionSLTPReq\x12J\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType: PROTO_OA_AMEND_POSITION_SLTP_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x12\n\npositionId\x18\x03 \x02(\x03\x12\x10\n\x08stopLoss\x18\x04 \x01(\x01\x12\x12\n\ntakeProfit\x18\x05 \x01(\x01\x12\x1a\n\x12guaranteedStopLoss\x18\x07 \x01(\x08\x12\x18\n\x10trailingStopLoss\x18\x08 \x01(\x08\x12@\n\x15stopLossTriggerMethod\x18\t \x01(\x0e\x32\x1a.ProtoOAOrderTriggerMethod:\x05TRADE\"\xa1\x01\n\x17ProtoOAClosePositionReq\x12\x45\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1bPROTO_OA_CLOSE_POSITION_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x12\n\npositionId\x18\x03 \x02(\x03\x12\x0e\n\x06volume\x18\x04 \x02(\x03\"\xe2\x01\n\x1dProtoOATrailingSLChangedEvent\x12L\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\"PROTO_OA_TRAILING_SL_CHANGED_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x12\n\npositionId\x18\x03 \x02(\x03\x12\x0f\n\x07orderId\x18\x04 \x02(\x03\x12\x11\n\tstopPrice\x18\x05 \x02(\x01\x12\x1e\n\x16utcLastUpdateTimestamp\x18\x06 \x02(\x03\"u\n\x13ProtoOAAssetListReq\x12\x41\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x17PROTO_OA_ASSET_LIST_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\x93\x01\n\x13ProtoOAAssetListRes\x12\x41\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x17PROTO_OA_ASSET_LIST_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1c\n\x05\x61sset\x18\x03 \x03(\x0b\x32\r.ProtoOAAsset\"\xa0\x01\n\x15ProtoOASymbolsListReq\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_SYMBOLS_LIST_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12%\n\x16includeArchivedSymbols\x18\x03 \x01(\x08:\x05\x66\x61lse\"\xce\x01\n\x15ProtoOASymbolsListRes\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_SYMBOLS_LIST_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12#\n\x06symbol\x18\x03 \x03(\x0b\x32\x13.ProtoOALightSymbol\x12.\n\x0e\x61rchivedSymbol\x18\x04 \x03(\x0b\x32\x16.ProtoOAArchivedSymbol\"\x8a\x01\n\x14ProtoOASymbolByIdReq\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_SYMBOL_BY_ID_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x03(\x03\"\xc8\x01\n\x14ProtoOASymbolByIdRes\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_SYMBOL_BY_ID_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1e\n\x06symbol\x18\x03 \x03(\x0b\x32\x0e.ProtoOASymbol\x12.\n\x0e\x61rchivedSymbol\x18\x04 \x03(\x0b\x32\x16.ProtoOAArchivedSymbol\"\xb7\x01\n\x1eProtoOASymbolsForConversionReq\x12M\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:#PROTO_OA_SYMBOLS_FOR_CONVERSION_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x14\n\x0c\x66irstAssetId\x18\x03 \x02(\x03\x12\x13\n\x0blastAssetId\x18\x04 \x02(\x03\"\xb1\x01\n\x1eProtoOASymbolsForConversionRes\x12M\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:#PROTO_OA_SYMBOLS_FOR_CONVERSION_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12#\n\x06symbol\x18\x03 \x03(\x0b\x32\x13.ProtoOALightSymbol\"\x93\x01\n\x19ProtoOASymbolChangedEvent\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_SYMBOL_CHANGED_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x03(\x03\"\x80\x01\n\x18ProtoOAAssetClassListReq\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_ASSET_CLASS_LIST_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xa8\x01\n\x18ProtoOAAssetClassListRes\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_ASSET_CLASS_LIST_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12&\n\nassetClass\x18\x03 \x03(\x0b\x32\x12.ProtoOAAssetClass\"n\n\x10ProtoOATraderReq\x12=\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x13PROTO_OA_TRADER_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\x8e\x01\n\x10ProtoOATraderRes\x12=\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x13PROTO_OA_TRADER_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1e\n\x06trader\x18\x03 \x02(\x0b\x32\x0e.ProtoOATrader\"\xa0\x01\n\x19ProtoOATraderUpdatedEvent\x12\x46\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1cPROTO_OA_TRADER_UPDATE_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1e\n\x06trader\x18\x03 \x02(\x0b\x32\x0e.ProtoOATrader\"t\n\x13ProtoOAReconcileReq\x12@\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x16PROTO_OA_RECONCILE_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xb6\x01\n\x13ProtoOAReconcileRes\x12@\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x16PROTO_OA_RECONCILE_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\"\n\x08position\x18\x03 \x03(\x0b\x32\x10.ProtoOAPosition\x12\x1c\n\x05order\x18\x04 \x03(\x0b\x32\r.ProtoOAOrder\"\xc8\x01\n\x16ProtoOAOrderErrorEvent\x12\x44\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1aPROTO_OA_ORDER_ERROR_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x05 \x02(\x03\x12\x11\n\terrorCode\x18\x02 \x02(\t\x12\x0f\n\x07orderId\x18\x03 \x01(\x03\x12\x12\n\npositionId\x18\x06 \x01(\x03\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\"\xb0\x01\n\x12ProtoOADealListReq\x12@\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x16PROTO_OA_DEAL_LIST_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x15\n\rfromTimestamp\x18\x03 \x02(\x03\x12\x13\n\x0btoTimestamp\x18\x04 \x02(\x03\x12\x0f\n\x07maxRows\x18\x05 \x01(\x05\"\xa0\x01\n\x12ProtoOADealListRes\x12@\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x16PROTO_OA_DEAL_LIST_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1a\n\x04\x64\x65\x61l\x18\x03 \x03(\x0b\x32\x0c.ProtoOADeal\x12\x0f\n\x07hasMore\x18\x04 \x02(\x08\"\xa1\x01\n\x13ProtoOAOrderListReq\x12\x41\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x17PROTO_OA_ORDER_LIST_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x15\n\rfromTimestamp\x18\x03 \x02(\x03\x12\x13\n\x0btoTimestamp\x18\x04 \x02(\x03\"\xa4\x01\n\x13ProtoOAOrderListRes\x12\x41\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x17PROTO_OA_ORDER_LIST_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1c\n\x05order\x18\x03 \x03(\x0b\x32\r.ProtoOAOrder\x12\x0f\n\x07hasMore\x18\x04 \x02(\x08\"\xa1\x01\n\x18ProtoOAExpectedMarginReq\x12\x46\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1cPROTO_OA_EXPECTED_MARGIN_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x02(\x03\x12\x0e\n\x06volume\x18\x04 \x03(\x03\"\xbc\x01\n\x18ProtoOAExpectedMarginRes\x12\x46\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1cPROTO_OA_EXPECTED_MARGIN_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12&\n\x06margin\x18\x03 \x03(\x0b\x32\x16.ProtoOAExpectedMargin\x12\x13\n\x0bmoneyDigits\x18\x04 \x01(\r\"\xbe\x01\n\x19ProtoOAMarginChangedEvent\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_MARGIN_CHANGED_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x12\n\npositionId\x18\x03 \x02(\x04\x12\x12\n\nusedMargin\x18\x04 \x02(\x04\x12\x13\n\x0bmoneyDigits\x18\x05 \x01(\r\"\xb7\x01\n\x1dProtoOACashFlowHistoryListReq\x12M\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:#PROTO_OA_CASH_FLOW_HISTORY_LIST_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x15\n\rfromTimestamp\x18\x03 \x02(\x03\x12\x13\n\x0btoTimestamp\x18\x04 \x02(\x03\"\xbd\x01\n\x1dProtoOACashFlowHistoryListRes\x12M\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:#PROTO_OA_CASH_FLOW_HISTORY_LIST_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x30\n\x0f\x64\x65positWithdraw\x18\x03 \x03(\x0b\x32\x17.ProtoOADepositWithdraw\"\x91\x01\n%ProtoOAGetAccountListByAccessTokenReq\x12S\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:)PROTO_OA_GET_ACCOUNTS_BY_ACCESS_TOKEN_REQ\x12\x13\n\x0b\x61\x63\x63\x65ssToken\x18\x02 \x02(\t\"\xff\x01\n%ProtoOAGetAccountListByAccessTokenRes\x12S\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:)PROTO_OA_GET_ACCOUNTS_BY_ACCESS_TOKEN_RES\x12\x13\n\x0b\x61\x63\x63\x65ssToken\x18\x02 \x02(\t\x12\x36\n\x0fpermissionScope\x18\x03 \x01(\x0e\x32\x1d.ProtoOAClientPermissionScope\x12\x34\n\x11\x63tidTraderAccount\x18\x04 \x03(\x0b\x32\x19.ProtoOACtidTraderAccount\"t\n\x16ProtoOARefreshTokenReq\x12\x44\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1aPROTO_OA_REFRESH_TOKEN_REQ\x12\x14\n\x0crefreshToken\x18\x02 \x02(\t\"\xaf\x01\n\x16ProtoOARefreshTokenRes\x12\x44\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1aPROTO_OA_REFRESH_TOKEN_RES\x12\x13\n\x0b\x61\x63\x63\x65ssToken\x18\x02 \x02(\t\x12\x11\n\ttokenType\x18\x03 \x02(\t\x12\x11\n\texpiresIn\x18\x04 \x02(\x03\x12\x14\n\x0crefreshToken\x18\x05 \x02(\t\"\xb3\x01\n\x18ProtoOASubscribeSpotsReq\x12\x46\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1cPROTO_OA_SUBSCRIBE_SPOTS_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x03(\x03\x12 \n\x18subscribeToSpotTimestamp\x18\x04 \x01(\x08\"\x7f\n\x18ProtoOASubscribeSpotsRes\x12\x46\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1cPROTO_OA_SUBSCRIBE_SPOTS_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\x95\x01\n\x1aProtoOAUnsubscribeSpotsReq\x12H\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1ePROTO_OA_UNSUBSCRIBE_SPOTS_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x03(\x03\"\x83\x01\n\x1aProtoOAUnsubscribeSpotsRes\x12H\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1ePROTO_OA_UNSUBSCRIBE_SPOTS_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xe7\x01\n\x10ProtoOASpotEvent\x12=\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x13PROTO_OA_SPOT_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x02(\x03\x12\x0b\n\x03\x62id\x18\x04 \x01(\x04\x12\x0b\n\x03\x61sk\x18\x05 \x01(\x04\x12\"\n\x08trendbar\x18\x06 \x03(\x0b\x32\x10.ProtoOATrendbar\x12\x14\n\x0csessionClose\x18\x07 \x01(\x04\x12\x11\n\ttimestamp\x18\x08 \x01(\x03\"\xc8\x01\n\x1fProtoOASubscribeLiveTrendbarReq\x12N\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:$PROTO_OA_SUBSCRIBE_LIVE_TRENDBAR_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12&\n\x06period\x18\x03 \x02(\x0e\x32\x16.ProtoOATrendbarPeriod\x12\x10\n\x08symbolId\x18\x04 \x02(\x03\"\x8e\x01\n\x1fProtoOASubscribeLiveTrendbarRes\x12N\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:$PROTO_OA_SUBSCRIBE_LIVE_TRENDBAR_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xcc\x01\n!ProtoOAUnsubscribeLiveTrendbarReq\x12P\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:&PROTO_OA_UNSUBSCRIBE_LIVE_TRENDBAR_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12&\n\x06period\x18\x03 \x02(\x0e\x32\x16.ProtoOATrendbarPeriod\x12\x10\n\x08symbolId\x18\x04 \x02(\x03\"\x92\x01\n!ProtoOAUnsubscribeLiveTrendbarRes\x12P\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:&PROTO_OA_UNSUBSCRIBE_LIVE_TRENDBAR_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xf0\x01\n\x16ProtoOAGetTrendbarsReq\x12\x44\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1aPROTO_OA_GET_TRENDBARS_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x15\n\rfromTimestamp\x18\x03 \x02(\x03\x12\x13\n\x0btoTimestamp\x18\x04 \x02(\x03\x12&\n\x06period\x18\x05 \x02(\x0e\x32\x16.ProtoOATrendbarPeriod\x12\x10\n\x08symbolId\x18\x06 \x02(\x03\x12\r\n\x05\x63ount\x18\x07 \x01(\r\"\xec\x01\n\x16ProtoOAGetTrendbarsRes\x12\x44\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1aPROTO_OA_GET_TRENDBARS_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12&\n\x06period\x18\x03 \x02(\x0e\x32\x16.ProtoOATrendbarPeriod\x12\x11\n\ttimestamp\x18\x04 \x02(\x03\x12\"\n\x08trendbar\x18\x05 \x03(\x0b\x32\x10.ProtoOATrendbar\x12\x10\n\x08symbolId\x18\x06 \x01(\x03\"\xd8\x01\n\x15ProtoOAGetTickDataReq\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_GET_TICKDATA_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x02(\x03\x12\x1f\n\x04type\x18\x04 \x02(\x0e\x32\x11.ProtoOAQuoteType\x12\x15\n\rfromTimestamp\x18\x05 \x02(\x03\x12\x13\n\x0btoTimestamp\x18\x06 \x02(\x03\"\xae\x01\n\x15ProtoOAGetTickDataRes\x12\x43\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x19PROTO_OA_GET_TICKDATA_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\"\n\x08tickData\x18\x03 \x03(\x0b\x32\x10.ProtoOATickData\x12\x0f\n\x07hasMore\x18\x04 \x02(\x08\"\x88\x01\n\x1fProtoOAGetCtidProfileByTokenReq\x12P\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:&PROTO_OA_GET_CTID_PROFILE_BY_TOKEN_REQ\x12\x13\n\x0b\x61\x63\x63\x65ssToken\x18\x02 \x02(\t\"\x99\x01\n\x1fProtoOAGetCtidProfileByTokenRes\x12P\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:&PROTO_OA_GET_CTID_PROFILE_BY_TOKEN_RES\x12$\n\x07profile\x18\x02 \x02(\x0b\x32\x13.ProtoOACtidProfile\"\xc4\x01\n\x11ProtoOADepthEvent\x12>\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x14PROTO_OA_DEPTH_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x02(\x04\x12%\n\tnewQuotes\x18\x04 \x03(\x0b\x32\x12.ProtoOADepthQuote\x12\x19\n\rdeletedQuotes\x18\x05 \x03(\x04\x42\x02\x10\x01\"\x9e\x01\n\x1eProtoOASubscribeDepthQuotesReq\x12M\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:#PROTO_OA_SUBSCRIBE_DEPTH_QUOTES_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x03(\x03\"\x8c\x01\n\x1eProtoOASubscribeDepthQuotesRes\x12M\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:#PROTO_OA_SUBSCRIBE_DEPTH_QUOTES_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xa2\x01\n ProtoOAUnsubscribeDepthQuotesReq\x12O\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:%PROTO_OA_UNSUBSCRIBE_DEPTH_QUOTES_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x10\n\x08symbolId\x18\x03 \x03(\x03\"\x90\x01\n ProtoOAUnsubscribeDepthQuotesRes\x12O\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:%PROTO_OA_UNSUBSCRIBE_DEPTH_QUOTES_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\x83\x01\n\x1cProtoOASymbolCategoryListReq\x12\x46\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1cPROTO_OA_SYMBOL_CATEGORY_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xb3\x01\n\x1cProtoOASymbolCategoryListRes\x12\x46\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1cPROTO_OA_SYMBOL_CATEGORY_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12.\n\x0esymbolCategory\x18\x03 \x03(\x0b\x32\x16.ProtoOASymbolCategory\"}\n\x17ProtoOAAccountLogoutReq\x12\x45\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1bPROTO_OA_ACCOUNT_LOGOUT_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"}\n\x17ProtoOAAccountLogoutRes\x12\x45\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1bPROTO_OA_ACCOUNT_LOGOUT_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\x89\x01\n\x1dProtoOAAccountDisconnectEvent\x12K\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:!PROTO_OA_ACCOUNT_DISCONNECT_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\x80\x01\n\x18ProtoOAMarginCallListReq\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_MARGIN_CALL_LIST_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\x8b\x01\n\x18ProtoOAMarginCallListRes\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_MARGIN_CALL_LIST_RES\x12&\n\nmarginCall\x18\x02 \x03(\x0b\x32\x12.ProtoOAMarginCall\"\xac\x01\n\x1aProtoOAMarginCallUpdateReq\x12I\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1fPROTO_OA_MARGIN_CALL_UPDATE_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12&\n\nmarginCall\x18\x03 \x02(\x0b\x32\x12.ProtoOAMarginCall\"g\n\x1aProtoOAMarginCallUpdateRes\x12I\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1fPROTO_OA_MARGIN_CALL_UPDATE_RES\"\xb0\x01\n\x1cProtoOAMarginCallUpdateEvent\x12K\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:!PROTO_OA_MARGIN_CALL_UPDATE_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12&\n\nmarginCall\x18\x03 \x02(\x0b\x32\x12.ProtoOAMarginCall\"\xb2\x01\n\x1dProtoOAMarginCallTriggerEvent\x12L\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\"PROTO_OA_MARGIN_CALL_TRIGGER_EVENT\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12&\n\nmarginCall\x18\x03 \x02(\x0b\x32\x12.ProtoOAMarginCall\"\xa0\x01\n ProtoOAGetDynamicLeverageByIDReq\x12K\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:!PROTO_OA_GET_DYNAMIC_LEVERAGE_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x12\n\nleverageId\x18\x03 \x02(\x03\"\xb7\x01\n ProtoOAGetDynamicLeverageByIDRes\x12K\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:!PROTO_OA_GET_DYNAMIC_LEVERAGE_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12)\n\x08leverage\x18\x03 \x02(\x0b\x32\x17.ProtoOADynamicLeverage\"\xce\x01\n\x1eProtoOADealListByPositionIdReq\x12O\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:%PROTO_OA_DEAL_LIST_BY_POSITION_ID_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x12\n\npositionId\x18\x03 \x02(\x03\x12\x15\n\rfromTimestamp\x18\x04 \x02(\x03\x12\x13\n\x0btoTimestamp\x18\x05 \x02(\x03\"\xbb\x01\n\x1eProtoOADealListByPositionIdRes\x12O\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:%PROTO_OA_DEAL_LIST_BY_POSITION_ID_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1a\n\x04\x64\x65\x61l\x18\x03 \x03(\x0b\x32\x0c.ProtoOADeal\x12\x0f\n\x07hasMore\x18\x04 \x01(\x03\"\x90\x01\n\x18ProtoOADealOffsetListReq\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_DEAL_OFFSET_LIST_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x0e\n\x06\x64\x65\x61lId\x18\x03 \x02(\x03\"\xce\x01\n\x18ProtoOADealOffsetListRes\x12G\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1dPROTO_OA_DEAL_OFFSET_LIST_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12$\n\x08offsetBy\x18\x03 \x03(\x0b\x32\x12.ProtoOADealOffset\x12&\n\noffsetting\x18\x04 \x03(\x0b\x32\x12.ProtoOADealOffset\"\x95\x01\n\"ProtoOAGetPositionUnrealizedPnLReq\x12R\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:(PROTO_OA_GET_POSITION_UNREALIZED_PNL_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\"\xe8\x01\n\"ProtoOAGetPositionUnrealizedPnLRes\x12R\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:(PROTO_OA_GET_POSITION_UNREALIZED_PNL_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12<\n\x15positionUnrealizedPnL\x18\x03 \x03(\x0b\x32\x1d.ProtoOAPositionUnrealizedPnL\x12\x13\n\x0bmoneyDigits\x18\x04 \x02(\r\"\x8c\x01\n\x16ProtoOAOrderDetailsReq\x12\x44\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1aPROTO_OA_ORDER_DETAILS_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x0f\n\x07orderId\x18\x03 \x02(\x03\"\xb5\x01\n\x16ProtoOAOrderDetailsRes\x12\x44\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:\x1aPROTO_OA_ORDER_DETAILS_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1c\n\x05order\x18\x03 \x02(\x0b\x32\r.ProtoOAOrder\x12\x1a\n\x04\x64\x65\x61l\x18\x04 \x03(\x0b\x32\x0c.ProtoOADeal\"\xd0\x01\n\x1fProtoOAOrderListByPositionIdReq\x12P\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:&PROTO_OA_ORDER_LIST_BY_POSITION_ID_REQ\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x12\n\npositionId\x18\x03 \x02(\x03\x12\x15\n\rfromTimestamp\x18\x04 \x01(\x03\x12\x13\n\x0btoTimestamp\x18\x05 \x01(\x03\"\xbf\x01\n\x1fProtoOAOrderListByPositionIdRes\x12P\n\x0bpayloadType\x18\x01 \x01(\x0e\x32\x13.ProtoOAPayloadType:&PROTO_OA_ORDER_LIST_BY_POSITION_ID_RES\x12\x1b\n\x13\x63tidTraderAccountId\x18\x02 \x02(\x03\x12\x1c\n\x05order\x18\x03 \x03(\x0b\x32\r.ProtoOAOrder\x12\x0f\n\x07hasMore\x18\x04 \x02(\x08\x42\x42\n\x1f\x63om.xtrader.protocol.openapi.v2B\x1a\x43ontainerOpenApiV2MessagesP\x01\xa0\x01\x01') 17 | 18 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) 19 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OpenApiMessages_pb2', globals()) 20 | if _descriptor._USE_C_DESCRIPTORS == False: 21 | 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n\037com.xtrader.protocol.openapi.v2B\032ContainerOpenApiV2MessagesP\001\240\001\001' 24 | _PROTOOADEPTHEVENT.fields_by_name['deletedQuotes']._options = None 25 | _PROTOOADEPTHEVENT.fields_by_name['deletedQuotes']._serialized_options = b'\020\001' 26 | _PROTOOAAPPLICATIONAUTHREQ._serialized_start=54 27 | _PROTOOAAPPLICATIONAUTHREQ._serialized_end=194 28 | _PROTOOAAPPLICATIONAUTHRES._serialized_start=196 29 | _PROTOOAAPPLICATIONAUTHRES._serialized_end=296 30 | _PROTOOAACCOUNTAUTHREQ._serialized_start=299 31 | _PROTOOAACCOUNTAUTHREQ._serialized_end=441 32 | _PROTOOAACCOUNTAUTHRES._serialized_start=443 33 | _PROTOOAACCOUNTAUTHRES._serialized_end=564 34 | _PROTOOAERRORRES._serialized_start=567 35 | _PROTOOAERRORRES._serialized_end=748 36 | _PROTOOACLIENTDISCONNECTEVENT._serialized_start=750 37 | _PROTOOACLIENTDISCONNECTEVENT._serialized_end=872 38 | _PROTOOAACCOUNTSTOKENINVALIDATEDEVENT._serialized_start=875 39 | _PROTOOAACCOUNTSTOKENINVALIDATEDEVENT._serialized_end=1044 40 | _PROTOOAVERSIONREQ._serialized_start=1046 41 | _PROTOOAVERSIONREQ._serialized_end=1129 42 | _PROTOOAVERSIONRES._serialized_start=1131 43 | _PROTOOAVERSIONRES._serialized_end=1231 44 | _PROTOOANEWORDERREQ._serialized_start=1234 45 | _PROTOOANEWORDERREQ._serialized_end=1923 46 | _PROTOOAEXECUTIONEVENT._serialized_start=1926 47 | _PROTOOAEXECUTIONEVENT._serialized_end=2338 48 | _PROTOOACANCELORDERREQ._serialized_start=2341 49 | _PROTOOACANCELORDERREQ._serialized_end=2479 50 | _PROTOOAAMENDORDERREQ._serialized_start=2482 51 | _PROTOOAAMENDORDERREQ._serialized_end=2936 52 | _PROTOOAAMENDPOSITIONSLTPREQ._serialized_start=2939 53 | _PROTOOAAMENDPOSITIONSLTPREQ._serialized_end=3251 54 | _PROTOOACLOSEPOSITIONREQ._serialized_start=3254 55 | _PROTOOACLOSEPOSITIONREQ._serialized_end=3415 56 | _PROTOOATRAILINGSLCHANGEDEVENT._serialized_start=3418 57 | _PROTOOATRAILINGSLCHANGEDEVENT._serialized_end=3644 58 | _PROTOOAASSETLISTREQ._serialized_start=3646 59 | _PROTOOAASSETLISTREQ._serialized_end=3763 60 | _PROTOOAASSETLISTRES._serialized_start=3766 61 | _PROTOOAASSETLISTRES._serialized_end=3913 62 | _PROTOOASYMBOLSLISTREQ._serialized_start=3916 63 | _PROTOOASYMBOLSLISTREQ._serialized_end=4076 64 | _PROTOOASYMBOLSLISTRES._serialized_start=4079 65 | _PROTOOASYMBOLSLISTRES._serialized_end=4285 66 | _PROTOOASYMBOLBYIDREQ._serialized_start=4288 67 | _PROTOOASYMBOLBYIDREQ._serialized_end=4426 68 | _PROTOOASYMBOLBYIDRES._serialized_start=4429 69 | _PROTOOASYMBOLBYIDRES._serialized_end=4629 70 | _PROTOOASYMBOLSFORCONVERSIONREQ._serialized_start=4632 71 | _PROTOOASYMBOLSFORCONVERSIONREQ._serialized_end=4815 72 | _PROTOOASYMBOLSFORCONVERSIONRES._serialized_start=4818 73 | _PROTOOASYMBOLSFORCONVERSIONRES._serialized_end=4995 74 | _PROTOOASYMBOLCHANGEDEVENT._serialized_start=4998 75 | _PROTOOASYMBOLCHANGEDEVENT._serialized_end=5145 76 | _PROTOOAASSETCLASSLISTREQ._serialized_start=5148 77 | _PROTOOAASSETCLASSLISTREQ._serialized_end=5276 78 | _PROTOOAASSETCLASSLISTRES._serialized_start=5279 79 | _PROTOOAASSETCLASSLISTRES._serialized_end=5447 80 | _PROTOOATRADERREQ._serialized_start=5449 81 | _PROTOOATRADERREQ._serialized_end=5559 82 | _PROTOOATRADERRES._serialized_start=5562 83 | _PROTOOATRADERRES._serialized_end=5704 84 | _PROTOOATRADERUPDATEDEVENT._serialized_start=5707 85 | _PROTOOATRADERUPDATEDEVENT._serialized_end=5867 86 | _PROTOOARECONCILEREQ._serialized_start=5869 87 | _PROTOOARECONCILEREQ._serialized_end=5985 88 | _PROTOOARECONCILERES._serialized_start=5988 89 | _PROTOOARECONCILERES._serialized_end=6170 90 | _PROTOOAORDERERROREVENT._serialized_start=6173 91 | _PROTOOAORDERERROREVENT._serialized_end=6373 92 | _PROTOOADEALLISTREQ._serialized_start=6376 93 | _PROTOOADEALLISTREQ._serialized_end=6552 94 | _PROTOOADEALLISTRES._serialized_start=6555 95 | _PROTOOADEALLISTRES._serialized_end=6715 96 | _PROTOOAORDERLISTREQ._serialized_start=6718 97 | _PROTOOAORDERLISTREQ._serialized_end=6879 98 | _PROTOOAORDERLISTRES._serialized_start=6882 99 | _PROTOOAORDERLISTRES._serialized_end=7046 100 | _PROTOOAEXPECTEDMARGINREQ._serialized_start=7049 101 | _PROTOOAEXPECTEDMARGINREQ._serialized_end=7210 102 | _PROTOOAEXPECTEDMARGINRES._serialized_start=7213 103 | _PROTOOAEXPECTEDMARGINRES._serialized_end=7401 104 | _PROTOOAMARGINCHANGEDEVENT._serialized_start=7404 105 | _PROTOOAMARGINCHANGEDEVENT._serialized_end=7594 106 | _PROTOOACASHFLOWHISTORYLISTREQ._serialized_start=7597 107 | _PROTOOACASHFLOWHISTORYLISTREQ._serialized_end=7780 108 | _PROTOOACASHFLOWHISTORYLISTRES._serialized_start=7783 109 | _PROTOOACASHFLOWHISTORYLISTRES._serialized_end=7972 110 | _PROTOOAGETACCOUNTLISTBYACCESSTOKENREQ._serialized_start=7975 111 | _PROTOOAGETACCOUNTLISTBYACCESSTOKENREQ._serialized_end=8120 112 | _PROTOOAGETACCOUNTLISTBYACCESSTOKENRES._serialized_start=8123 113 | _PROTOOAGETACCOUNTLISTBYACCESSTOKENRES._serialized_end=8378 114 | _PROTOOAREFRESHTOKENREQ._serialized_start=8380 115 | _PROTOOAREFRESHTOKENREQ._serialized_end=8496 116 | _PROTOOAREFRESHTOKENRES._serialized_start=8499 117 | _PROTOOAREFRESHTOKENRES._serialized_end=8674 118 | _PROTOOASUBSCRIBESPOTSREQ._serialized_start=8677 119 | _PROTOOASUBSCRIBESPOTSREQ._serialized_end=8856 120 | _PROTOOASUBSCRIBESPOTSRES._serialized_start=8858 121 | _PROTOOASUBSCRIBESPOTSRES._serialized_end=8985 122 | _PROTOOAUNSUBSCRIBESPOTSREQ._serialized_start=8988 123 | _PROTOOAUNSUBSCRIBESPOTSREQ._serialized_end=9137 124 | _PROTOOAUNSUBSCRIBESPOTSRES._serialized_start=9140 125 | _PROTOOAUNSUBSCRIBESPOTSRES._serialized_end=9271 126 | _PROTOOASPOTEVENT._serialized_start=9274 127 | _PROTOOASPOTEVENT._serialized_end=9505 128 | _PROTOOASUBSCRIBELIVETRENDBARREQ._serialized_start=9508 129 | _PROTOOASUBSCRIBELIVETRENDBARREQ._serialized_end=9708 130 | _PROTOOASUBSCRIBELIVETRENDBARRES._serialized_start=9711 131 | _PROTOOASUBSCRIBELIVETRENDBARRES._serialized_end=9853 132 | _PROTOOAUNSUBSCRIBELIVETRENDBARREQ._serialized_start=9856 133 | _PROTOOAUNSUBSCRIBELIVETRENDBARREQ._serialized_end=10060 134 | _PROTOOAUNSUBSCRIBELIVETRENDBARRES._serialized_start=10063 135 | _PROTOOAUNSUBSCRIBELIVETRENDBARRES._serialized_end=10209 136 | _PROTOOAGETTRENDBARSREQ._serialized_start=10212 137 | _PROTOOAGETTRENDBARSREQ._serialized_end=10452 138 | _PROTOOAGETTRENDBARSRES._serialized_start=10455 139 | _PROTOOAGETTRENDBARSRES._serialized_end=10691 140 | _PROTOOAGETTICKDATAREQ._serialized_start=10694 141 | _PROTOOAGETTICKDATAREQ._serialized_end=10910 142 | _PROTOOAGETTICKDATARES._serialized_start=10913 143 | _PROTOOAGETTICKDATARES._serialized_end=11087 144 | _PROTOOAGETCTIDPROFILEBYTOKENREQ._serialized_start=11090 145 | _PROTOOAGETCTIDPROFILEBYTOKENREQ._serialized_end=11226 146 | _PROTOOAGETCTIDPROFILEBYTOKENRES._serialized_start=11229 147 | _PROTOOAGETCTIDPROFILEBYTOKENRES._serialized_end=11382 148 | _PROTOOADEPTHEVENT._serialized_start=11385 149 | _PROTOOADEPTHEVENT._serialized_end=11581 150 | _PROTOOASUBSCRIBEDEPTHQUOTESREQ._serialized_start=11584 151 | _PROTOOASUBSCRIBEDEPTHQUOTESREQ._serialized_end=11742 152 | _PROTOOASUBSCRIBEDEPTHQUOTESRES._serialized_start=11745 153 | _PROTOOASUBSCRIBEDEPTHQUOTESRES._serialized_end=11885 154 | _PROTOOAUNSUBSCRIBEDEPTHQUOTESREQ._serialized_start=11888 155 | _PROTOOAUNSUBSCRIBEDEPTHQUOTESREQ._serialized_end=12050 156 | _PROTOOAUNSUBSCRIBEDEPTHQUOTESRES._serialized_start=12053 157 | _PROTOOAUNSUBSCRIBEDEPTHQUOTESRES._serialized_end=12197 158 | _PROTOOASYMBOLCATEGORYLISTREQ._serialized_start=12200 159 | _PROTOOASYMBOLCATEGORYLISTREQ._serialized_end=12331 160 | _PROTOOASYMBOLCATEGORYLISTRES._serialized_start=12334 161 | _PROTOOASYMBOLCATEGORYLISTRES._serialized_end=12513 162 | _PROTOOAACCOUNTLOGOUTREQ._serialized_start=12515 163 | _PROTOOAACCOUNTLOGOUTREQ._serialized_end=12640 164 | _PROTOOAACCOUNTLOGOUTRES._serialized_start=12642 165 | _PROTOOAACCOUNTLOGOUTRES._serialized_end=12767 166 | _PROTOOAACCOUNTDISCONNECTEVENT._serialized_start=12770 167 | _PROTOOAACCOUNTDISCONNECTEVENT._serialized_end=12907 168 | _PROTOOAMARGINCALLLISTREQ._serialized_start=12910 169 | _PROTOOAMARGINCALLLISTREQ._serialized_end=13038 170 | _PROTOOAMARGINCALLLISTRES._serialized_start=13041 171 | _PROTOOAMARGINCALLLISTRES._serialized_end=13180 172 | _PROTOOAMARGINCALLUPDATEREQ._serialized_start=13183 173 | _PROTOOAMARGINCALLUPDATEREQ._serialized_end=13355 174 | _PROTOOAMARGINCALLUPDATERES._serialized_start=13357 175 | _PROTOOAMARGINCALLUPDATERES._serialized_end=13460 176 | _PROTOOAMARGINCALLUPDATEEVENT._serialized_start=13463 177 | _PROTOOAMARGINCALLUPDATEEVENT._serialized_end=13639 178 | _PROTOOAMARGINCALLTRIGGEREVENT._serialized_start=13642 179 | _PROTOOAMARGINCALLTRIGGEREVENT._serialized_end=13820 180 | _PROTOOAGETDYNAMICLEVERAGEBYIDREQ._serialized_start=13823 181 | _PROTOOAGETDYNAMICLEVERAGEBYIDREQ._serialized_end=13983 182 | _PROTOOAGETDYNAMICLEVERAGEBYIDRES._serialized_start=13986 183 | _PROTOOAGETDYNAMICLEVERAGEBYIDRES._serialized_end=14169 184 | _PROTOOADEALLISTBYPOSITIONIDREQ._serialized_start=14172 185 | _PROTOOADEALLISTBYPOSITIONIDREQ._serialized_end=14378 186 | _PROTOOADEALLISTBYPOSITIONIDRES._serialized_start=14381 187 | _PROTOOADEALLISTBYPOSITIONIDRES._serialized_end=14568 188 | _PROTOOADEALOFFSETLISTREQ._serialized_start=14670 189 | _PROTOOADEALOFFSETLISTREQ._serialized_end=14814 190 | _PROTOOADEALOFFSETLISTRES._serialized_start=14817 191 | _PROTOOADEALOFFSETLISTRES._serialized_end=15023 192 | _PROTOOAGETPOSITIONUNREALIZEDPNLREQ._serialized_start=15026 193 | _PROTOOAGETPOSITIONUNREALIZEDPNLREQ._serialized_end=15175 194 | _PROTOOAGETPOSITIONUNREALIZEDPNLRES._serialized_start=15178 195 | _PROTOOAGETPOSITIONUNREALIZEDPNLRES._serialized_end=15410 196 | _PROTOOAORDERDETAILSREQ._serialized_start=15413 197 | _PROTOOAORDERDETAILSREQ._serialized_end=15553 198 | _PROTOOAORDERDETAILSRES._serialized_start=15556 199 | _PROTOOAORDERDETAILSRES._serialized_end=15737 200 | _PROTOOAORDERLISTBYPOSITIONIDREQ._serialized_start=15740 201 | _PROTOOAORDERLISTBYPOSITIONIDREQ._serialized_end=15948 202 | _PROTOOAORDERLISTBYPOSITIONIDRES._serialized_start=15951 203 | _PROTOOAORDERLISTBYPOSITIONIDRES._serialized_end=16142 204 | # @@protoc_insertion_point(module_scope) 205 | -------------------------------------------------------------------------------- /ctrader_open_api/messages/OpenApiModelMessages_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: OpenApiModelMessages.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import builder as _builder 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aOpenApiModelMessages.proto\"R\n\x0cProtoOAAsset\x12\x0f\n\x07\x61ssetId\x18\x01 \x02(\x03\x12\x0c\n\x04name\x18\x02 \x02(\t\x12\x13\n\x0b\x64isplayName\x18\x03 \x01(\t\x12\x0e\n\x06\x64igits\x18\x04 \x01(\x05\"\xc8\t\n\rProtoOASymbol\x12\x10\n\x08symbolId\x18\x01 \x02(\x03\x12\x0e\n\x06\x64igits\x18\x02 \x02(\x05\x12\x13\n\x0bpipPosition\x18\x03 \x02(\x05\x12\x1a\n\x12\x65nableShortSelling\x18\x04 \x01(\x08\x12\x1a\n\x12guaranteedStopLoss\x18\x05 \x01(\x08\x12\x34\n\x11swapRollover3Days\x18\x06 \x01(\x0e\x32\x11.ProtoOADayOfWeek:\x06MONDAY\x12\x10\n\x08swapLong\x18\x07 \x01(\x01\x12\x11\n\tswapShort\x18\x08 \x01(\x01\x12\x11\n\tmaxVolume\x18\t \x01(\x03\x12\x11\n\tminVolume\x18\n \x01(\x03\x12\x12\n\nstepVolume\x18\x0b \x01(\x03\x12\x13\n\x0bmaxExposure\x18\x0c \x01(\x04\x12\"\n\x08schedule\x18\r \x03(\x0b\x32\x10.ProtoOAInterval\x12\x16\n\ncommission\x18\x0e \x01(\x03\x42\x02\x18\x01\x12\x43\n\x0e\x63ommissionType\x18\x0f \x01(\x0e\x32\x16.ProtoOACommissionType:\x13USD_PER_MILLION_USD\x12\x12\n\nslDistance\x18\x10 \x01(\r\x12\x12\n\ntpDistance\x18\x11 \x01(\r\x12\x13\n\x0bgslDistance\x18\x12 \x01(\r\x12\x11\n\tgslCharge\x18\x13 \x01(\x03\x12L\n\rdistanceSetIn\x18\x14 \x01(\x0e\x32\x1a.ProtoOASymbolDistanceType:\x19SYMBOL_DISTANCE_IN_POINTS\x12\x19\n\rminCommission\x18\x15 \x01(\x03\x42\x02\x18\x01\x12>\n\x11minCommissionType\x18\x16 \x01(\x0e\x32\x19.ProtoOAMinCommissionType:\x08\x43URRENCY\x12\x1f\n\x12minCommissionAsset\x18\x17 \x01(\t:\x03USD\x12\x1a\n\x12rolloverCommission\x18\x18 \x01(\x03\x12\x18\n\x10skipRolloverDays\x18\x19 \x01(\x05\x12\x18\n\x10scheduleTimeZone\x18\x1a \x01(\t\x12\x31\n\x0btradingMode\x18\x1b \x01(\x0e\x32\x13.ProtoOATradingMode:\x07\x45NABLED\x12:\n\x17rolloverCommission3Days\x18\x1c \x01(\x0e\x32\x11.ProtoOADayOfWeek:\x06MONDAY\x12>\n\x13swapCalculationType\x18\x1d \x01(\x0e\x32\x1b.ProtoOASwapCalculationType:\x04PIPS\x12\x0f\n\x07lotSize\x18\x1e \x01(\x03\x12$\n\x1cpreciseTradingCommissionRate\x18\x1f \x01(\x03\x12\x1c\n\x14preciseMinCommission\x18 \x01(\x03\x12 \n\x07holiday\x18! \x03(\x0b\x32\x0f.ProtoOAHoliday\x12\x1c\n\x14pnlConversionFeeRate\x18\" \x01(\x05\x12\x12\n\nleverageId\x18# \x01(\x03\x12\x12\n\nswapPeriod\x18$ \x01(\x05\x12\x10\n\x08swapTime\x18% \x01(\x05\x12\x17\n\x0fskipSWAPPeriods\x18& \x01(\x05\x12\x1c\n\x14\x63hargeSwapAtWeekends\x18\' \x01(\x08\"\xa5\x01\n\x12ProtoOALightSymbol\x12\x10\n\x08symbolId\x18\x01 \x02(\x03\x12\x12\n\nsymbolName\x18\x02 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x03 \x01(\x08\x12\x13\n\x0b\x62\x61seAssetId\x18\x04 \x01(\x03\x12\x14\n\x0cquoteAssetId\x18\x05 \x01(\x03\x12\x18\n\x10symbolCategoryId\x18\x06 \x01(\x03\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\"l\n\x15ProtoOAArchivedSymbol\x12\x10\n\x08symbolId\x18\x01 \x02(\x03\x12\x0c\n\x04name\x18\x02 \x02(\t\x12\x1e\n\x16utcLastUpdateTimestamp\x18\x03 \x02(\x03\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\"G\n\x15ProtoOASymbolCategory\x12\n\n\x02id\x18\x01 \x02(\x03\x12\x14\n\x0c\x61ssetClassId\x18\x02 \x02(\x03\x12\x0c\n\x04name\x18\x03 \x02(\t\"9\n\x0fProtoOAInterval\x12\x13\n\x0bstartSecond\x18\x03 \x02(\r\x12\x11\n\tendSecond\x18\x04 \x02(\r\"\xa4\x05\n\rProtoOATrader\x12\x1b\n\x13\x63tidTraderAccountId\x18\x01 \x02(\x03\x12\x0f\n\x07\x62\x61lance\x18\x02 \x02(\x03\x12\x16\n\x0e\x62\x61lanceVersion\x18\x03 \x01(\x03\x12\x14\n\x0cmanagerBonus\x18\x04 \x01(\x03\x12\x0f\n\x07ibBonus\x18\x05 \x01(\x03\x12\x1c\n\x14nonWithdrawableBonus\x18\x06 \x01(\x03\x12\x37\n\x0c\x61\x63\x63\x65ssRights\x18\x07 \x01(\x0e\x32\x14.ProtoOAAccessRights:\x0b\x46ULL_ACCESS\x12\x16\n\x0e\x64\x65positAssetId\x18\x08 \x02(\x03\x12\x10\n\x08swapFree\x18\t \x01(\x08\x12\x17\n\x0fleverageInCents\x18\n \x01(\r\x12\x46\n\x1atotalMarginCalculationType\x18\x0b \x01(\x0e\x32\".ProtoOATotalMarginCalculationType\x12\x13\n\x0bmaxLeverage\x18\x0c \x01(\r\x12\x16\n\nfrenchRisk\x18\r \x01(\x08\x42\x02\x18\x01\x12\x13\n\x0btraderLogin\x18\x0e \x01(\x03\x12\x30\n\x0b\x61\x63\x63ountType\x18\x0f \x01(\x0e\x32\x13.ProtoOAAccountType:\x06HEDGED\x12\x12\n\nbrokerName\x18\x10 \x01(\t\x12\x1d\n\x15registrationTimestamp\x18\x11 \x01(\x03\x12\x15\n\risLimitedRisk\x18\x12 \x01(\x08\x12q\n$limitedRiskMarginCalculationStrategy\x18\x13 \x01(\x0e\x32,.ProtoOALimitedRiskMarginCalculationStrategy:\x15\x41\x43\x43ORDING_TO_LEVERAGE\x12\x13\n\x0bmoneyDigits\x18\x14 \x01(\r\"\xc4\x03\n\x0fProtoOAPosition\x12\x12\n\npositionId\x18\x01 \x02(\x03\x12$\n\ttradeData\x18\x02 \x02(\x0b\x32\x11.ProtoOATradeData\x12.\n\x0epositionStatus\x18\x03 \x02(\x0e\x32\x16.ProtoOAPositionStatus\x12\x0c\n\x04swap\x18\x04 \x02(\x03\x12\r\n\x05price\x18\x05 \x01(\x01\x12\x10\n\x08stopLoss\x18\x06 \x01(\x01\x12\x12\n\ntakeProfit\x18\x07 \x01(\x01\x12\x1e\n\x16utcLastUpdateTimestamp\x18\x08 \x01(\x03\x12\x12\n\ncommission\x18\t \x01(\x03\x12\x12\n\nmarginRate\x18\n \x01(\x01\x12\x1b\n\x13mirroringCommission\x18\x0b \x01(\x03\x12\x1a\n\x12guaranteedStopLoss\x18\x0c \x01(\x08\x12\x12\n\nusedMargin\x18\r \x01(\x04\x12@\n\x15stopLossTriggerMethod\x18\x0e \x01(\x0e\x32\x1a.ProtoOAOrderTriggerMethod:\x05TRADE\x12\x13\n\x0bmoneyDigits\x18\x0f \x01(\r\x12\x18\n\x10trailingStopLoss\x18\x10 \x01(\x08\"\xad\x01\n\x10ProtoOATradeData\x12\x10\n\x08symbolId\x18\x01 \x02(\x03\x12\x0e\n\x06volume\x18\x02 \x02(\x03\x12$\n\ttradeSide\x18\x03 \x02(\x0e\x32\x11.ProtoOATradeSide\x12\x15\n\ropenTimestamp\x18\x04 \x01(\x03\x12\r\n\x05label\x18\x05 \x01(\t\x12\x1a\n\x12guaranteedStopLoss\x18\x06 \x01(\x08\x12\x0f\n\x07\x63omment\x18\x07 \x01(\t\"\xa5\x05\n\x0cProtoOAOrder\x12\x0f\n\x07orderId\x18\x01 \x02(\x03\x12$\n\ttradeData\x18\x02 \x02(\x0b\x32\x11.ProtoOATradeData\x12$\n\torderType\x18\x03 \x02(\x0e\x32\x11.ProtoOAOrderType\x12(\n\x0borderStatus\x18\x04 \x02(\x0e\x32\x13.ProtoOAOrderStatus\x12\x1b\n\x13\x65xpirationTimestamp\x18\x06 \x01(\x03\x12\x16\n\x0e\x65xecutionPrice\x18\x07 \x01(\x01\x12\x16\n\x0e\x65xecutedVolume\x18\x08 \x01(\x03\x12\x1e\n\x16utcLastUpdateTimestamp\x18\t \x01(\x03\x12\x19\n\x11\x62\x61seSlippagePrice\x18\n \x01(\x01\x12\x18\n\x10slippageInPoints\x18\x0b \x01(\x03\x12\x14\n\x0c\x63losingOrder\x18\x0c \x01(\x08\x12\x12\n\nlimitPrice\x18\r \x01(\x01\x12\x11\n\tstopPrice\x18\x0e \x01(\x01\x12\x10\n\x08stopLoss\x18\x0f \x01(\x01\x12\x12\n\ntakeProfit\x18\x10 \x01(\x01\x12\x15\n\rclientOrderId\x18\x11 \x01(\t\x12=\n\x0btimeInForce\x18\x12 \x01(\x0e\x32\x13.ProtoOATimeInForce:\x13IMMEDIATE_OR_CANCEL\x12\x12\n\npositionId\x18\x13 \x01(\x03\x12\x18\n\x10relativeStopLoss\x18\x14 \x01(\x03\x12\x1a\n\x12relativeTakeProfit\x18\x15 \x01(\x03\x12\x11\n\tisStopOut\x18\x16 \x01(\x08\x12\x18\n\x10trailingStopLoss\x18\x17 \x01(\x08\x12<\n\x11stopTriggerMethod\x18\x18 \x01(\x0e\x32\x1a.ProtoOAOrderTriggerMethod:\x05TRADE\"\x99\x02\n\x1bProtoOABonusDepositWithdraw\x12.\n\roperationType\x18\x01 \x02(\x0e\x32\x17.ProtoOAChangeBonusType\x12\x16\n\x0e\x62onusHistoryId\x18\x02 \x02(\x03\x12\x14\n\x0cmanagerBonus\x18\x03 \x02(\x03\x12\x14\n\x0cmanagerDelta\x18\x04 \x02(\x03\x12\x0f\n\x07ibBonus\x18\x05 \x02(\x03\x12\x0f\n\x07ibDelta\x18\x06 \x02(\x03\x12\x1c\n\x14\x63hangeBonusTimestamp\x18\x07 \x02(\x03\x12\x14\n\x0c\x65xternalNote\x18\x08 \x01(\t\x12\x1b\n\x13introducingBrokerId\x18\t \x01(\x03\x12\x13\n\x0bmoneyDigits\x18\n \x01(\r\"\xf7\x01\n\x16ProtoOADepositWithdraw\x12\x30\n\roperationType\x18\x01 \x02(\x0e\x32\x19.ProtoOAChangeBalanceType\x12\x18\n\x10\x62\x61lanceHistoryId\x18\x02 \x02(\x03\x12\x0f\n\x07\x62\x61lance\x18\x03 \x02(\x03\x12\r\n\x05\x64\x65lta\x18\x04 \x02(\x03\x12\x1e\n\x16\x63hangeBalanceTimestamp\x18\x05 \x02(\x03\x12\x14\n\x0c\x65xternalNote\x18\x06 \x01(\t\x12\x16\n\x0e\x62\x61lanceVersion\x18\x07 \x01(\x03\x12\x0e\n\x06\x65quity\x18\x08 \x01(\x03\x12\x13\n\x0bmoneyDigits\x18\t \x01(\r\"\xcd\x03\n\x0bProtoOADeal\x12\x0e\n\x06\x64\x65\x61lId\x18\x01 \x02(\x03\x12\x0f\n\x07orderId\x18\x02 \x02(\x03\x12\x12\n\npositionId\x18\x03 \x02(\x03\x12\x0e\n\x06volume\x18\x04 \x02(\x03\x12\x14\n\x0c\x66illedVolume\x18\x05 \x02(\x03\x12\x10\n\x08symbolId\x18\x06 \x02(\x03\x12\x17\n\x0f\x63reateTimestamp\x18\x07 \x02(\x03\x12\x1a\n\x12\x65xecutionTimestamp\x18\x08 \x02(\x03\x12\x1e\n\x16utcLastUpdateTimestamp\x18\t \x01(\x03\x12\x16\n\x0e\x65xecutionPrice\x18\n \x01(\x01\x12$\n\ttradeSide\x18\x0b \x02(\x0e\x32\x11.ProtoOATradeSide\x12&\n\ndealStatus\x18\x0c \x02(\x0e\x32\x12.ProtoOADealStatus\x12\x12\n\nmarginRate\x18\r \x01(\x01\x12\x12\n\ncommission\x18\x0e \x01(\x03\x12\x1f\n\x17\x62\x61seToUsdConversionRate\x18\x0f \x01(\x01\x12\x38\n\x13\x63losePositionDetail\x18\x10 \x01(\x0b\x32\x1b.ProtoOAClosePositionDetail\x12\x13\n\x0bmoneyDigits\x18\x11 \x01(\r\"\xfb\x01\n\x1aProtoOAClosePositionDetail\x12\x12\n\nentryPrice\x18\x01 \x02(\x01\x12\x13\n\x0bgrossProfit\x18\x02 \x02(\x03\x12\x0c\n\x04swap\x18\x03 \x02(\x03\x12\x12\n\ncommission\x18\x04 \x02(\x03\x12\x0f\n\x07\x62\x61lance\x18\x05 \x02(\x03\x12$\n\x1cquoteToDepositConversionRate\x18\x06 \x01(\x01\x12\x14\n\x0c\x63losedVolume\x18\x07 \x01(\x03\x12\x16\n\x0e\x62\x61lanceVersion\x18\x08 \x01(\x03\x12\x13\n\x0bmoneyDigits\x18\t \x01(\r\x12\x18\n\x10pnlConversionFee\x18\n \x01(\x03\"\xb3\x01\n\x0fProtoOATrendbar\x12\x0e\n\x06volume\x18\x03 \x02(\x03\x12*\n\x06period\x18\x04 \x01(\x0e\x32\x16.ProtoOATrendbarPeriod:\x02M1\x12\x0b\n\x03low\x18\x05 \x01(\x03\x12\x11\n\tdeltaOpen\x18\x06 \x01(\x04\x12\x12\n\ndeltaClose\x18\x07 \x01(\x04\x12\x11\n\tdeltaHigh\x18\x08 \x01(\x04\x12\x1d\n\x15utcTimestampInMinutes\x18\t \x01(\r\"N\n\x15ProtoOAExpectedMargin\x12\x0e\n\x06volume\x18\x01 \x02(\x03\x12\x11\n\tbuyMargin\x18\x02 \x02(\x03\x12\x12\n\nsellMargin\x18\x03 \x02(\x03\"2\n\x0fProtoOATickData\x12\x11\n\ttimestamp\x18\x01 \x02(\x03\x12\x0c\n\x04tick\x18\x02 \x02(\x03\"$\n\x12ProtoOACtidProfile\x12\x0e\n\x06userId\x18\x01 \x02(\x03\"\xa2\x01\n\x18ProtoOACtidTraderAccount\x12\x1b\n\x13\x63tidTraderAccountId\x18\x01 \x02(\x04\x12\x0e\n\x06isLive\x18\x02 \x01(\x08\x12\x13\n\x0btraderLogin\x18\x03 \x01(\x03\x12 \n\x18lastClosingDealTimestamp\x18\x04 \x01(\x03\x12\"\n\x1alastBalanceUpdateTimestamp\x18\x05 \x01(\x03\"-\n\x11ProtoOAAssetClass\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\"G\n\x11ProtoOADepthQuote\x12\n\n\x02id\x18\x01 \x02(\x04\x12\x0c\n\x04size\x18\x03 \x02(\x04\x12\x0b\n\x03\x62id\x18\x04 \x01(\x04\x12\x0b\n\x03\x61sk\x18\x05 \x01(\x04\"\x83\x01\n\x11ProtoOAMarginCall\x12\x30\n\x0emarginCallType\x18\x01 \x02(\x0e\x32\x18.ProtoOANotificationType\x12\x1c\n\x14marginLevelThreshold\x18\x02 \x02(\x01\x12\x1e\n\x16utcLastUpdateTimestamp\x18\x03 \x01(\x03\"\xb2\x01\n\x0eProtoOAHoliday\x12\x11\n\tholidayId\x18\x01 \x02(\x03\x12\x0c\n\x04name\x18\x02 \x02(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x18\n\x10scheduleTimeZone\x18\x04 \x02(\t\x12\x13\n\x0bholidayDate\x18\x05 \x02(\x03\x12\x13\n\x0bisRecurring\x18\x06 \x02(\x08\x12\x13\n\x0bstartSecond\x18\x07 \x01(\x05\x12\x11\n\tendSecond\x18\x08 \x01(\x05\"X\n\x16ProtoOADynamicLeverage\x12\x12\n\nleverageId\x18\x01 \x02(\x03\x12*\n\x05tiers\x18\x02 \x03(\x0b\x32\x1b.ProtoOADynamicLeverageTier\">\n\x1aProtoOADynamicLeverageTier\x12\x0e\n\x06volume\x18\x01 \x02(\x03\x12\x10\n\x08leverage\x18\x02 \x02(\x03\"g\n\x11ProtoOADealOffset\x12\x0e\n\x06\x64\x65\x61lId\x18\x01 \x02(\x03\x12\x0e\n\x06volume\x18\x02 \x02(\x03\x12\x1a\n\x12\x65xecutionTimestamp\x18\x03 \x01(\x03\x12\x16\n\x0e\x65xecutionPrice\x18\x04 \x01(\x01\"h\n\x1cProtoOAPositionUnrealizedPnL\x12\x12\n\npositionId\x18\x01 \x02(\x03\x12\x1a\n\x12grossUnrealizedPnL\x18\x02 \x02(\x03\x12\x18\n\x10netUnrealizedPnL\x18\x03 \x02(\x05*\xb1\x19\n\x12ProtoOAPayloadType\x12\"\n\x1dPROTO_OA_APPLICATION_AUTH_REQ\x10\xb4\x10\x12\"\n\x1dPROTO_OA_APPLICATION_AUTH_RES\x10\xb5\x10\x12\x1e\n\x19PROTO_OA_ACCOUNT_AUTH_REQ\x10\xb6\x10\x12\x1e\n\x19PROTO_OA_ACCOUNT_AUTH_RES\x10\xb7\x10\x12\x19\n\x14PROTO_OA_VERSION_REQ\x10\xb8\x10\x12\x19\n\x14PROTO_OA_VERSION_RES\x10\xb9\x10\x12\x1b\n\x16PROTO_OA_NEW_ORDER_REQ\x10\xba\x10\x12\'\n\"PROTO_OA_TRAILING_SL_CHANGED_EVENT\x10\xbb\x10\x12\x1e\n\x19PROTO_OA_CANCEL_ORDER_REQ\x10\xbc\x10\x12\x1d\n\x18PROTO_OA_AMEND_ORDER_REQ\x10\xbd\x10\x12%\n PROTO_OA_AMEND_POSITION_SLTP_REQ\x10\xbe\x10\x12 \n\x1bPROTO_OA_CLOSE_POSITION_REQ\x10\xbf\x10\x12\x1c\n\x17PROTO_OA_ASSET_LIST_REQ\x10\xc0\x10\x12\x1c\n\x17PROTO_OA_ASSET_LIST_RES\x10\xc1\x10\x12\x1e\n\x19PROTO_OA_SYMBOLS_LIST_REQ\x10\xc2\x10\x12\x1e\n\x19PROTO_OA_SYMBOLS_LIST_RES\x10\xc3\x10\x12\x1e\n\x19PROTO_OA_SYMBOL_BY_ID_REQ\x10\xc4\x10\x12\x1e\n\x19PROTO_OA_SYMBOL_BY_ID_RES\x10\xc5\x10\x12(\n#PROTO_OA_SYMBOLS_FOR_CONVERSION_REQ\x10\xc6\x10\x12(\n#PROTO_OA_SYMBOLS_FOR_CONVERSION_RES\x10\xc7\x10\x12\"\n\x1dPROTO_OA_SYMBOL_CHANGED_EVENT\x10\xc8\x10\x12\x18\n\x13PROTO_OA_TRADER_REQ\x10\xc9\x10\x12\x18\n\x13PROTO_OA_TRADER_RES\x10\xca\x10\x12!\n\x1cPROTO_OA_TRADER_UPDATE_EVENT\x10\xcb\x10\x12\x1b\n\x16PROTO_OA_RECONCILE_REQ\x10\xcc\x10\x12\x1b\n\x16PROTO_OA_RECONCILE_RES\x10\xcd\x10\x12\x1d\n\x18PROTO_OA_EXECUTION_EVENT\x10\xce\x10\x12!\n\x1cPROTO_OA_SUBSCRIBE_SPOTS_REQ\x10\xcf\x10\x12!\n\x1cPROTO_OA_SUBSCRIBE_SPOTS_RES\x10\xd0\x10\x12#\n\x1ePROTO_OA_UNSUBSCRIBE_SPOTS_REQ\x10\xd1\x10\x12#\n\x1ePROTO_OA_UNSUBSCRIBE_SPOTS_RES\x10\xd2\x10\x12\x18\n\x13PROTO_OA_SPOT_EVENT\x10\xd3\x10\x12\x1f\n\x1aPROTO_OA_ORDER_ERROR_EVENT\x10\xd4\x10\x12\x1b\n\x16PROTO_OA_DEAL_LIST_REQ\x10\xd5\x10\x12\x1b\n\x16PROTO_OA_DEAL_LIST_RES\x10\xd6\x10\x12)\n$PROTO_OA_SUBSCRIBE_LIVE_TRENDBAR_REQ\x10\xd7\x10\x12+\n&PROTO_OA_UNSUBSCRIBE_LIVE_TRENDBAR_REQ\x10\xd8\x10\x12\x1f\n\x1aPROTO_OA_GET_TRENDBARS_REQ\x10\xd9\x10\x12\x1f\n\x1aPROTO_OA_GET_TRENDBARS_RES\x10\xda\x10\x12!\n\x1cPROTO_OA_EXPECTED_MARGIN_REQ\x10\xdb\x10\x12!\n\x1cPROTO_OA_EXPECTED_MARGIN_RES\x10\xdc\x10\x12\"\n\x1dPROTO_OA_MARGIN_CHANGED_EVENT\x10\xdd\x10\x12\x17\n\x12PROTO_OA_ERROR_RES\x10\xde\x10\x12(\n#PROTO_OA_CASH_FLOW_HISTORY_LIST_REQ\x10\xdf\x10\x12(\n#PROTO_OA_CASH_FLOW_HISTORY_LIST_RES\x10\xe0\x10\x12\x1e\n\x19PROTO_OA_GET_TICKDATA_REQ\x10\xe1\x10\x12\x1e\n\x19PROTO_OA_GET_TICKDATA_RES\x10\xe2\x10\x12.\n)PROTO_OA_ACCOUNTS_TOKEN_INVALIDATED_EVENT\x10\xe3\x10\x12%\n PROTO_OA_CLIENT_DISCONNECT_EVENT\x10\xe4\x10\x12.\n)PROTO_OA_GET_ACCOUNTS_BY_ACCESS_TOKEN_REQ\x10\xe5\x10\x12.\n)PROTO_OA_GET_ACCOUNTS_BY_ACCESS_TOKEN_RES\x10\xe6\x10\x12+\n&PROTO_OA_GET_CTID_PROFILE_BY_TOKEN_REQ\x10\xe7\x10\x12+\n&PROTO_OA_GET_CTID_PROFILE_BY_TOKEN_RES\x10\xe8\x10\x12\"\n\x1dPROTO_OA_ASSET_CLASS_LIST_REQ\x10\xe9\x10\x12\"\n\x1dPROTO_OA_ASSET_CLASS_LIST_RES\x10\xea\x10\x12\x19\n\x14PROTO_OA_DEPTH_EVENT\x10\xeb\x10\x12(\n#PROTO_OA_SUBSCRIBE_DEPTH_QUOTES_REQ\x10\xec\x10\x12(\n#PROTO_OA_SUBSCRIBE_DEPTH_QUOTES_RES\x10\xed\x10\x12*\n%PROTO_OA_UNSUBSCRIBE_DEPTH_QUOTES_REQ\x10\xee\x10\x12*\n%PROTO_OA_UNSUBSCRIBE_DEPTH_QUOTES_RES\x10\xef\x10\x12!\n\x1cPROTO_OA_SYMBOL_CATEGORY_REQ\x10\xf0\x10\x12!\n\x1cPROTO_OA_SYMBOL_CATEGORY_RES\x10\xf1\x10\x12 \n\x1bPROTO_OA_ACCOUNT_LOGOUT_REQ\x10\xf2\x10\x12 \n\x1bPROTO_OA_ACCOUNT_LOGOUT_RES\x10\xf3\x10\x12&\n!PROTO_OA_ACCOUNT_DISCONNECT_EVENT\x10\xf4\x10\x12)\n$PROTO_OA_SUBSCRIBE_LIVE_TRENDBAR_RES\x10\xf5\x10\x12+\n&PROTO_OA_UNSUBSCRIBE_LIVE_TRENDBAR_RES\x10\xf6\x10\x12\"\n\x1dPROTO_OA_MARGIN_CALL_LIST_REQ\x10\xf7\x10\x12\"\n\x1dPROTO_OA_MARGIN_CALL_LIST_RES\x10\xf8\x10\x12$\n\x1fPROTO_OA_MARGIN_CALL_UPDATE_REQ\x10\xf9\x10\x12$\n\x1fPROTO_OA_MARGIN_CALL_UPDATE_RES\x10\xfa\x10\x12&\n!PROTO_OA_MARGIN_CALL_UPDATE_EVENT\x10\xfb\x10\x12\'\n\"PROTO_OA_MARGIN_CALL_TRIGGER_EVENT\x10\xfc\x10\x12\x1f\n\x1aPROTO_OA_REFRESH_TOKEN_REQ\x10\xfd\x10\x12\x1f\n\x1aPROTO_OA_REFRESH_TOKEN_RES\x10\xfe\x10\x12\x1c\n\x17PROTO_OA_ORDER_LIST_REQ\x10\xff\x10\x12\x1c\n\x17PROTO_OA_ORDER_LIST_RES\x10\x80\x11\x12&\n!PROTO_OA_GET_DYNAMIC_LEVERAGE_REQ\x10\x81\x11\x12&\n!PROTO_OA_GET_DYNAMIC_LEVERAGE_RES\x10\x82\x11\x12*\n%PROTO_OA_DEAL_LIST_BY_POSITION_ID_REQ\x10\x83\x11\x12*\n%PROTO_OA_DEAL_LIST_BY_POSITION_ID_RES\x10\x84\x11\x12\x1f\n\x1aPROTO_OA_ORDER_DETAILS_REQ\x10\x85\x11\x12\x1f\n\x1aPROTO_OA_ORDER_DETAILS_RES\x10\x86\x11\x12+\n&PROTO_OA_ORDER_LIST_BY_POSITION_ID_REQ\x10\x87\x11\x12+\n&PROTO_OA_ORDER_LIST_BY_POSITION_ID_RES\x10\x88\x11\x12\"\n\x1dPROTO_OA_DEAL_OFFSET_LIST_REQ\x10\x89\x11\x12\"\n\x1dPROTO_OA_DEAL_OFFSET_LIST_RES\x10\x8a\x11\x12-\n(PROTO_OA_GET_POSITION_UNREALIZED_PNL_REQ\x10\x8b\x11\x12-\n(PROTO_OA_GET_POSITION_UNREALIZED_PNL_RES\x10\x8c\x11*x\n\x10ProtoOADayOfWeek\x12\x08\n\x04NONE\x10\x00\x12\n\n\x06MONDAY\x10\x01\x12\x0b\n\x07TUESDAY\x10\x02\x12\r\n\tWEDNESDAY\x10\x03\x12\x0c\n\x08THURSDAY\x10\x04\x12\n\n\x06\x46RIDAY\x10\x05\x12\x0c\n\x08SATURDAY\x10\x06\x12\n\n\x06SUNDAY\x10\x07*q\n\x15ProtoOACommissionType\x12\x17\n\x13USD_PER_MILLION_USD\x10\x01\x12\x0f\n\x0bUSD_PER_LOT\x10\x02\x12\x17\n\x13PERCENTAGE_OF_VALUE\x10\x03\x12\x15\n\x11QUOTE_CCY_PER_LOT\x10\x04*]\n\x19ProtoOASymbolDistanceType\x12\x1d\n\x19SYMBOL_DISTANCE_IN_POINTS\x10\x01\x12!\n\x1dSYMBOL_DISTANCE_IN_PERCENTAGE\x10\x02*<\n\x18ProtoOAMinCommissionType\x12\x0c\n\x08\x43URRENCY\x10\x01\x12\x12\n\x0eQUOTE_CURRENCY\x10\x02*\x85\x01\n\x12ProtoOATradingMode\x12\x0b\n\x07\x45NABLED\x10\x00\x12\'\n#DISABLED_WITHOUT_PENDINGS_EXECUTION\x10\x01\x12$\n DISABLED_WITH_PENDINGS_EXECUTION\x10\x02\x12\x13\n\x0f\x43LOSE_ONLY_MODE\x10\x03*6\n\x1aProtoOASwapCalculationType\x12\x08\n\x04PIPS\x10\x00\x12\x0e\n\nPERCENTAGE\x10\x01*T\n\x13ProtoOAAccessRights\x12\x0f\n\x0b\x46ULL_ACCESS\x10\x00\x12\x0e\n\nCLOSE_ONLY\x10\x01\x12\x0e\n\nNO_TRADING\x10\x02\x12\x0c\n\x08NO_LOGIN\x10\x03*>\n!ProtoOATotalMarginCalculationType\x12\x07\n\x03MAX\x10\x00\x12\x07\n\x03SUM\x10\x01\x12\x07\n\x03NET\x10\x02*@\n\x12ProtoOAAccountType\x12\n\n\x06HEDGED\x10\x00\x12\n\n\x06NETTED\x10\x01\x12\x12\n\x0eSPREAD_BETTING\x10\x02*\x85\x01\n\x15ProtoOAPositionStatus\x12\x18\n\x14POSITION_STATUS_OPEN\x10\x01\x12\x1a\n\x16POSITION_STATUS_CLOSED\x10\x02\x12\x1b\n\x17POSITION_STATUS_CREATED\x10\x03\x12\x19\n\x15POSITION_STATUS_ERROR\x10\x04*%\n\x10ProtoOATradeSide\x12\x07\n\x03\x42UY\x10\x01\x12\x08\n\x04SELL\x10\x02*p\n\x10ProtoOAOrderType\x12\n\n\x06MARKET\x10\x01\x12\t\n\x05LIMIT\x10\x02\x12\x08\n\x04STOP\x10\x03\x12\x19\n\x15STOP_LOSS_TAKE_PROFIT\x10\x04\x12\x10\n\x0cMARKET_RANGE\x10\x05\x12\x0e\n\nSTOP_LIMIT\x10\x06*}\n\x12ProtoOATimeInForce\x12\x12\n\x0eGOOD_TILL_DATE\x10\x01\x12\x14\n\x10GOOD_TILL_CANCEL\x10\x02\x12\x17\n\x13IMMEDIATE_OR_CANCEL\x10\x03\x12\x10\n\x0c\x46ILL_OR_KILL\x10\x04\x12\x12\n\x0eMARKET_ON_OPEN\x10\x05*\x99\x01\n\x12ProtoOAOrderStatus\x12\x19\n\x15ORDER_STATUS_ACCEPTED\x10\x01\x12\x17\n\x13ORDER_STATUS_FILLED\x10\x02\x12\x19\n\x15ORDER_STATUS_REJECTED\x10\x03\x12\x18\n\x14ORDER_STATUS_EXPIRED\x10\x04\x12\x1a\n\x16ORDER_STATUS_CANCELLED\x10\x05*[\n\x19ProtoOAOrderTriggerMethod\x12\t\n\x05TRADE\x10\x01\x12\x0c\n\x08OPPOSITE\x10\x02\x12\x10\n\x0c\x44OUBLE_TRADE\x10\x03\x12\x13\n\x0f\x44OUBLE_OPPOSITE\x10\x04*\xfb\x01\n\x14ProtoOAExecutionType\x12\x12\n\x0eORDER_ACCEPTED\x10\x02\x12\x10\n\x0cORDER_FILLED\x10\x03\x12\x12\n\x0eORDER_REPLACED\x10\x04\x12\x13\n\x0fORDER_CANCELLED\x10\x05\x12\x11\n\rORDER_EXPIRED\x10\x06\x12\x12\n\x0eORDER_REJECTED\x10\x07\x12\x19\n\x15ORDER_CANCEL_REJECTED\x10\x08\x12\x08\n\x04SWAP\x10\t\x12\x14\n\x10\x44\x45POSIT_WITHDRAW\x10\n\x12\x16\n\x12ORDER_PARTIAL_FILL\x10\x0b\x12\x1a\n\x16\x42ONUS_DEPOSIT_WITHDRAW\x10\x0c*?\n\x16ProtoOAChangeBonusType\x12\x11\n\rBONUS_DEPOSIT\x10\x00\x12\x12\n\x0e\x42ONUS_WITHDRAW\x10\x01*\xb8\n\n\x18ProtoOAChangeBalanceType\x12\x13\n\x0f\x42\x41LANCE_DEPOSIT\x10\x00\x12\x14\n\x10\x42\x41LANCE_WITHDRAW\x10\x01\x12-\n)BALANCE_DEPOSIT_STRATEGY_COMMISSION_INNER\x10\x03\x12.\n*BALANCE_WITHDRAW_STRATEGY_COMMISSION_INNER\x10\x04\x12\"\n\x1e\x42\x41LANCE_DEPOSIT_IB_COMMISSIONS\x10\x05\x12)\n%BALANCE_WITHDRAW_IB_SHARED_PERCENTAGE\x10\x06\x12\x34\n0BALANCE_DEPOSIT_IB_SHARED_PERCENTAGE_FROM_SUB_IB\x10\x07\x12\x34\n0BALANCE_DEPOSIT_IB_SHARED_PERCENTAGE_FROM_BROKER\x10\x08\x12\x1a\n\x16\x42\x41LANCE_DEPOSIT_REBATE\x10\t\x12\x1b\n\x17\x42\x41LANCE_WITHDRAW_REBATE\x10\n\x12-\n)BALANCE_DEPOSIT_STRATEGY_COMMISSION_OUTER\x10\x0b\x12.\n*BALANCE_WITHDRAW_STRATEGY_COMMISSION_OUTER\x10\x0c\x12\'\n#BALANCE_WITHDRAW_BONUS_COMPENSATION\x10\r\x12\x33\n/BALANCE_WITHDRAW_IB_SHARED_PERCENTAGE_TO_BROKER\x10\x0e\x12\x1d\n\x19\x42\x41LANCE_DEPOSIT_DIVIDENDS\x10\x0f\x12\x1e\n\x1a\x42\x41LANCE_WITHDRAW_DIVIDENDS\x10\x10\x12\x1f\n\x1b\x42\x41LANCE_WITHDRAW_GSL_CHARGE\x10\x11\x12\x1d\n\x19\x42\x41LANCE_WITHDRAW_ROLLOVER\x10\x12\x12)\n%BALANCE_DEPOSIT_NONWITHDRAWABLE_BONUS\x10\x13\x12*\n&BALANCE_WITHDRAW_NONWITHDRAWABLE_BONUS\x10\x14\x12\x18\n\x14\x42\x41LANCE_DEPOSIT_SWAP\x10\x15\x12\x19\n\x15\x42\x41LANCE_WITHDRAW_SWAP\x10\x16\x12\"\n\x1e\x42\x41LANCE_DEPOSIT_MANAGEMENT_FEE\x10\x1b\x12#\n\x1f\x42\x41LANCE_WITHDRAW_MANAGEMENT_FEE\x10\x1c\x12#\n\x1f\x42\x41LANCE_DEPOSIT_PERFORMANCE_FEE\x10\x1d\x12#\n\x1f\x42\x41LANCE_WITHDRAW_FOR_SUBACCOUNT\x10\x1e\x12!\n\x1d\x42\x41LANCE_DEPOSIT_TO_SUBACCOUNT\x10\x1f\x12$\n BALANCE_WITHDRAW_FROM_SUBACCOUNT\x10 \x12#\n\x1f\x42\x41LANCE_DEPOSIT_FROM_SUBACCOUNT\x10!\x12\x1d\n\x19\x42\x41LANCE_WITHDRAW_COPY_FEE\x10\"\x12#\n\x1f\x42\x41LANCE_WITHDRAW_INACTIVITY_FEE\x10#\x12\x1c\n\x18\x42\x41LANCE_DEPOSIT_TRANSFER\x10$\x12\x1d\n\x19\x42\x41LANCE_WITHDRAW_TRANSFER\x10%\x12#\n\x1f\x42\x41LANCE_DEPOSIT_CONVERTED_BONUS\x10&\x12/\n+BALANCE_DEPOSIT_NEGATIVE_BALANCE_PROTECTION\x10\'*s\n\x11ProtoOADealStatus\x12\n\n\x06\x46ILLED\x10\x02\x12\x14\n\x10PARTIALLY_FILLED\x10\x03\x12\x0c\n\x08REJECTED\x10\x04\x12\x17\n\x13INTERNALLY_REJECTED\x10\x05\x12\t\n\x05\x45RROR\x10\x06\x12\n\n\x06MISSED\x10\x07*\x8c\x01\n\x15ProtoOATrendbarPeriod\x12\x06\n\x02M1\x10\x01\x12\x06\n\x02M2\x10\x02\x12\x06\n\x02M3\x10\x03\x12\x06\n\x02M4\x10\x04\x12\x06\n\x02M5\x10\x05\x12\x07\n\x03M10\x10\x06\x12\x07\n\x03M15\x10\x07\x12\x07\n\x03M30\x10\x08\x12\x06\n\x02H1\x10\t\x12\x06\n\x02H4\x10\n\x12\x07\n\x03H12\x10\x0b\x12\x06\n\x02\x44\x31\x10\x0c\x12\x06\n\x02W1\x10\r\x12\x07\n\x03MN1\x10\x0e*$\n\x10ProtoOAQuoteType\x12\x07\n\x03\x42ID\x10\x01\x12\x07\n\x03\x41SK\x10\x02*?\n\x1cProtoOAClientPermissionScope\x12\x0e\n\nSCOPE_VIEW\x10\x00\x12\x0f\n\x0bSCOPE_TRADE\x10\x01*s\n\x17ProtoOANotificationType\x12\x1c\n\x18MARGIN_LEVEL_THRESHOLD_1\x10=\x12\x1c\n\x18MARGIN_LEVEL_THRESHOLD_2\x10>\x12\x1c\n\x18MARGIN_LEVEL_THRESHOLD_3\x10?*\xde\x08\n\x10ProtoOAErrorCode\x12\x19\n\x15OA_AUTH_TOKEN_EXPIRED\x10\x01\x12\x1a\n\x16\x41\x43\x43OUNT_NOT_AUTHORIZED\x10\x02\x12\x15\n\x11\x41LREADY_LOGGED_IN\x10\x0e\x12\x1a\n\x16\x43H_CLIENT_AUTH_FAILURE\x10\x65\x12\x1f\n\x1b\x43H_CLIENT_NOT_AUTHENTICATED\x10\x66\x12#\n\x1f\x43H_CLIENT_ALREADY_AUTHENTICATED\x10g\x12\x1b\n\x17\x43H_ACCESS_TOKEN_INVALID\x10h\x12\x1b\n\x17\x43H_SERVER_NOT_REACHABLE\x10i\x12$\n CH_CTID_TRADER_ACCOUNT_NOT_FOUND\x10j\x12\x1a\n\x16\x43H_OA_CLIENT_NOT_FOUND\x10k\x12\x1e\n\x1aREQUEST_FREQUENCY_EXCEEDED\x10l\x12\x1f\n\x1bSERVER_IS_UNDER_MAINTENANCE\x10m\x12\x16\n\x12\x43HANNEL_IS_BLOCKED\x10n\x12\x1e\n\x1a\x43ONNECTIONS_LIMIT_EXCEEDED\x10\x43\x12\x19\n\x15WORSE_GSL_NOT_ALLOWED\x10\x44\x12\x16\n\x12SYMBOL_HAS_HOLIDAY\x10\x45\x12\x1b\n\x17NOT_SUBSCRIBED_TO_SPOTS\x10p\x12\x16\n\x12\x41LREADY_SUBSCRIBED\x10q\x12\x14\n\x10SYMBOL_NOT_FOUND\x10r\x12\x12\n\x0eUNKNOWN_SYMBOL\x10s\x12\x18\n\x14INCORRECT_BOUNDARIES\x10#\x12\r\n\tNO_QUOTES\x10u\x12\x14\n\x10NOT_ENOUGH_MONEY\x10v\x12\x18\n\x14MAX_EXPOSURE_REACHED\x10w\x12\x16\n\x12POSITION_NOT_FOUND\x10x\x12\x13\n\x0fORDER_NOT_FOUND\x10y\x12\x15\n\x11POSITION_NOT_OPEN\x10z\x12\x13\n\x0fPOSITION_LOCKED\x10{\x12\x16\n\x12TOO_MANY_POSITIONS\x10|\x12\x16\n\x12TRADING_BAD_VOLUME\x10}\x12\x15\n\x11TRADING_BAD_STOPS\x10~\x12\x16\n\x12TRADING_BAD_PRICES\x10\x7f\x12\x16\n\x11TRADING_BAD_STAKE\x10\x80\x01\x12&\n!PROTECTION_IS_TOO_CLOSE_TO_MARKET\x10\x81\x01\x12 \n\x1bTRADING_BAD_EXPIRATION_DATE\x10\x82\x01\x12\x16\n\x11PENDING_EXECUTION\x10\x83\x01\x12\x15\n\x10TRADING_DISABLED\x10\x84\x01\x12\x18\n\x13TRADING_NOT_ALLOWED\x10\x85\x01\x12\x1b\n\x16UNABLE_TO_CANCEL_ORDER\x10\x86\x01\x12\x1a\n\x15UNABLE_TO_AMEND_ORDER\x10\x87\x01\x12\x1e\n\x19SHORT_SELLING_NOT_ALLOWED\x10\x88\x01*\x81\x01\n+ProtoOALimitedRiskMarginCalculationStrategy\x12\x19\n\x15\x41\x43\x43ORDING_TO_LEVERAGE\x10\x00\x12\x14\n\x10\x41\x43\x43ORDING_TO_GSL\x10\x01\x12!\n\x1d\x41\x43\x43ORDING_TO_GSL_AND_LEVERAGE\x10\x02\x42M\n%com.xtrader.protocol.openapi.v2.modelB\x1f\x43ontainerOpenApiV2ModelMessagesP\x01\xa0\x01\x01') 17 | 18 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) 19 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OpenApiModelMessages_pb2', globals()) 20 | if _descriptor._USE_C_DESCRIPTORS == False: 21 | 22 | DESCRIPTOR._options = None 23 | DESCRIPTOR._serialized_options = b'\n%com.xtrader.protocol.openapi.v2.modelB\037ContainerOpenApiV2ModelMessagesP\001\240\001\001' 24 | _PROTOOASYMBOL.fields_by_name['commission']._options = None 25 | _PROTOOASYMBOL.fields_by_name['commission']._serialized_options = b'\030\001' 26 | _PROTOOASYMBOL.fields_by_name['minCommission']._options = None 27 | _PROTOOASYMBOL.fields_by_name['minCommission']._serialized_options = b'\030\001' 28 | _PROTOOATRADER.fields_by_name['frenchRisk']._options = None 29 | _PROTOOATRADER.fields_by_name['frenchRisk']._serialized_options = b'\030\001' 30 | _PROTOOAPAYLOADTYPE._serialized_start=6100 31 | _PROTOOAPAYLOADTYPE._serialized_end=9349 32 | _PROTOOADAYOFWEEK._serialized_start=9351 33 | _PROTOOADAYOFWEEK._serialized_end=9471 34 | _PROTOOACOMMISSIONTYPE._serialized_start=9473 35 | _PROTOOACOMMISSIONTYPE._serialized_end=9586 36 | _PROTOOASYMBOLDISTANCETYPE._serialized_start=9588 37 | _PROTOOASYMBOLDISTANCETYPE._serialized_end=9681 38 | _PROTOOAMINCOMMISSIONTYPE._serialized_start=9683 39 | _PROTOOAMINCOMMISSIONTYPE._serialized_end=9743 40 | _PROTOOATRADINGMODE._serialized_start=9746 41 | _PROTOOATRADINGMODE._serialized_end=9879 42 | _PROTOOASWAPCALCULATIONTYPE._serialized_start=9881 43 | _PROTOOASWAPCALCULATIONTYPE._serialized_end=9935 44 | _PROTOOAACCESSRIGHTS._serialized_start=9937 45 | _PROTOOAACCESSRIGHTS._serialized_end=10021 46 | _PROTOOATOTALMARGINCALCULATIONTYPE._serialized_start=10023 47 | _PROTOOATOTALMARGINCALCULATIONTYPE._serialized_end=10085 48 | _PROTOOAACCOUNTTYPE._serialized_start=10087 49 | _PROTOOAACCOUNTTYPE._serialized_end=10151 50 | _PROTOOAPOSITIONSTATUS._serialized_start=10154 51 | _PROTOOAPOSITIONSTATUS._serialized_end=10287 52 | _PROTOOATRADESIDE._serialized_start=10289 53 | _PROTOOATRADESIDE._serialized_end=10326 54 | _PROTOOAORDERTYPE._serialized_start=10328 55 | _PROTOOAORDERTYPE._serialized_end=10440 56 | _PROTOOATIMEINFORCE._serialized_start=10442 57 | _PROTOOATIMEINFORCE._serialized_end=10567 58 | _PROTOOAORDERSTATUS._serialized_start=10570 59 | _PROTOOAORDERSTATUS._serialized_end=10723 60 | _PROTOOAORDERTRIGGERMETHOD._serialized_start=10725 61 | _PROTOOAORDERTRIGGERMETHOD._serialized_end=10816 62 | _PROTOOAEXECUTIONTYPE._serialized_start=10819 63 | _PROTOOAEXECUTIONTYPE._serialized_end=11070 64 | _PROTOOACHANGEBONUSTYPE._serialized_start=11072 65 | _PROTOOACHANGEBONUSTYPE._serialized_end=11135 66 | _PROTOOACHANGEBALANCETYPE._serialized_start=11138 67 | _PROTOOACHANGEBALANCETYPE._serialized_end=12474 68 | _PROTOOADEALSTATUS._serialized_start=12476 69 | _PROTOOADEALSTATUS._serialized_end=12591 70 | _PROTOOATRENDBARPERIOD._serialized_start=12594 71 | _PROTOOATRENDBARPERIOD._serialized_end=12734 72 | _PROTOOAQUOTETYPE._serialized_start=12736 73 | _PROTOOAQUOTETYPE._serialized_end=12772 74 | _PROTOOACLIENTPERMISSIONSCOPE._serialized_start=12774 75 | _PROTOOACLIENTPERMISSIONSCOPE._serialized_end=12837 76 | _PROTOOANOTIFICATIONTYPE._serialized_start=12839 77 | _PROTOOANOTIFICATIONTYPE._serialized_end=12954 78 | _PROTOOAERRORCODE._serialized_start=12957 79 | _PROTOOAERRORCODE._serialized_end=14075 80 | _PROTOOALIMITEDRISKMARGINCALCULATIONSTRATEGY._serialized_start=14078 81 | _PROTOOALIMITEDRISKMARGINCALCULATIONSTRATEGY._serialized_end=14207 82 | _PROTOOAASSET._serialized_start=30 83 | _PROTOOAASSET._serialized_end=112 84 | _PROTOOASYMBOL._serialized_start=115 85 | _PROTOOASYMBOL._serialized_end=1339 86 | _PROTOOALIGHTSYMBOL._serialized_start=1342 87 | _PROTOOALIGHTSYMBOL._serialized_end=1507 88 | _PROTOOAARCHIVEDSYMBOL._serialized_start=1509 89 | _PROTOOAARCHIVEDSYMBOL._serialized_end=1617 90 | _PROTOOASYMBOLCATEGORY._serialized_start=1619 91 | _PROTOOASYMBOLCATEGORY._serialized_end=1690 92 | _PROTOOAINTERVAL._serialized_start=1692 93 | _PROTOOAINTERVAL._serialized_end=1749 94 | _PROTOOATRADER._serialized_start=1752 95 | _PROTOOATRADER._serialized_end=2428 96 | _PROTOOAPOSITION._serialized_start=2431 97 | _PROTOOAPOSITION._serialized_end=2883 98 | _PROTOOATRADEDATA._serialized_start=2886 99 | _PROTOOATRADEDATA._serialized_end=3059 100 | _PROTOOAORDER._serialized_start=3062 101 | _PROTOOAORDER._serialized_end=3739 102 | _PROTOOABONUSDEPOSITWITHDRAW._serialized_start=3742 103 | _PROTOOABONUSDEPOSITWITHDRAW._serialized_end=4023 104 | _PROTOOADEPOSITWITHDRAW._serialized_start=4026 105 | _PROTOOADEPOSITWITHDRAW._serialized_end=4273 106 | _PROTOOADEAL._serialized_start=4276 107 | _PROTOOADEAL._serialized_end=4737 108 | _PROTOOACLOSEPOSITIONDETAIL._serialized_start=4740 109 | _PROTOOACLOSEPOSITIONDETAIL._serialized_end=4991 110 | _PROTOOATRENDBAR._serialized_start=4994 111 | _PROTOOATRENDBAR._serialized_end=5173 112 | _PROTOOAEXPECTEDMARGIN._serialized_start=5175 113 | _PROTOOAEXPECTEDMARGIN._serialized_end=5253 114 | _PROTOOATICKDATA._serialized_start=5255 115 | _PROTOOATICKDATA._serialized_end=5305 116 | _PROTOOACTIDPROFILE._serialized_start=5307 117 | _PROTOOACTIDPROFILE._serialized_end=5343 118 | _PROTOOACTIDTRADERACCOUNT._serialized_start=5346 119 | _PROTOOACTIDTRADERACCOUNT._serialized_end=5508 120 | _PROTOOAASSETCLASS._serialized_start=5510 121 | _PROTOOAASSETCLASS._serialized_end=5555 122 | _PROTOOADEPTHQUOTE._serialized_start=5557 123 | _PROTOOADEPTHQUOTE._serialized_end=5628 124 | _PROTOOAMARGINCALL._serialized_start=5631 125 | _PROTOOAMARGINCALL._serialized_end=5762 126 | _PROTOOAHOLIDAY._serialized_start=5765 127 | _PROTOOAHOLIDAY._serialized_end=5943 128 | _PROTOOADYNAMICLEVERAGE._serialized_start=5945 129 | _PROTOOADYNAMICLEVERAGE._serialized_end=6033 130 | _PROTOOADYNAMICLEVERAGETIER._serialized_start=6035 131 | _PROTOOADYNAMICLEVERAGETIER._serialized_end=6097 132 | # @@protoc_insertion_point(module_scope) 133 | -------------------------------------------------------------------------------- /ctrader_open_api/messages/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ctrader_open_api/protobuf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | class Protobuf(object): 4 | _protos = dict() 5 | _names = dict() 6 | _abbr_names = dict() 7 | 8 | @classmethod 9 | def populate(cls): 10 | import re 11 | from .messages import OpenApiCommonMessages_pb2 as o1 12 | from .messages import OpenApiMessages_pb2 as o2 13 | 14 | for name in dir(o1) + dir(o2): 15 | if not name.startswith("Proto"): 16 | continue 17 | 18 | m = o1 if hasattr(o1, name) else o2 19 | klass = getattr(m, name) 20 | cls._protos[klass().payloadType] = klass 21 | cls._names[klass.__name__] = klass().payloadType 22 | abbr_name = re.sub(r'^Proto(OA)?(.*)', r'\2', klass.__name__) 23 | cls._names[abbr_name] = klass().payloadType 24 | return cls._protos 25 | 26 | @classmethod 27 | def get(cls, payload, fail=True, **params): 28 | if not cls._protos: 29 | cls.populate() 30 | 31 | if payload in cls._protos: 32 | return cls._protos[payload](**params) 33 | 34 | for d in [cls._names, cls._abbr_names]: 35 | if payload in d: 36 | payload = d[payload] 37 | return cls._protos[payload](**params) 38 | if fail: # pragma: nocover 39 | raise IndexError("Invalid payload: " + str(payload)) 40 | return None # pragma: nocover 41 | 42 | @classmethod 43 | def get_type(cls, payload, **params): 44 | p = cls.get(payload, **params) 45 | return p.payloadType 46 | 47 | @classmethod 48 | def extract(cls, message): 49 | payload = cls.get(message.payloadType) 50 | payload.ParseFromString(message.payload) 51 | return payload 52 | -------------------------------------------------------------------------------- /ctrader_open_api/tcpProtocol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from collections import deque 4 | from twisted.protocols.basic import Int32StringReceiver 5 | from twisted.internet import task 6 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import ProtoMessage, ProtoHeartbeatEvent 7 | import datetime 8 | 9 | class TcpProtocol(Int32StringReceiver): 10 | MAX_LENGTH = 15000000 11 | _send_queue = deque([]) 12 | _send_task = None 13 | _lastSendMessageTime = None 14 | 15 | def connectionMade(self): 16 | super().connectionMade() 17 | 18 | if not self._send_task: 19 | self._send_task = task.LoopingCall(self._sendStrings) 20 | self._send_task.start(1) 21 | self.factory.connected(self) 22 | 23 | def connectionLost(self, reason): 24 | super().connectionLost(reason) 25 | if self._send_task.running: 26 | self._send_task.stop() 27 | self.factory.disconnected(reason) 28 | 29 | def heartbeat(self): 30 | self.send(ProtoHeartbeatEvent(), True) 31 | 32 | def send(self, message, instant=False, clientMsgId=None, isCanceled = None): 33 | data = b'' 34 | 35 | if isinstance(message, ProtoMessage): 36 | data = message.SerializeToString() 37 | 38 | if isinstance(message, bytes): 39 | data = message 40 | 41 | if isinstance(message, ProtoMessage.__base__): 42 | msg = ProtoMessage(payload=message.SerializeToString(), 43 | clientMsgId=clientMsgId, 44 | payloadType=message.payloadType) 45 | data = msg.SerializeToString() 46 | 47 | if instant: 48 | self.sendString(data) 49 | self._lastSendMessageTime = datetime.datetime.now() 50 | else: 51 | self._send_queue.append((isCanceled, data)) 52 | 53 | def _sendStrings(self): 54 | size = len(self._send_queue) 55 | 56 | if not size: 57 | if self._lastSendMessageTime is None or (datetime.datetime.now() - self._lastSendMessageTime).total_seconds() > 20: 58 | self.heartbeat() 59 | return 60 | 61 | for _ in range(min(size, self.factory.numberOfMessagesToSendPerSecond)): 62 | isCanceled, data = self._send_queue.popleft() 63 | if isCanceled is not None and isCanceled(): 64 | continue; 65 | self.sendString(data) 66 | self._lastSendMessageTime = datetime.datetime.now() 67 | 68 | def stringReceived(self, data): 69 | msg = ProtoMessage() 70 | msg.ParseFromString(data) 71 | 72 | if msg.payloadType == ProtoHeartbeatEvent().payloadType: 73 | self.heartbeat() 74 | self.factory.received(msg) 75 | return data 76 | -------------------------------------------------------------------------------- /docs/authentication.md: -------------------------------------------------------------------------------- 1 | ### Auth Class 2 | 3 | For authentication you can use the package Auth class, first create an instance of it: 4 | 5 | ```python 6 | from ctrader_open_api import Auth 7 | 8 | auth = Auth("Your App ID", "Your App Secret", "Your App redirect URI") 9 | ``` 10 | 11 | ### Auth URI 12 | 13 | The first step for authentication is sending user to the cTrader Open API authentication web page, there the user will give access to your API application to manage the user trading accounts on behalf of him. 14 | 15 | To get the cTrader Open API authentication web page URL you can use the Auth class getAuthUri method: 16 | 17 | ```python 18 | authUri = auth.getAuthUri() 19 | ``` 20 | The getAuthUri has two optional parameters: 21 | 22 | * scope: Allows you to set the scope of authentication, the default value is trading which means you will have full access to user trading accounts, if you want to just have access to user trading account data then use accounts 23 | 24 | * baseUri: The base URI for authentication, the default value is EndPoints.AUTH_URI which is https://connect.spotware.com/apps/auth 25 | 26 | ### Getting Token 27 | 28 | After user authenticated your Application he will be redirected to your provided redirect URI with an authentication code appended at the end of your redirect URI: 29 | 30 | ``` 31 | https://redirect-uri.com/?code={authorization-code-will-be-here} 32 | ``` 33 | 34 | You can use this authentication code to get an access token from API, for that you can use the Auth class getToken method: 35 | 36 | ```python 37 | # This method uses EndPoints.TOKEN_URI as a base URI to get token 38 | # you can change it by passing another URI via optional baseUri parameter 39 | token = auth.getToken("auth_code") 40 | ``` 41 | 42 | Pass the received auth code to getToken method and it will give you a token JSON object, the object will have these properties: 43 | 44 | * accessToken: This is the access token that you will use for authentication 45 | 46 | * refreshToken: This is the token that you will use for refreshing the accessToken onces it expired 47 | 48 | * expiresIn: The expiry of token in seconds from the time it generated 49 | 50 | * tokenType: The type of token, standard OAuth token type parameter (bearer) 51 | 52 | * errorCode: This will have the error code if something went wrong 53 | 54 | * description: The error description 55 | 56 | ### Refreshing Token 57 | 58 | API access tokens have an expiry time, you can only use it until that time and once it expired you have to refresh it by using the refresh token you received previously. 59 | 60 | To refresh an access token you can use the Auth class refreshToken method: 61 | 62 | ```python 63 | # This method uses EndPoints.TOKEN_URI as a base URI to refresh token 64 | # you can change it by passing another URI via optional baseUri parameter 65 | newToken = auth.refreshToken("refresh_Token") 66 | ``` 67 | 68 | You have to pass the refresh token to "refreshToken" method, and it will return a new token JSON object which will have all the previously mentioned token properties. 69 | 70 | You can always refresh a token, even before it expires and the refresh token has no expiry, but you can only use it once. 71 | -------------------------------------------------------------------------------- /docs/client.md: -------------------------------------------------------------------------------- 1 | ### Client Class 2 | 3 | You will use an instance of this class to interact with API. 4 | 5 | Each instance of this class will have one connection to API, either live or demo endpoint. 6 | 7 | The client class is driven from Twisted ClientService class, and it abstracts away all the connection / reconnection complexities from you. 8 | 9 | ### Creating a Client 10 | 11 | Let's create an isntance of Client class: 12 | 13 | ```python 14 | 15 | from ctrader_open_api import Client, Protobuf, TcpProtocol, Auth, EndPoints 16 | 17 | client = Client(EndPoints.PROTOBUF_DEMO_HOST, EndPoints.PROTOBUF_PORT, TcpProtocol) 18 | 19 | ``` 20 | 21 | It's constructor has several parameters that you can use for controling it behavior: 22 | 23 | * host: The API host endpoint, you can use either EndPoints.PROTOBUF_DEMO_HOST or EndPoints.PROTOBUF_LIVE_HOST 24 | 25 | * port: The API host port number, you can use EndPoints.PROTOBUF_PORT 26 | 27 | * protocol: The protocol that will be used by client for making connections, use imported TcpProtocol 28 | 29 | * numberOfMessagesToSendPerSecond: This is the number of messages that will be sent to API per second, set it based on API limitations or leave the default value 30 | 31 | There are three other optional parameters which are from Twisted client service, you can find their detail here: https://twistedmatrix.com/documents/current/api/twisted.application.internet.ClientService.html 32 | 33 | ### Sending Message 34 | 35 | To send a message you have to first create the proto message, ex: 36 | 37 | ```python 38 | # Import all message types 39 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import * 40 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import * 41 | from ctrader_open_api.messages.OpenApiMessages_pb2 import * 42 | from ctrader_open_api.messages.OpenApiModelMessages_pb2 import * 43 | 44 | # ProtoOAApplicationAuthReq message 45 | applicationAuthReq = ProtoOAApplicationAuthReq() 46 | applicationAuthReq.clientId = "Your App Client ID" 47 | applicationAuthReq.clientSecret = "Your App Client secret" 48 | 49 | ``` 50 | 51 | After you created the message and populated its fields, you can send it by using Client send method: 52 | 53 | ```python 54 | deferred = client.send(applicationAuthReq) 55 | ``` 56 | 57 | The client send method returns a Twisted deferred, it will be called when the message response arrived, the callback result will be the response proto message. 58 | 59 | If the message send failed, the returned deferred error callback will be called, to handle both cases you can attach two callbacks for getting response or error: 60 | 61 | ```python 62 | def onProtoOAApplicationAuthRes(result): 63 | print(result) 64 | 65 | def onError(failure): 66 | print(failure) 67 | 68 | deferred.addCallbacks(onProtoOAApplicationAuthRes, onError) 69 | ``` 70 | For more about Twisted deferreds please check their documentation: https://docs.twistedmatrix.com/en/twisted-16.2.0/core/howto/defer-intro.html 71 | 72 | ### Canceling Message 73 | 74 | You can cancel a message by calling the returned deferred from Client send method Cancel method. 75 | 76 | If the message is not sent yet, it will be removed from the messages queue and the deferred Errback method will be called with CancelledError. 77 | 78 | If the message is already sent but the response is not received yet, then you will not receive the response and the deferred Errback method will be called with CancelledError. 79 | 80 | If the message is already sent and the reponse is received then canceling it's deferred will not have any effect. 81 | 82 | ### Other Callbacks 83 | 84 | The client class has some other optional general purpose callbacks that you can use: 85 | 86 | * ConnectedCallback(client): This callback will be called when client gets connected, use client setConnectedCallback method to assign a callback for it 87 | 88 | * DisconnectedCallback(client, reason): This callback will be called when client gets disconnected, use client setDisconnectedCallback method to assign a callback for it 89 | 90 | * MessageReceivedCallback(client, message): This callback will be called when a message is received, it's called for all message types, use setMessageReceivedCallback to assign a callback for it 91 | -------------------------------------------------------------------------------- /docs/css/extra.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "TitilliumWeb-Regular"; 3 | src: url(../fonts/TitilliumWeb-Regular.ttf); 4 | } 5 | 6 | @font-face { 7 | font-family: "TitilliumWeb-SemiBold"; 8 | src: url(../fonts/TitilliumWeb-SemiBold.ttf); 9 | } 10 | 11 | h1 { 12 | font-family: "TitilliumWeb-SemiBold"; 13 | } 14 | 15 | h2 { 16 | font-family: "TitilliumWeb-SemiBold"; 17 | } 18 | 19 | :root > * { 20 | --md-primary-fg-color: #292929; 21 | --md-primary-fg-color--light: #292929; 22 | --md-primary-fg-color--dark: #3C3C3C; 23 | --md-primary-bg-color: hsla(0, 0%, 100%, 1); 24 | --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7); 25 | --md-typeset-a-color: #009345; 26 | --md-text-font: "TitilliumWeb-Regular"; 27 | } 28 | 29 | [data-md-color-scheme="slate"] { 30 | --md-hue: 232; 31 | --md-default-fg-color: hsla(var(--md-hue),75%,95%,1); 32 | --md-default-fg-color--light: hsla(var(--md-hue),75%,90%,0.62); 33 | --md-default-fg-color--lighter: hsla(var(--md-hue),75%,90%,0.32); 34 | --md-default-fg-color--lightest: hsla(var(--md-hue),75%,90%,0.12); 35 | --md-default-bg-color: hsl(0, 0%, 24%); 36 | --md-default-bg-color--light: hsla(var(--md-hue),15%,21%,0.54); 37 | --md-default-bg-color--lighter: hsla(var(--md-hue),15%,21%,0.26); 38 | --md-default-bg-color--lightest: hsla(var(--md-hue),15%,21%,0.07); 39 | --md-code-fg-color: hsla(var(--md-hue),18%,86%,1); 40 | --md-code-bg-color: hsla(var(--md-hue),15%,15%,1); 41 | --md-code-hl-color: rgba(66,135,255,.15); 42 | --md-code-hl-number-color: #e6695b; 43 | --md-code-hl-special-color: #f06090; 44 | --md-code-hl-function-color: #c973d9; 45 | --md-code-hl-constant-color: #9383e2; 46 | --md-code-hl-keyword-color: #6791e0; 47 | --md-code-hl-string-color: #2fb170; 48 | --md-code-hl-name-color: var(--md-code-fg-color); 49 | --md-code-hl-operator-color: var(--md-default-fg-color--light); 50 | --md-code-hl-punctuation-color: var(--md-default-fg-color--light); 51 | --md-code-hl-comment-color: var(--md-default-fg-color--light); 52 | --md-code-hl-generic-color: var(--md-default-fg-color--light); 53 | --md-code-hl-variable-color: var(--md-default-fg-color--light); 54 | --md-typeset-color: var(--md-default-fg-color); 55 | --md-typeset-a-color: #009345; 56 | --md-typeset-mark-color: rgba(66,135,255,.3); 57 | --md-typeset-kbd-color: hsla(var(--md-hue),15%,94%,0.12); 58 | --md-typeset-kbd-accent-color: hsla(var(--md-hue),15%,94%,0.2); 59 | --md-typeset-kbd-border-color: hsla(var(--md-hue),15%,14%,1); 60 | --md-typeset-table-color: hsla(var(--md-hue),75%,95%,0.12); 61 | --md-admonition-bg-color: hsla(var(--md-hue),0%,100%,0.025); 62 | --md-footer-bg-color: #292929; 63 | --md-footer-bg-color--dark: #3C3C3C; 64 | 65 | } 66 | -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-Black.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-Bold.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-BoldItalic.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-ExtraLight.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-Italic.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-Light.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-LightItalic.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-Regular.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-SemiBold.ttf -------------------------------------------------------------------------------- /docs/fonts/TitilliumWeb-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/fonts/TitilliumWeb-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/img/favicon.ico -------------------------------------------------------------------------------- /docs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/docs/img/logo.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ### Introduction 2 | 3 | A Python package for interacting with cTrader Open API. 4 | 5 | This package is developed and maintained by Spotware. 6 | 7 | You can use OpenApiPy on all kinds of Python apps, it uses Twisted to send and receive messages asynchronously. 8 | 9 | Github Repository: https://github.com/spotware/OpenApiPy 10 | 11 | ### Installation 12 | 13 | You can install OpenApiPy from pip: 14 | 15 | ``` 16 | pip install ctrader-open-api 17 | ``` 18 | 19 | ### Usage 20 | 21 | ```python 22 | 23 | from ctrader_open_api import Client, Protobuf, TcpProtocol, Auth, EndPoints 24 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import * 25 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import * 26 | from ctrader_open_api.messages.OpenApiMessages_pb2 import * 27 | from ctrader_open_api.messages.OpenApiModelMessages_pb2 import * 28 | from twisted.internet import reactor 29 | 30 | hostType = input("Host (Live/Demo): ") 31 | host = EndPoints.PROTOBUF_LIVE_HOST if hostType.lower() == "live" else EndPoints.PROTOBUF_DEMO_HOST 32 | client = Client(host, EndPoints.PROTOBUF_PORT, TcpProtocol) 33 | 34 | def onError(failure): # Call back for errors 35 | print("Message Error: ", failure) 36 | 37 | def connected(client): # Callback for client connection 38 | print("\nConnected") 39 | # Now we send a ProtoOAApplicationAuthReq 40 | request = ProtoOAApplicationAuthReq() 41 | request.clientId = "Your application Client ID" 42 | request.clientSecret = "Your application Client secret" 43 | # Client send method returns a Twisted deferred 44 | deferred = client.send(request) 45 | # You can use the returned Twisted deferred to attach callbacks 46 | # for getting message response or error backs for getting error if something went wrong 47 | # deferred.addCallbacks(onProtoOAApplicationAuthRes, onError) 48 | deferred.addErrback(onError) 49 | 50 | def disconnected(client, reason): # Callback for client disconnection 51 | print("\nDisconnected: ", reason) 52 | 53 | def onMessageReceived(client, message): # Callback for receiving all messages 54 | print("Message received: \n", Protobuf.extract(message)) 55 | 56 | # Setting optional client callbacks 57 | client.setConnectedCallback(connected) 58 | client.setDisconnectedCallback(disconnected) 59 | client.setMessageReceivedCallback(onMessageReceived) 60 | # Starting the client service 61 | client.startService() 62 | # Run Twisted reactor 63 | reactor.run() 64 | 65 | ``` 66 | 67 | -------------------------------------------------------------------------------- /docs/js/extra.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("scroll", function (event) { 2 | copyrightYear = document.querySelector('#copyright-year'); 3 | copyrightYear.innerText = new Date().getUTCFullYear(); 4 | }); 5 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Project Information 3 | site_name: OpenApiPy 4 | site_author: Spotware 5 | site_url: https://openapinet.readthedocs.io/en/latest/ 6 | site_description: cTrader Open API Python package documentation 7 | 8 | # Repository information 9 | repo_name: spotware/openApiPy 10 | repo_url: https://github.com/spotware/openApiPy 11 | edit_uri: "https://github.com/spotware/openApiPy/tree/master/docs" 12 | 13 | copyright: "Copyright © Spotware Systems Ltd. cTrader, cAlgo, cBroker, cMirror. All rights reserved." 14 | 15 | theme: 16 | name: material 17 | favicon: img/favicon.ico 18 | logo: img/logo.png 19 | include_search_page: false 20 | search_index_only: true 21 | language: en 22 | custom_dir: overrides 23 | features: 24 | - navigation.instant 25 | - navigation.top 26 | - navigation.tracking 27 | - search.highlight 28 | - search.share 29 | - search.suggest 30 | #- navigation.instant 31 | #- navigation.top 32 | #- navigation.tracking 33 | #- navigation.tabs 34 | #- navigation.sections 35 | ##- navigation.indexes 36 | ##- content.code.annotate 37 | ## - content.tabs.link 38 | ## - header.autohide 39 | ## - navigation.expand 40 | ## - navigation.instant 41 | ## - toc.integrate 42 | palette: 43 | - scheme: default 44 | primary: indigo 45 | accent: indigo 46 | toggle: 47 | icon: material/toggle-switch-off-outline 48 | name: Switch to dark mode 49 | - scheme: slate 50 | primary: indigo 51 | accent: indigo 52 | toggle: 53 | icon: material/toggle-switch 54 | name: Switch to light mode 55 | font: 56 | text: Roboto 57 | code: Roboto Mono 58 | static_templates: 59 | - 404.html 60 | 61 | plugins: 62 | - search 63 | - minify: 64 | minify_html: true 65 | 66 | extra: 67 | generator: false 68 | 69 | extra_css: 70 | - css/extra.css 71 | 72 | extra_javascript: 73 | - js/extra.js 74 | 75 | markdown_extensions: 76 | - admonition 77 | - abbr 78 | - attr_list 79 | - def_list 80 | - footnotes 81 | - meta 82 | - md_in_html 83 | - toc: 84 | permalink: true 85 | - pymdownx.arithmatex: 86 | generic: true 87 | - pymdownx.betterem: 88 | smart_enable: all 89 | - pymdownx.caret 90 | - pymdownx.critic 91 | - pymdownx.details 92 | - pymdownx.emoji: 93 | emoji_index: !!python/name:materialx.emoji.twemoji 94 | emoji_generator: !!python/name:materialx.emoji.to_svg 95 | - pymdownx.highlight 96 | - pymdownx.inlinehilite 97 | - pymdownx.keys 98 | - pymdownx.magiclink: 99 | repo_url_shorthand: true 100 | user: squidfunk 101 | repo: mkdocs-material 102 | - pymdownx.mark 103 | - pymdownx.smartsymbols 104 | - pymdownx.superfences: 105 | custom_fences: 106 | - name: mermaid 107 | class: mermaid 108 | format: !!python/name:pymdownx.superfences.fence_code_format 109 | - pymdownx.tasklist: 110 | custom_checkbox: true 111 | - pymdownx.tilde 112 | - pymdownx.tabbed: 113 | alternate_style: true 114 | 115 | # Page tree 116 | nav: 117 | - Getting Started: index.md 118 | - Authentication: 'authentication.md' 119 | - Client: 'client.md' 120 | - Samples: "https://github.com/spotware/OpenApiPy/tree/main/samples" 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {{ super() }} 5 | 6 | 7 |

{{ lang.t("meta.comments") }}

8 | 9 | 10 | 24 | 25 | 26 | 52 | {% endblock %} 53 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "appdirs" 3 | version = "1.4.4" 4 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 5 | category = "dev" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "astroid" 11 | version = "2.4.2" 12 | description = "An abstract syntax tree for Python with inference support." 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=3.5" 16 | 17 | [package.dependencies] 18 | lazy-object-proxy = ">=1.4.0,<1.5.0" 19 | six = ">=1.12,<2.0" 20 | typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} 21 | wrapt = ">=1.11,<2.0" 22 | 23 | [[package]] 24 | name = "atomicwrites" 25 | version = "1.4.0" 26 | description = "Atomic file writes." 27 | category = "dev" 28 | optional = false 29 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 30 | 31 | [[package]] 32 | name = "attrs" 33 | version = "20.3.0" 34 | description = "Classes Without Boilerplate" 35 | category = "dev" 36 | optional = false 37 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 38 | 39 | [package.extras] 40 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] 41 | docs = ["furo", "sphinx", "zope.interface"] 42 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] 43 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] 44 | 45 | [[package]] 46 | name = "black" 47 | version = "20.8b1" 48 | description = "The uncompromising code formatter." 49 | category = "dev" 50 | optional = false 51 | python-versions = ">=3.6" 52 | 53 | [package.dependencies] 54 | appdirs = "*" 55 | click = ">=7.1.2" 56 | mypy-extensions = ">=0.4.3" 57 | pathspec = ">=0.6,<1" 58 | regex = ">=2020.1.8" 59 | toml = ">=0.10.1" 60 | typed-ast = ">=1.4.0" 61 | typing-extensions = ">=3.7.4" 62 | 63 | [package.extras] 64 | colorama = ["colorama (>=0.4.3)"] 65 | d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] 66 | 67 | [[package]] 68 | name = "click" 69 | version = "7.1.2" 70 | description = "Composable command line interface toolkit" 71 | category = "dev" 72 | optional = false 73 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 74 | 75 | [[package]] 76 | name = "colorama" 77 | version = "0.4.4" 78 | description = "Cross-platform colored terminal text." 79 | category = "dev" 80 | optional = false 81 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 82 | 83 | [[package]] 84 | name = "coverage" 85 | version = "5.3" 86 | description = "Code coverage measurement for Python" 87 | category = "dev" 88 | optional = false 89 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 90 | 91 | [package.extras] 92 | toml = ["toml"] 93 | 94 | [[package]] 95 | name = "future" 96 | version = "0.18.2" 97 | description = "Clean single-source support for Python 3 and 2" 98 | category = "dev" 99 | optional = false 100 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 101 | 102 | [[package]] 103 | name = "importlib-metadata" 104 | version = "3.3.0" 105 | description = "Read metadata from Python packages" 106 | category = "dev" 107 | optional = false 108 | python-versions = ">=3.6" 109 | 110 | [package.dependencies] 111 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 112 | zipp = ">=0.5" 113 | 114 | [package.extras] 115 | docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] 116 | testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] 117 | 118 | [[package]] 119 | name = "iniconfig" 120 | version = "1.1.1" 121 | description = "iniconfig: brain-dead simple config-ini parsing" 122 | category = "dev" 123 | optional = false 124 | python-versions = "*" 125 | 126 | [[package]] 127 | name = "isort" 128 | version = "5.6.4" 129 | description = "A Python utility / library to sort Python imports." 130 | category = "dev" 131 | optional = false 132 | python-versions = ">=3.6,<4.0" 133 | 134 | [package.extras] 135 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 136 | requirements_deprecated_finder = ["pipreqs", "pip-api"] 137 | colors = ["colorama (>=0.4.3,<0.5.0)"] 138 | 139 | [[package]] 140 | name = "jinja2" 141 | version = "2.11.2" 142 | description = "A very fast and expressive template engine." 143 | category = "dev" 144 | optional = false 145 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 146 | 147 | [package.dependencies] 148 | MarkupSafe = ">=0.23" 149 | 150 | [package.extras] 151 | i18n = ["Babel (>=0.8)"] 152 | 153 | [[package]] 154 | name = "joblib" 155 | version = "1.0.0" 156 | description = "Lightweight pipelining with Python functions" 157 | category = "dev" 158 | optional = false 159 | python-versions = ">=3.6" 160 | 161 | [[package]] 162 | name = "lazy-object-proxy" 163 | version = "1.4.3" 164 | description = "A fast and thorough lazy object proxy." 165 | category = "dev" 166 | optional = false 167 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 168 | 169 | [[package]] 170 | name = "livereload" 171 | version = "2.6.3" 172 | description = "Python LiveReload is an awesome tool for web developers" 173 | category = "dev" 174 | optional = false 175 | python-versions = "*" 176 | 177 | [package.dependencies] 178 | six = "*" 179 | tornado = {version = "*", markers = "python_version > \"2.7\""} 180 | 181 | [[package]] 182 | name = "lunr" 183 | version = "0.5.8" 184 | description = "A Python implementation of Lunr.js" 185 | category = "dev" 186 | optional = false 187 | python-versions = "*" 188 | 189 | [package.dependencies] 190 | future = ">=0.16.0" 191 | nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} 192 | six = ">=1.11.0" 193 | 194 | [package.extras] 195 | languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] 196 | 197 | [[package]] 198 | name = "markdown" 199 | version = "3.3.3" 200 | description = "Python implementation of Markdown." 201 | category = "dev" 202 | optional = false 203 | python-versions = ">=3.6" 204 | 205 | [package.dependencies] 206 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 207 | 208 | [package.extras] 209 | testing = ["coverage", "pyyaml"] 210 | 211 | [[package]] 212 | name = "markupsafe" 213 | version = "1.1.1" 214 | description = "Safely add untrusted strings to HTML/XML markup." 215 | category = "dev" 216 | optional = false 217 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" 218 | 219 | [[package]] 220 | name = "mccabe" 221 | version = "0.6.1" 222 | description = "McCabe checker, plugin for flake8" 223 | category = "dev" 224 | optional = false 225 | python-versions = "*" 226 | 227 | [[package]] 228 | name = "mkdocs" 229 | version = "1.1.2" 230 | description = "Project documentation with Markdown." 231 | category = "dev" 232 | optional = false 233 | python-versions = ">=3.5" 234 | 235 | [package.dependencies] 236 | click = ">=3.3" 237 | Jinja2 = ">=2.10.1" 238 | livereload = ">=2.5.1" 239 | lunr = {version = "0.5.8", extras = ["languages"]} 240 | Markdown = ">=3.2.1" 241 | PyYAML = ">=3.10" 242 | tornado = ">=5.0" 243 | 244 | [[package]] 245 | name = "mkdocs-material" 246 | version = "6.1.7" 247 | description = "A Material Design theme for MkDocs" 248 | category = "dev" 249 | optional = false 250 | python-versions = "*" 251 | 252 | [package.dependencies] 253 | markdown = ">=3.2" 254 | mkdocs = ">=1.1" 255 | mkdocs-material-extensions = ">=1.0" 256 | Pygments = ">=2.4" 257 | pymdown-extensions = ">=7.0" 258 | 259 | [[package]] 260 | name = "mkdocs-material-extensions" 261 | version = "1.0.1" 262 | description = "Extension pack for Python Markdown." 263 | category = "dev" 264 | optional = false 265 | python-versions = ">=3.5" 266 | 267 | [package.dependencies] 268 | mkdocs-material = ">=5.0.0" 269 | 270 | [[package]] 271 | name = "mypy" 272 | version = "0.790" 273 | description = "Optional static typing for Python" 274 | category = "dev" 275 | optional = false 276 | python-versions = ">=3.5" 277 | 278 | [package.dependencies] 279 | mypy-extensions = ">=0.4.3,<0.5.0" 280 | typed-ast = ">=1.4.0,<1.5.0" 281 | typing-extensions = ">=3.7.4" 282 | 283 | [package.extras] 284 | dmypy = ["psutil (>=4.0)"] 285 | 286 | [[package]] 287 | name = "mypy-extensions" 288 | version = "0.4.3" 289 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 290 | category = "dev" 291 | optional = false 292 | python-versions = "*" 293 | 294 | [[package]] 295 | name = "nltk" 296 | version = "3.5" 297 | description = "Natural Language Toolkit" 298 | category = "dev" 299 | optional = false 300 | python-versions = "*" 301 | 302 | [package.dependencies] 303 | click = "*" 304 | joblib = "*" 305 | regex = "*" 306 | tqdm = "*" 307 | 308 | [package.extras] 309 | all = ["requests", "numpy", "python-crfsuite", "scikit-learn", "twython", "pyparsing", "scipy", "matplotlib", "gensim"] 310 | corenlp = ["requests"] 311 | machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"] 312 | plot = ["matplotlib"] 313 | tgrep = ["pyparsing"] 314 | twitter = ["twython"] 315 | 316 | [[package]] 317 | name = "packaging" 318 | version = "20.8" 319 | description = "Core utilities for Python packages" 320 | category = "dev" 321 | optional = false 322 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 323 | 324 | [package.dependencies] 325 | pyparsing = ">=2.0.2" 326 | 327 | [[package]] 328 | name = "pathspec" 329 | version = "0.8.1" 330 | description = "Utility library for gitignore style pattern matching of file paths." 331 | category = "dev" 332 | optional = false 333 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 334 | 335 | [[package]] 336 | name = "pluggy" 337 | version = "0.13.1" 338 | description = "plugin and hook calling mechanisms for python" 339 | category = "dev" 340 | optional = false 341 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 342 | 343 | [package.dependencies] 344 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 345 | 346 | [package.extras] 347 | dev = ["pre-commit", "tox"] 348 | 349 | [[package]] 350 | name = "py" 351 | version = "1.10.0" 352 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 353 | category = "dev" 354 | optional = false 355 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 356 | 357 | [[package]] 358 | name = "pygments" 359 | version = "2.7.3" 360 | description = "Pygments is a syntax highlighting package written in Python." 361 | category = "dev" 362 | optional = false 363 | python-versions = ">=3.5" 364 | 365 | [[package]] 366 | name = "pylint" 367 | version = "2.6.0" 368 | description = "python code static checker" 369 | category = "dev" 370 | optional = false 371 | python-versions = ">=3.5.*" 372 | 373 | [package.dependencies] 374 | astroid = ">=2.4.0,<=2.5" 375 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 376 | isort = ">=4.2.5,<6" 377 | mccabe = ">=0.6,<0.7" 378 | toml = ">=0.7.1" 379 | 380 | [[package]] 381 | name = "pymdown-extensions" 382 | version = "8.0.1" 383 | description = "Extension pack for Python Markdown." 384 | category = "dev" 385 | optional = false 386 | python-versions = ">=3.5" 387 | 388 | [package.dependencies] 389 | Markdown = ">=3.2" 390 | 391 | [[package]] 392 | name = "pyparsing" 393 | version = "2.4.7" 394 | description = "Python parsing module" 395 | category = "dev" 396 | optional = false 397 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 398 | 399 | [[package]] 400 | name = "pytest" 401 | version = "6.2.1" 402 | description = "pytest: simple powerful testing with Python" 403 | category = "dev" 404 | optional = false 405 | python-versions = ">=3.6" 406 | 407 | [package.dependencies] 408 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 409 | attrs = ">=19.2.0" 410 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 411 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 412 | iniconfig = "*" 413 | packaging = "*" 414 | pluggy = ">=0.12,<1.0.0a1" 415 | py = ">=1.8.2" 416 | toml = "*" 417 | 418 | [package.extras] 419 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 420 | 421 | [[package]] 422 | name = "pytest-cov" 423 | version = "2.10.1" 424 | description = "Pytest plugin for measuring coverage." 425 | category = "dev" 426 | optional = false 427 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 428 | 429 | [package.dependencies] 430 | coverage = ">=4.4" 431 | pytest = ">=4.6" 432 | 433 | [package.extras] 434 | testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] 435 | 436 | [[package]] 437 | name = "pyyaml" 438 | version = "5.3.1" 439 | description = "YAML parser and emitter for Python" 440 | category = "dev" 441 | optional = false 442 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 443 | 444 | [[package]] 445 | name = "regex" 446 | version = "2020.11.13" 447 | description = "Alternative regular expression module, to replace re." 448 | category = "dev" 449 | optional = false 450 | python-versions = "*" 451 | 452 | [[package]] 453 | name = "six" 454 | version = "1.15.0" 455 | description = "Python 2 and 3 compatibility utilities" 456 | category = "dev" 457 | optional = false 458 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 459 | 460 | [[package]] 461 | name = "toml" 462 | version = "0.10.2" 463 | description = "Python Library for Tom's Obvious, Minimal Language" 464 | category = "dev" 465 | optional = false 466 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 467 | 468 | [[package]] 469 | name = "tornado" 470 | version = "6.1" 471 | description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." 472 | category = "dev" 473 | optional = false 474 | python-versions = ">= 3.5" 475 | 476 | [[package]] 477 | name = "tqdm" 478 | version = "4.54.1" 479 | description = "Fast, Extensible Progress Meter" 480 | category = "dev" 481 | optional = false 482 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" 483 | 484 | [package.extras] 485 | dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown", "wheel"] 486 | 487 | [[package]] 488 | name = "typed-ast" 489 | version = "1.4.1" 490 | description = "a fork of Python 2 and 3 ast modules with type comment support" 491 | category = "dev" 492 | optional = false 493 | python-versions = "*" 494 | 495 | [[package]] 496 | name = "typing-extensions" 497 | version = "3.7.4.3" 498 | description = "Backported and Experimental Type Hints for Python 3.5+" 499 | category = "dev" 500 | optional = false 501 | python-versions = "*" 502 | 503 | [[package]] 504 | name = "wrapt" 505 | version = "1.12.1" 506 | description = "Module for decorators, wrappers and monkey patching." 507 | category = "dev" 508 | optional = false 509 | python-versions = "*" 510 | 511 | [[package]] 512 | name = "zipp" 513 | version = "3.4.0" 514 | description = "Backport of pathlib-compatible object wrapper for zip files" 515 | category = "dev" 516 | optional = false 517 | python-versions = ">=3.6" 518 | 519 | [package.extras] 520 | docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] 521 | testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] 522 | 523 | [metadata] 524 | lock-version = "1.1" 525 | python-versions = "^3.7" 526 | content-hash = "c5047428e67536122bbd0d3f5020e2d3bf0605ffd96aecaed3c0e7f800d31c25" 527 | 528 | [metadata.files] 529 | appdirs = [ 530 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, 531 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, 532 | ] 533 | astroid = [ 534 | {file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"}, 535 | {file = "astroid-2.4.2.tar.gz", hash = "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703"}, 536 | ] 537 | atomicwrites = [ 538 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 539 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 540 | ] 541 | attrs = [ 542 | {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, 543 | {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, 544 | ] 545 | black = [ 546 | {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, 547 | ] 548 | click = [ 549 | {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, 550 | {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, 551 | ] 552 | colorama = [ 553 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 554 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 555 | ] 556 | coverage = [ 557 | {file = "coverage-5.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270"}, 558 | {file = "coverage-5.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4"}, 559 | {file = "coverage-5.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9"}, 560 | {file = "coverage-5.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729"}, 561 | {file = "coverage-5.3-cp27-cp27m-win32.whl", hash = "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d"}, 562 | {file = "coverage-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418"}, 563 | {file = "coverage-5.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9"}, 564 | {file = "coverage-5.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5"}, 565 | {file = "coverage-5.3-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822"}, 566 | {file = "coverage-5.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097"}, 567 | {file = "coverage-5.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9"}, 568 | {file = "coverage-5.3-cp35-cp35m-win32.whl", hash = "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636"}, 569 | {file = "coverage-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f"}, 570 | {file = "coverage-5.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237"}, 571 | {file = "coverage-5.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54"}, 572 | {file = "coverage-5.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7"}, 573 | {file = "coverage-5.3-cp36-cp36m-win32.whl", hash = "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a"}, 574 | {file = "coverage-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d"}, 575 | {file = "coverage-5.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"}, 576 | {file = "coverage-5.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f"}, 577 | {file = "coverage-5.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c"}, 578 | {file = "coverage-5.3-cp37-cp37m-win32.whl", hash = "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751"}, 579 | {file = "coverage-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709"}, 580 | {file = "coverage-5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516"}, 581 | {file = "coverage-5.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f"}, 582 | {file = "coverage-5.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259"}, 583 | {file = "coverage-5.3-cp38-cp38-win32.whl", hash = "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82"}, 584 | {file = "coverage-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221"}, 585 | {file = "coverage-5.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978"}, 586 | {file = "coverage-5.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21"}, 587 | {file = "coverage-5.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24"}, 588 | {file = "coverage-5.3-cp39-cp39-win32.whl", hash = "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7"}, 589 | {file = "coverage-5.3-cp39-cp39-win_amd64.whl", hash = "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7"}, 590 | {file = "coverage-5.3.tar.gz", hash = "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0"}, 591 | ] 592 | future = [ 593 | {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, 594 | ] 595 | importlib-metadata = [ 596 | {file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"}, 597 | {file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"}, 598 | ] 599 | iniconfig = [ 600 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 601 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 602 | ] 603 | isort = [ 604 | {file = "isort-5.6.4-py3-none-any.whl", hash = "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7"}, 605 | {file = "isort-5.6.4.tar.gz", hash = "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"}, 606 | ] 607 | jinja2 = [ 608 | {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, 609 | {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, 610 | ] 611 | joblib = [ 612 | {file = "joblib-1.0.0-py3-none-any.whl", hash = "sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f"}, 613 | {file = "joblib-1.0.0.tar.gz", hash = "sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24"}, 614 | ] 615 | lazy-object-proxy = [ 616 | {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"}, 617 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442"}, 618 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win32.whl", hash = "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4"}, 619 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a"}, 620 | {file = "lazy_object_proxy-1.4.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d"}, 621 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a"}, 622 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win32.whl", hash = "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e"}, 623 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win_amd64.whl", hash = "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357"}, 624 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50"}, 625 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db"}, 626 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449"}, 627 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156"}, 628 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531"}, 629 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb"}, 630 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08"}, 631 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383"}, 632 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142"}, 633 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea"}, 634 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62"}, 635 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-win32.whl", hash = "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd"}, 636 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239"}, 637 | ] 638 | livereload = [ 639 | {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, 640 | ] 641 | lunr = [ 642 | {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, 643 | {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, 644 | ] 645 | markdown = [ 646 | {file = "Markdown-3.3.3-py3-none-any.whl", hash = "sha256:c109c15b7dc20a9ac454c9e6025927d44460b85bd039da028d85e2b6d0bcc328"}, 647 | {file = "Markdown-3.3.3.tar.gz", hash = "sha256:5d9f2b5ca24bc4c7a390d22323ca4bad200368612b5aaa7796babf971d2b2f18"}, 648 | ] 649 | markupsafe = [ 650 | {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, 651 | {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, 652 | {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, 653 | {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, 654 | {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, 655 | {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, 656 | {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, 657 | {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, 658 | {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, 659 | {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, 660 | {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, 661 | {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, 662 | {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, 663 | {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, 664 | {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, 665 | {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, 666 | {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, 667 | {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, 668 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, 669 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, 670 | {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, 671 | {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, 672 | {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, 673 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, 674 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, 675 | {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, 676 | {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, 677 | {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, 678 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, 679 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, 680 | {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, 681 | {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, 682 | {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, 683 | ] 684 | mccabe = [ 685 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 686 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 687 | ] 688 | mkdocs = [ 689 | {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, 690 | {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, 691 | ] 692 | mkdocs-material = [ 693 | {file = "mkdocs-material-6.1.7.tar.gz", hash = "sha256:44e28f5f4ac3728c80fc9831cbda428e807b6b38473130acc45da6e62ce7f866"}, 694 | {file = "mkdocs_material-6.1.7-py2.py3-none-any.whl", hash = "sha256:af993fac90a91bdce2385227f682abcdaa55434b6bfaff52a8b6f4dd56634f54"}, 695 | ] 696 | mkdocs-material-extensions = [ 697 | {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, 698 | {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"}, 699 | ] 700 | mypy = [ 701 | {file = "mypy-0.790-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669"}, 702 | {file = "mypy-0.790-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802"}, 703 | {file = "mypy-0.790-cp35-cp35m-win_amd64.whl", hash = "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de"}, 704 | {file = "mypy-0.790-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1"}, 705 | {file = "mypy-0.790-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc"}, 706 | {file = "mypy-0.790-cp36-cp36m-win_amd64.whl", hash = "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7"}, 707 | {file = "mypy-0.790-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"}, 708 | {file = "mypy-0.790-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178"}, 709 | {file = "mypy-0.790-cp37-cp37m-win_amd64.whl", hash = "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324"}, 710 | {file = "mypy-0.790-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01"}, 711 | {file = "mypy-0.790-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666"}, 712 | {file = "mypy-0.790-cp38-cp38-win_amd64.whl", hash = "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea"}, 713 | {file = "mypy-0.790-py3-none-any.whl", hash = "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122"}, 714 | {file = "mypy-0.790.tar.gz", hash = "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975"}, 715 | ] 716 | mypy-extensions = [ 717 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 718 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 719 | ] 720 | nltk = [ 721 | {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, 722 | ] 723 | packaging = [ 724 | {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, 725 | {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, 726 | ] 727 | pathspec = [ 728 | {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, 729 | {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, 730 | ] 731 | pluggy = [ 732 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 733 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 734 | ] 735 | py = [ 736 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, 737 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, 738 | ] 739 | pygments = [ 740 | {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"}, 741 | {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"}, 742 | ] 743 | pylint = [ 744 | {file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"}, 745 | {file = "pylint-2.6.0.tar.gz", hash = "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210"}, 746 | ] 747 | pymdown-extensions = [ 748 | {file = "pymdown-extensions-8.0.1.tar.gz", hash = "sha256:9ba704052d4bdc04a7cd63f7db4ef6add73bafcef22c0cf6b2e3386cf4ece51e"}, 749 | {file = "pymdown_extensions-8.0.1-py2.py3-none-any.whl", hash = "sha256:a3689c04f4cbddacd9d569425c571ae07e2673cc4df63a26cdbf1abc15229137"}, 750 | ] 751 | pyparsing = [ 752 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 753 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 754 | ] 755 | pytest = [ 756 | {file = "pytest-6.2.1-py3-none-any.whl", hash = "sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8"}, 757 | {file = "pytest-6.2.1.tar.gz", hash = "sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"}, 758 | ] 759 | pytest-cov = [ 760 | {file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"}, 761 | {file = "pytest_cov-2.10.1-py2.py3-none-any.whl", hash = "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191"}, 762 | ] 763 | pyyaml = [ 764 | {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, 765 | {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, 766 | {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, 767 | {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, 768 | {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, 769 | {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, 770 | {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, 771 | {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, 772 | {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, 773 | {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, 774 | {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, 775 | ] 776 | regex = [ 777 | {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, 778 | {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, 779 | {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, 780 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, 781 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, 782 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, 783 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, 784 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, 785 | {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, 786 | {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, 787 | {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, 788 | {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, 789 | {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, 790 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, 791 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, 792 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, 793 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, 794 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, 795 | {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, 796 | {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, 797 | {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, 798 | {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, 799 | {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, 800 | {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, 801 | {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, 802 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, 803 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, 804 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, 805 | {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, 806 | {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, 807 | {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, 808 | {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, 809 | {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, 810 | {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, 811 | {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, 812 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, 813 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, 814 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, 815 | {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, 816 | {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, 817 | {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, 818 | ] 819 | six = [ 820 | {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, 821 | {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, 822 | ] 823 | toml = [ 824 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 825 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 826 | ] 827 | tornado = [ 828 | {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, 829 | {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, 830 | {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, 831 | {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, 832 | {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, 833 | {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, 834 | {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, 835 | {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, 836 | {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, 837 | {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, 838 | {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, 839 | {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, 840 | {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, 841 | {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, 842 | {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, 843 | {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, 844 | {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, 845 | {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, 846 | {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, 847 | {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, 848 | {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, 849 | {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, 850 | {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, 851 | {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, 852 | {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, 853 | {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, 854 | {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, 855 | {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, 856 | {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, 857 | {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, 858 | {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, 859 | {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, 860 | {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, 861 | {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, 862 | {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, 863 | {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, 864 | {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, 865 | {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, 866 | {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, 867 | {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, 868 | {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, 869 | ] 870 | tqdm = [ 871 | {file = "tqdm-4.54.1-py2.py3-none-any.whl", hash = "sha256:d4f413aecb61c9779888c64ddf0c62910ad56dcbe857d8922bb505d4dbff0df1"}, 872 | {file = "tqdm-4.54.1.tar.gz", hash = "sha256:38b658a3e4ecf9b4f6f8ff75ca16221ae3378b2e175d846b6b33ea3a20852cf5"}, 873 | ] 874 | typed-ast = [ 875 | {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, 876 | {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, 877 | {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, 878 | {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, 879 | {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, 880 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, 881 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, 882 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f"}, 883 | {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, 884 | {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, 885 | {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, 886 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, 887 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, 888 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298"}, 889 | {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, 890 | {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, 891 | {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, 892 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, 893 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, 894 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d"}, 895 | {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, 896 | {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, 897 | {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, 898 | {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c"}, 899 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072"}, 900 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91"}, 901 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d"}, 902 | {file = "typed_ast-1.4.1-cp39-cp39-win32.whl", hash = "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395"}, 903 | {file = "typed_ast-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c"}, 904 | {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, 905 | ] 906 | typing-extensions = [ 907 | {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, 908 | {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, 909 | {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, 910 | ] 911 | wrapt = [ 912 | {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, 913 | ] 914 | zipp = [ 915 | {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, 916 | {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, 917 | ] 918 | -------------------------------------------------------------------------------- /poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | in-project = true 3 | [repositories] 4 | [repositories.testpypi] 5 | url = "https://test.pypi.org/legacy/" 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool] 2 | 3 | [tool.poetry] 4 | name = "ctrader_open_api" 5 | version = "0.0.0" 6 | homepage = "https://github.com/spotware/openApiPy" 7 | description = "A Python package for interacting with cTrader Open API" 8 | authors = ["Spotware "] 9 | documentation = "https://spotware.github.io/OpenApiPy" 10 | readme = "README.md" 11 | license = "MIT" 12 | classifiers=[ 13 | 'License :: OSI Approved :: MIT License', 14 | 'Natural Language :: English', 15 | 'Programming Language :: Python :: 3.8', 16 | ] 17 | include = [ 18 | "LICENSE" 19 | ] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.8" 23 | Twisted = "24.3.0" 24 | pyOpenSSL = "24.1.0" 25 | protobuf = "3.20.1" 26 | requests = "2.32.3" 27 | inputimeout = "1.0.4" 28 | 29 | [tool.poetry.dev-dependencies] 30 | Twisted = "24.3.0" 31 | pyOpenSSL = "24.1.0" 32 | protobuf = "3.20.1" 33 | requests = "2.32.3" 34 | inputimeout = "1.0.4" 35 | 36 | [tool.black] 37 | line-length=100 38 | 39 | [tool.pylint.reports] 40 | output-format="colorized" 41 | reports="y" 42 | include-ids="yes" 43 | msg-template="{msg_id}:{line:3d},{column}: {obj}: {msg}" 44 | 45 | [tool.pytest.ini_options] 46 | addopts = "--cov=ctrader_openApiPy --cov-branch --cov-report term-missing -vv --color=yes --cov-fail-under 100" 47 | python_files = "tests.py test_*.py *_tests.py" 48 | 49 | [build-system] 50 | requires = ["poetry-core>=1.0.8"] 51 | build-backend = "poetry.core.masonry.api" 52 | -------------------------------------------------------------------------------- /samples/ConsoleSample/README.md: -------------------------------------------------------------------------------- 1 | # Console Sample 2 | 3 | This is the console sample for Spotware OpenApiPy Python package. 4 | 5 | It uses a single thread which is the Python main execution thread for both getting user inputs and sending/receiving messages to/from API. 6 | 7 | Because it uses only the Python main execution thread the user input command has a time out, and if you don't enter your command on that specific time period it will block for few seconds and then it starts accepting user command again. 8 | 9 | This sample uses [inputimeout](https://pypi.org/project/inputimeout/) Python package, you have to install it before running the sample. 10 | -------------------------------------------------------------------------------- /samples/ConsoleSample/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from ctrader_open_api import Client, Protobuf, TcpProtocol, Auth, EndPoints 4 | from ctrader_open_api.endpoints import EndPoints 5 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import * 6 | from ctrader_open_api.messages.OpenApiMessages_pb2 import * 7 | from ctrader_open_api.messages.OpenApiModelMessages_pb2 import * 8 | from twisted.internet import reactor 9 | from inputimeout import inputimeout, TimeoutOccurred 10 | import webbrowser 11 | import datetime 12 | import calendar 13 | 14 | if __name__ == "__main__": 15 | currentAccountId = None 16 | hostType = input("Host (Live/Demo): ") 17 | hostType = hostType.lower() 18 | 19 | while hostType != "live" and hostType != "demo": 20 | print(f"{hostType} is not a valid host type.") 21 | hostType = input("Host (Live/Demo): ") 22 | 23 | appClientId = input("App Client ID: ") 24 | appClientSecret = input("App Client Secret: ") 25 | isTokenAvailable = input("Do you have an access token? (Y/N): ").lower() == "y" 26 | 27 | accessToken = None 28 | if isTokenAvailable == False: 29 | appRedirectUri = input("App Redirect URI: ") 30 | auth = Auth(appClientId, appClientSecret, appRedirectUri) 31 | authUri = auth.getAuthUri() 32 | print(f"Please continue the authentication on your browser:\n {authUri}") 33 | webbrowser.open_new(authUri) 34 | print("\nThen enter the auth code that is appended to redirect URI immediatly (the code is after ?code= in URI)") 35 | authCode = input("Auth Code: ") 36 | token = auth.getToken(authCode) 37 | if "accessToken" not in token: 38 | raise KeyError(token) 39 | print("Token: \n", token) 40 | accessToken = token["accessToken"] 41 | else: 42 | accessToken = input("Access Token: ") 43 | 44 | client = Client(EndPoints.PROTOBUF_LIVE_HOST if hostType.lower() == "live" else EndPoints.PROTOBUF_DEMO_HOST, EndPoints.PROTOBUF_PORT, TcpProtocol) 45 | 46 | def connected(client): # Callback for client connection 47 | print("\nConnected") 48 | request = ProtoOAApplicationAuthReq() 49 | request.clientId = appClientId 50 | request.clientSecret = appClientSecret 51 | deferred = client.send(request) 52 | deferred.addErrback(onError) 53 | 54 | def disconnected(client, reason): # Callback for client disconnection 55 | print("\nDisconnected: ", reason) 56 | 57 | def onMessageReceived(client, message): # Callback for receiving all messages 58 | if message.payloadType in [ProtoOASubscribeSpotsRes().payloadType, ProtoOAAccountLogoutRes().payloadType, ProtoHeartbeatEvent().payloadType]: 59 | return 60 | elif message.payloadType == ProtoOAApplicationAuthRes().payloadType: 61 | print("API Application authorized\n") 62 | print("Please use setAccount command to set the authorized account before sending any other command, try help for more detail\n") 63 | print("To get account IDs use ProtoOAGetAccountListByAccessTokenReq command") 64 | if currentAccountId is not None: 65 | sendProtoOAAccountAuthReq() 66 | return 67 | elif message.payloadType == ProtoOAAccountAuthRes().payloadType: 68 | protoOAAccountAuthRes = Protobuf.extract(message) 69 | print(f"Account {protoOAAccountAuthRes.ctidTraderAccountId} has been authorized\n") 70 | print("This acccount will be used for all future requests\n") 71 | print("You can change the account by using setAccount command") 72 | else: 73 | print("Message received: \n", Protobuf.extract(message)) 74 | reactor.callLater(3, callable=executeUserCommand) 75 | 76 | def onError(failure): # Call back for errors 77 | print("Message Error: ", failure) 78 | reactor.callLater(3, callable=executeUserCommand) 79 | 80 | def showHelp(): 81 | print("Commands (Parameters with an * are required), ignore the description inside ()") 82 | print("setAccount(For all subsequent requests this account will be used) *accountId") 83 | print("ProtoOAVersionReq clientMsgId") 84 | print("ProtoOAGetAccountListByAccessTokenReq clientMsgId") 85 | print("ProtoOAAssetListReq clientMsgId") 86 | print("ProtoOAAssetClassListReq clientMsgId") 87 | print("ProtoOASymbolCategoryListReq clientMsgId") 88 | print("ProtoOASymbolsListReq includeArchivedSymbols(True/False) clientMsgId") 89 | print("ProtoOATraderReq clientMsgId") 90 | print("ProtoOASubscribeSpotsReq *symbolId *timeInSeconds(Unsubscribes after this time) subscribeToSpotTimestamp(True/False) clientMsgId") 91 | print("ProtoOAReconcileReq clientMsgId") 92 | print("ProtoOAGetTrendbarsReq *weeks *period *symbolId clientMsgId") 93 | print("ProtoOAGetTickDataReq *days *type *symbolId clientMsgId") 94 | print("NewMarketOrder *symbolId *tradeSide *volume clientMsgId") 95 | print("NewLimitOrder *symbolId *tradeSide *volume *price clientMsgId") 96 | print("NewStopOrder *symbolId *tradeSide *volume *price clientMsgId") 97 | print("ClosePosition *positionId *volume clientMsgId") 98 | print("CancelOrder *orderId clientMsgId") 99 | print("DealOffsetList *dealId clientMsgId") 100 | print("GetPositionUnrealizedPnL clientMsgId") 101 | print("OrderDetails clientMsgId") 102 | print("OrderListByPositionId *positionId fromTimestamp toTimestamp clientMsgId") 103 | 104 | reactor.callLater(3, callable=executeUserCommand) 105 | 106 | def setAccount(accountId): 107 | global currentAccountId 108 | if currentAccountId is not None: 109 | sendProtoOAAccountLogoutReq() 110 | currentAccountId = int(accountId) 111 | sendProtoOAAccountAuthReq() 112 | 113 | def sendProtoOAVersionReq(clientMsgId = None): 114 | request = ProtoOAVersionReq() 115 | deferred = client.send(request, clientMsgId = clientMsgId) 116 | deferred.addErrback(onError) 117 | 118 | def sendProtoOAGetAccountListByAccessTokenReq(clientMsgId = None): 119 | request = ProtoOAGetAccountListByAccessTokenReq() 120 | request.accessToken = accessToken 121 | deferred = client.send(request, clientMsgId = clientMsgId) 122 | deferred.addErrback(onError) 123 | 124 | def sendProtoOAAccountLogoutReq(clientMsgId = None): 125 | request = ProtoOAAccountLogoutReq() 126 | request.ctidTraderAccountId = currentAccountId 127 | deferred = client.send(request, clientMsgId = clientMsgId) 128 | deferred.addErrback(onError) 129 | 130 | def sendProtoOAAccountAuthReq(clientMsgId = None): 131 | request = ProtoOAAccountAuthReq() 132 | request.ctidTraderAccountId = currentAccountId 133 | request.accessToken = accessToken 134 | deferred = client.send(request, clientMsgId = clientMsgId) 135 | deferred.addErrback(onError) 136 | 137 | def sendProtoOAAssetListReq(clientMsgId = None): 138 | request = ProtoOAAssetListReq() 139 | request.ctidTraderAccountId = currentAccountId 140 | deferred = client.send(request, clientMsgId = clientMsgId) 141 | deferred.addErrback(onError) 142 | 143 | def sendProtoOAAssetClassListReq(clientMsgId = None): 144 | request = ProtoOAAssetClassListReq() 145 | request.ctidTraderAccountId = currentAccountId 146 | deferred = client.send(request, clientMsgId = clientMsgId) 147 | deferred.addErrback(onError) 148 | 149 | def sendProtoOASymbolCategoryListReq(clientMsgId = None): 150 | request = ProtoOASymbolCategoryListReq() 151 | request.ctidTraderAccountId = currentAccountId 152 | deferred = client.send(request, clientMsgId = clientMsgId) 153 | deferred.addErrback(onError) 154 | 155 | def sendProtoOASymbolsListReq(includeArchivedSymbols = False, clientMsgId = None): 156 | request = ProtoOASymbolsListReq() 157 | request.ctidTraderAccountId = currentAccountId 158 | request.includeArchivedSymbols = includeArchivedSymbols if type(includeArchivedSymbols) is bool else bool(includeArchivedSymbols) 159 | deferred = client.send(request) 160 | deferred.addErrback(onError) 161 | 162 | def sendProtoOATraderReq(clientMsgId = None): 163 | request = ProtoOATraderReq() 164 | request.ctidTraderAccountId = currentAccountId 165 | deferred = client.send(request, clientMsgId = clientMsgId) 166 | deferred.addErrback(onError) 167 | 168 | def sendProtoOAUnsubscribeSpotsReq(symbolId, clientMsgId = None): 169 | request = ProtoOAUnsubscribeSpotsReq() 170 | request.ctidTraderAccountId = currentAccountId 171 | request.symbolId.append(int(symbolId)) 172 | deferred = client.send(request, clientMsgId = clientMsgId) 173 | deferred.addErrback(onError) 174 | 175 | def sendProtoOASubscribeSpotsReq(symbolId, timeInSeconds, subscribeToSpotTimestamp = False, clientMsgId = None): 176 | request = ProtoOASubscribeSpotsReq() 177 | request.ctidTraderAccountId = currentAccountId 178 | request.symbolId.append(int(symbolId)) 179 | request.subscribeToSpotTimestamp = subscribeToSpotTimestamp if type(subscribeToSpotTimestamp) is bool else bool(subscribeToSpotTimestamp) 180 | deferred = client.send(request, clientMsgId = clientMsgId) 181 | deferred.addErrback(onError) 182 | reactor.callLater(int(timeInSeconds), sendProtoOAUnsubscribeSpotsReq, symbolId) 183 | 184 | def sendProtoOAReconcileReq(clientMsgId = None): 185 | request = ProtoOAReconcileReq() 186 | request.ctidTraderAccountId = currentAccountId 187 | deferred = client.send(request, clientMsgId = clientMsgId) 188 | deferred.addErrback(onError) 189 | 190 | def sendProtoOAGetTrendbarsReq(weeks, period, symbolId, clientMsgId = None): 191 | request = ProtoOAGetTrendbarsReq() 192 | request.ctidTraderAccountId = currentAccountId 193 | request.period = ProtoOATrendbarPeriod.Value(period) 194 | request.fromTimestamp = int(calendar.timegm((datetime.datetime.utcnow() - datetime.timedelta(weeks=int(weeks))).utctimetuple())) * 1000 195 | request.toTimestamp = int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) * 1000 196 | request.symbolId = int(symbolId) 197 | deferred = client.send(request, clientMsgId = clientMsgId) 198 | deferred.addErrback(onError) 199 | 200 | def sendProtoOAGetTickDataReq(days, quoteType, symbolId, clientMsgId = None): 201 | request = ProtoOAGetTickDataReq() 202 | request.ctidTraderAccountId = currentAccountId 203 | request.type = ProtoOAQuoteType.Value(quoteType.upper()) 204 | request.fromTimestamp = int(calendar.timegm((datetime.datetime.utcnow() - datetime.timedelta(days=int(days))).utctimetuple())) * 1000 205 | request.toTimestamp = int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) * 1000 206 | request.symbolId = int(symbolId) 207 | deferred = client.send(request, clientMsgId = clientMsgId) 208 | deferred.addErrback(onError) 209 | 210 | def sendProtoOANewOrderReq(symbolId, orderType, tradeSide, volume, price = None, clientMsgId = None): 211 | request = ProtoOANewOrderReq() 212 | request.ctidTraderAccountId = currentAccountId 213 | request.symbolId = int(symbolId) 214 | request.orderType = ProtoOAOrderType.Value(orderType.upper()) 215 | request.tradeSide = ProtoOATradeSide.Value(tradeSide.upper()) 216 | request.volume = int(volume) * 100 217 | if request.orderType == ProtoOAOrderType.LIMIT: 218 | request.limitPrice = float(price) 219 | elif request.orderType == ProtoOAOrderType.STOP: 220 | request.stopPrice = float(price) 221 | deferred = client.send(request, clientMsgId = clientMsgId) 222 | deferred.addErrback(onError) 223 | 224 | def sendNewMarketOrder(symbolId, tradeSide, volume, clientMsgId = None): 225 | sendProtoOANewOrderReq(symbolId, "MARKET", tradeSide, volume, clientMsgId = clientMsgId) 226 | 227 | def sendNewLimitOrder(symbolId, tradeSide, volume, price, clientMsgId = None): 228 | sendProtoOANewOrderReq(symbolId, "LIMIT", tradeSide, volume, price, clientMsgId) 229 | 230 | def sendNewStopOrder(symbolId, tradeSide, volume, price, clientMsgId = None): 231 | sendProtoOANewOrderReq(symbolId, "STOP", tradeSide, volume, price, clientMsgId) 232 | 233 | def sendProtoOAClosePositionReq(positionId, volume, clientMsgId = None): 234 | request = ProtoOAClosePositionReq() 235 | request.ctidTraderAccountId = currentAccountId 236 | request.positionId = int(positionId) 237 | request.volume = int(volume) * 100 238 | deferred = client.send(request, clientMsgId = clientMsgId) 239 | deferred.addErrback(onError) 240 | 241 | def sendProtoOACancelOrderReq(orderId, clientMsgId = None): 242 | request = ProtoOACancelOrderReq() 243 | request.ctidTraderAccountId = currentAccountId 244 | request.orderId = int(orderId) 245 | deferred = client.send(request, clientMsgId = clientMsgId) 246 | deferred.addErrback(onError) 247 | 248 | def sendProtoOADealOffsetListReq(dealId, clientMsgId=None): 249 | request = ProtoOADealOffsetListReq() 250 | request.ctidTraderAccountId = currentAccountId 251 | request.dealId = int(dealId) 252 | deferred = client.send(request, clientMsgId=clientMsgId) 253 | deferred.addErrback(onError) 254 | 255 | def sendProtoOAGetPositionUnrealizedPnLReq(clientMsgId=None): 256 | request = ProtoOAGetPositionUnrealizedPnLReq() 257 | request.ctidTraderAccountId = currentAccountId 258 | deferred = client.send(request, clientMsgId=clientMsgId) 259 | deferred.addErrback(onError) 260 | 261 | def sendProtoOAOrderDetailsReq(orderId, clientMsgId=None): 262 | request = ProtoOAOrderDetailsReq() 263 | request.ctidTraderAccountId = currentAccountId 264 | request.orderId = int(orderId) 265 | deferred = client.send(request, clientMsgId=clientMsgId) 266 | deferred.addErrback(onError) 267 | 268 | def sendProtoOAOrderListByPositionIdReq(positionId, fromTimestamp=None, toTimestamp=None, clientMsgId=None): 269 | request = ProtoOAOrderListByPositionIdReq() 270 | request.ctidTraderAccountId = currentAccountId 271 | request.positionId = int(positionId) 272 | deferred = client.send(request, fromTimestamp=fromTimestamp, toTimestamp=toTimestamp, clientMsgId=clientMsgId) 273 | deferred.addErrback(onError) 274 | 275 | commands = { 276 | "help": showHelp, 277 | "setAccount": setAccount, 278 | "ProtoOAVersionReq": sendProtoOAVersionReq, 279 | "ProtoOAGetAccountListByAccessTokenReq": sendProtoOAGetAccountListByAccessTokenReq, 280 | "ProtoOAAssetListReq": sendProtoOAAssetListReq, 281 | "ProtoOAAssetClassListReq": sendProtoOAAssetClassListReq, 282 | "ProtoOASymbolCategoryListReq": sendProtoOASymbolCategoryListReq, 283 | "ProtoOASymbolsListReq": sendProtoOASymbolsListReq, 284 | "ProtoOATraderReq": sendProtoOATraderReq, 285 | "ProtoOASubscribeSpotsReq": sendProtoOASubscribeSpotsReq, 286 | "ProtoOAReconcileReq": sendProtoOAReconcileReq, 287 | "ProtoOAGetTrendbarsReq": sendProtoOAGetTrendbarsReq, 288 | "ProtoOAGetTickDataReq": sendProtoOAGetTickDataReq, 289 | "NewMarketOrder": sendNewMarketOrder, 290 | "NewLimitOrder": sendNewLimitOrder, 291 | "NewStopOrder": sendNewStopOrder, 292 | "ClosePosition": sendProtoOAClosePositionReq, 293 | "CancelOrder": sendProtoOACancelOrderReq, 294 | "DealOffsetList": sendProtoOADealOffsetListReq, 295 | "GetPositionUnrealizedPnL": sendProtoOAGetPositionUnrealizedPnLReq, 296 | "OrderDetails": sendProtoOAOrderDetailsReq, 297 | "OrderListByPositionId": sendProtoOAOrderListByPositionIdReq, 298 | } 299 | 300 | def executeUserCommand(): 301 | try: 302 | print("\n") 303 | userInput = inputimeout("Command (ex help): ", timeout=18) 304 | except TimeoutOccurred: 305 | print("Command Input Timeout") 306 | reactor.callLater(3, callable=executeUserCommand) 307 | return 308 | userInputSplit = userInput.split(" ") 309 | if not userInputSplit: 310 | print("Command split error: ", userInput) 311 | reactor.callLater(3, callable=executeUserCommand) 312 | return 313 | command = userInputSplit[0] 314 | try: 315 | parameters = [parameter if parameter[0] != "*" else parameter[1:] for parameter in userInputSplit[1:]] 316 | except: 317 | print("Invalid parameters: ", userInput) 318 | reactor.callLater(3, callable=executeUserCommand) 319 | if command in commands: 320 | commands[command](*parameters) 321 | else: 322 | print("Invalid Command: ", userInput) 323 | reactor.callLater(3, callable=executeUserCommand) 324 | 325 | # Setting optional client callbacks 326 | client.setConnectedCallback(connected) 327 | client.setDisconnectedCallback(disconnected) 328 | client.setMessageReceivedCallback(onMessageReceived) 329 | # Starting the client service 330 | client.startService() 331 | reactor.run() 332 | -------------------------------------------------------------------------------- /samples/KleinWebAppSample/README.md: -------------------------------------------------------------------------------- 1 | # Klein Web App Sample 2 | 3 | This is the web sample for Spotware OpenApiPy Python package. 4 | 5 | It's based on Twisted [Klein](https://github.com/twisted/klein) web framework. 6 | 7 | You can send and receive API commands by using this sample, it's very similar to Console sample. 8 | 9 | To use the sample you have to create a copy of "credentials.json" file and rename it to "credentials-dev.json". 10 | 11 | Then fill the file with your Open API application credentials. 12 | 13 | After that install Klein with pip: 14 | ``` 15 | pip install klein 16 | ``` 17 | 18 | Then run the "main.py" file. 19 | 20 | Open the "localhost:8080" on your web browser. 21 | 22 | You can escape the account authentication if you have already an access token, go to: "localhost:8080/client-area?token=your-access-token" 23 | 24 | ![Screenshot](screenshot.png) 25 | -------------------------------------------------------------------------------- /samples/KleinWebAppSample/credentials.json: -------------------------------------------------------------------------------- 1 | { 2 | "ClientId": "", 3 | "Secret": "", 4 | "Host": "demo" 5 | } 6 | -------------------------------------------------------------------------------- /samples/KleinWebAppSample/css/site.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/samples/KleinWebAppSample/css/site.css -------------------------------------------------------------------------------- /samples/KleinWebAppSample/js/site.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | const queryString = window.location.search; 3 | const urlParams = new URLSearchParams(queryString); 4 | const token = urlParams.get("token"); 5 | 6 | $("#sendButton").click(function () { 7 | $.getJSON(`/get-data?token=${token}&command=${$('#commandInput').val()}`, function (data, status, xhr) { 8 | response = "result" in data ? data["result"] : JSON.stringify(data) 9 | if ($("#outputTextarea").val() == "") { 10 | $("#outputTextarea").val(response + "\n").change() 11 | } 12 | else { 13 | $("#outputTextarea").val($("#outputTextarea").val() + "\n" + response + "\n").change() 14 | } 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /samples/KleinWebAppSample/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from klein import Klein 4 | from ctrader_open_api import Client, Protobuf, TcpProtocol, Auth, EndPoints 5 | from ctrader_open_api.endpoints import EndPoints 6 | from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import * 7 | from ctrader_open_api.messages.OpenApiMessages_pb2 import * 8 | from ctrader_open_api.messages.OpenApiModelMessages_pb2 import * 9 | from templates import AddAccountsElement, ClientAreaElement 10 | import json 11 | from twisted.internet import endpoints, reactor 12 | from twisted.web.server import Site 13 | import sys 14 | from twisted.python import log 15 | from twisted.web.static import File 16 | import datetime 17 | from google.protobuf.json_format import MessageToJson 18 | import calendar 19 | 20 | host = "localhost" 21 | port = 8080 22 | 23 | credentialsFile = open("credentials-dev.json") 24 | credentials = json.load(credentialsFile) 25 | token = "" 26 | currentAccountId = None 27 | 28 | auth = Auth(credentials["ClientId"], credentials["Secret"], f"http://{host}:{port}/redirect") 29 | authUri = auth.getAuthUri() 30 | app = Klein() 31 | 32 | @app.route('/') 33 | def root(request): 34 | return AddAccountsElement(authUri) 35 | 36 | @app.route('/redirect') 37 | def redirect(request): 38 | authCode = request.args.get(b"code", [None])[0] 39 | if (authCode is not None and authCode != b""): 40 | token = auth.getToken(authCode) 41 | if "errorCode" in token and token["errorCode"] is not None: 42 | return f'Error: {token["description"]}' 43 | else: 44 | return request.redirect(f'/client-area?token={token["access_token"]}') 45 | else: 46 | return "Error: Invalid/Empty Auth Code" 47 | 48 | @app.route('/client-area') 49 | def clientArea(request): 50 | global token 51 | token = request.args.get(b"token", [None])[0] 52 | if (token is None or token == b""): 53 | return "Error: Invalid/Empty Token" 54 | return ClientAreaElement() 55 | 56 | @app.route('/css/', branch=True) 57 | def css(request): 58 | return File("./css") 59 | 60 | @app.route('/js/', branch=True) 61 | def js(request): 62 | return File("./js") 63 | 64 | def onError(failure): 65 | print("Message Error: \n", failure) 66 | 67 | def connected(client): 68 | print("Client Connected") 69 | request = ProtoOAApplicationAuthReq() 70 | request.clientId = credentials["ClientId"] 71 | request.clientSecret = credentials["Secret"] 72 | deferred = client.send(request) 73 | deferred.addErrback(onError) 74 | 75 | def disconnected(client, reason): 76 | print("Client Disconnected, reason: \n", reason) 77 | 78 | def onMessageReceived(client, message): 79 | if message.payloadType == ProtoHeartbeatEvent().payloadType: 80 | return 81 | print("Client Received a Message: \n", message) 82 | 83 | authorizedAccounts = [] 84 | 85 | def setAccount(accountId): 86 | global currentAccountId 87 | currentAccountId = int(accountId) 88 | if accountId not in authorizedAccounts: 89 | return sendProtoOAAccountAuthReq(accountId) 90 | return "Account changed successfully" 91 | 92 | def sendProtoOAVersionReq(clientMsgId = None): 93 | request = ProtoOAVersionReq() 94 | deferred = client.send(request, clientMsgId = clientMsgId) 95 | deferred.addErrback(onError) 96 | return deferred 97 | 98 | def sendProtoOAGetAccountListByAccessTokenReq(clientMsgId = None): 99 | request = ProtoOAGetAccountListByAccessTokenReq() 100 | request.accessToken = token 101 | deferred = client.send(request, clientMsgId = clientMsgId) 102 | deferred.addErrback(onError) 103 | return deferred 104 | 105 | def sendProtoOAAccountLogoutReq(clientMsgId = None): 106 | request = ProtoOAAccountLogoutReq() 107 | request.ctidTraderAccountId = currentAccountId 108 | deferred = client.send(request, clientMsgId = clientMsgId) 109 | deferred.addErrback(onError) 110 | return deferred 111 | 112 | def sendProtoOAAccountAuthReq(clientMsgId = None): 113 | request = ProtoOAAccountAuthReq() 114 | request.ctidTraderAccountId = currentAccountId 115 | request.accessToken = token 116 | deferred = client.send(request, clientMsgId = clientMsgId) 117 | deferred.addErrback(onError) 118 | return deferred 119 | 120 | def sendProtoOAAssetListReq(clientMsgId = None): 121 | request = ProtoOAAssetListReq() 122 | request.ctidTraderAccountId = currentAccountId 123 | deferred = client.send(request, clientMsgId = clientMsgId) 124 | deferred.addErrback(onError) 125 | return deferred 126 | 127 | def sendProtoOAAssetClassListReq(clientMsgId = None): 128 | request = ProtoOAAssetClassListReq() 129 | request.ctidTraderAccountId = currentAccountId 130 | deferred = client.send(request, clientMsgId = clientMsgId) 131 | deferred.addErrback(onError) 132 | return deferred 133 | 134 | def sendProtoOASymbolCategoryListReq(clientMsgId = None): 135 | request = ProtoOASymbolCategoryListReq() 136 | request.ctidTraderAccountId = currentAccountId 137 | deferred = client.send(request, clientMsgId = clientMsgId) 138 | deferred.addErrback(onError) 139 | return deferred 140 | 141 | def sendProtoOASymbolsListReq(includeArchivedSymbols = False, clientMsgId = None): 142 | request = ProtoOASymbolsListReq() 143 | request.ctidTraderAccountId = currentAccountId 144 | request.includeArchivedSymbols = includeArchivedSymbols if type(includeArchivedSymbols) is bool else bool(includeArchivedSymbols) 145 | deferred = client.send(request) 146 | deferred.addErrback(onError) 147 | return deferred 148 | 149 | def sendProtoOATraderReq(clientMsgId = None): 150 | request = ProtoOATraderReq() 151 | request.ctidTraderAccountId = currentAccountId 152 | deferred = client.send(request, clientMsgId = clientMsgId) 153 | deferred.addErrback(onError) 154 | return deferred 155 | 156 | def sendProtoOAUnsubscribeSpotsReq(symbolId, clientMsgId = None): 157 | request = ProtoOAUnsubscribeSpotsReq() 158 | request.ctidTraderAccountId = currentAccountId 159 | request.symbolId.append(int(symbolId)) 160 | deferred = client.send(request, clientMsgId = clientMsgId) 161 | deferred.addErrback(onError) 162 | return deferred 163 | 164 | def sendProtoOAReconcileReq(clientMsgId = None): 165 | request = ProtoOAReconcileReq() 166 | request.ctidTraderAccountId = currentAccountId 167 | deferred = client.send(request, clientMsgId = clientMsgId) 168 | deferred.addErrback(onError) 169 | return deferred 170 | 171 | def sendProtoOAGetTrendbarsReq(weeks, period, symbolId, clientMsgId = None): 172 | request = ProtoOAGetTrendbarsReq() 173 | request.ctidTraderAccountId = currentAccountId 174 | request.period = ProtoOATrendbarPeriod.Value(period) 175 | request.fromTimestamp = int(calendar.timegm((datetime.datetime.utcnow() - datetime.timedelta(weeks=int(weeks))).utctimetuple())) * 1000 176 | request.toTimestamp = int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) * 1000 177 | request.symbolId = int(symbolId) 178 | deferred = client.send(request, clientMsgId = clientMsgId) 179 | deferred.addErrback(onError) 180 | return deferred 181 | 182 | def sendProtoOAGetTickDataReq(days, quoteType, symbolId, clientMsgId = None): 183 | request = ProtoOAGetTickDataReq() 184 | request.ctidTraderAccountId = currentAccountId 185 | request.type = ProtoOAQuoteType.Value(quoteType.upper()) 186 | request.fromTimestamp = int(calendar.timegm((datetime.datetime.utcnow() - datetime.timedelta(days=int(days))).utctimetuple())) * 1000 187 | request.toTimestamp = int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) * 1000 188 | request.symbolId = int(symbolId) 189 | deferred = client.send(request, clientMsgId = clientMsgId) 190 | deferred.addErrback(onError) 191 | return deferred 192 | 193 | def sendProtoOANewOrderReq(symbolId, orderType, tradeSide, volume, price = None, clientMsgId = None): 194 | request = ProtoOANewOrderReq() 195 | request.ctidTraderAccountId = currentAccountId 196 | request.symbolId = int(symbolId) 197 | request.orderType = ProtoOAOrderType.Value(orderType.upper()) 198 | request.tradeSide = ProtoOATradeSide.Value(tradeSide.upper()) 199 | request.volume = int(volume) * 100 200 | if request.orderType == ProtoOAOrderType.LIMIT: 201 | request.limitPrice = float(price) 202 | elif request.orderType == ProtoOAOrderType.STOP: 203 | request.stopPrice = float(price) 204 | deferred = client.send(request, clientMsgId = clientMsgId) 205 | deferred.addErrback(onError) 206 | return deferred 207 | 208 | def sendNewMarketOrder(symbolId, tradeSide, volume, clientMsgId = None): 209 | return sendProtoOANewOrderReq(symbolId, "MARKET", tradeSide, volume, clientMsgId = clientMsgId) 210 | 211 | def sendNewLimitOrder(symbolId, tradeSide, volume, price, clientMsgId = None): 212 | return sendProtoOANewOrderReq(symbolId, "LIMIT", tradeSide, volume, price, clientMsgId) 213 | 214 | def sendNewStopOrder(symbolId, tradeSide, volume, price, clientMsgId = None): 215 | return sendProtoOANewOrderReq(symbolId, "STOP", tradeSide, volume, price, clientMsgId) 216 | 217 | def sendProtoOAClosePositionReq(positionId, volume, clientMsgId = None): 218 | request = ProtoOAClosePositionReq() 219 | request.ctidTraderAccountId = currentAccountId 220 | request.positionId = int(positionId) 221 | request.volume = int(volume) * 100 222 | deferred = client.send(request, clientMsgId = clientMsgId) 223 | deferred.addErrback(onError) 224 | return deferred 225 | 226 | def sendProtoOACancelOrderReq(orderId, clientMsgId = None): 227 | request = ProtoOACancelOrderReq() 228 | request.ctidTraderAccountId = currentAccountId 229 | request.orderId = int(orderId) 230 | deferred = client.send(request, clientMsgId = clientMsgId) 231 | deferred.addErrback(onError) 232 | return deferred 233 | 234 | def sendProtoOADealOffsetListReq(dealId, clientMsgId=None): 235 | request = ProtoOADealOffsetListReq() 236 | request.ctidTraderAccountId = currentAccountId 237 | request.dealId = int(dealId) 238 | deferred = client.send(request, clientMsgId=clientMsgId) 239 | deferred.addErrback(onError) 240 | 241 | def sendProtoOAGetPositionUnrealizedPnLReq(clientMsgId=None): 242 | request = ProtoOAGetPositionUnrealizedPnLReq() 243 | request.ctidTraderAccountId = currentAccountId 244 | deferred = client.send(request, clientMsgId=clientMsgId) 245 | deferred.addErrback(onError) 246 | 247 | def sendProtoOAOrderDetailsReq(orderId, clientMsgId=None): 248 | request = ProtoOAOrderDetailsReq() 249 | request.ctidTraderAccountId = currentAccountId 250 | request.orderId = int(orderId) 251 | deferred = client.send(request, clientMsgId=clientMsgId) 252 | deferred.addErrback(onError) 253 | 254 | def sendProtoOAOrderListByPositionIdReq(positionId, fromTimestamp=None, toTimestamp=None, clientMsgId=None): 255 | request = ProtoOAOrderListByPositionIdReq() 256 | request.ctidTraderAccountId = currentAccountId 257 | request.positionId = int(positionId) 258 | deferred = client.send(request, fromTimestamp=fromTimestamp, toTimestamp=toTimestamp, clientMsgId=clientMsgId) 259 | deferred.addErrback(onError) 260 | 261 | commands = { 262 | "setAccount": setAccount, 263 | "ProtoOAVersionReq": sendProtoOAVersionReq, 264 | "ProtoOAGetAccountListByAccessTokenReq": sendProtoOAGetAccountListByAccessTokenReq, 265 | "ProtoOAAssetListReq": sendProtoOAAssetListReq, 266 | "ProtoOAAssetClassListReq": sendProtoOAAssetClassListReq, 267 | "ProtoOASymbolCategoryListReq": sendProtoOASymbolCategoryListReq, 268 | "ProtoOASymbolsListReq": sendProtoOASymbolsListReq, 269 | "ProtoOATraderReq": sendProtoOATraderReq, 270 | "ProtoOAReconcileReq": sendProtoOAReconcileReq, 271 | "ProtoOAGetTrendbarsReq": sendProtoOAGetTrendbarsReq, 272 | "ProtoOAGetTickDataReq": sendProtoOAGetTickDataReq, 273 | "NewMarketOrder": sendNewMarketOrder, 274 | "NewLimitOrder": sendNewLimitOrder, 275 | "NewStopOrder": sendNewStopOrder, 276 | "ClosePosition": sendProtoOAClosePositionReq, 277 | "CancelOrder": sendProtoOACancelOrderReq, 278 | "DealOffsetList": sendProtoOADealOffsetListReq, 279 | "GetPositionUnrealizedPnL": sendProtoOAGetPositionUnrealizedPnLReq, 280 | "OrderDetails": sendProtoOAOrderDetailsReq, 281 | "OrderListByPositionId": sendProtoOAOrderListByPositionIdReq, 282 | } 283 | 284 | def encodeResult(result): 285 | if type(result) is str: 286 | return f'{{"result": "{result}"}}'.encode(encoding = 'UTF-8') 287 | else: 288 | return MessageToJson(Protobuf.extract(result)).encode(encoding = 'UTF-8') 289 | 290 | @app.route('/get-data') 291 | def getData(request): 292 | request.responseHeaders.addRawHeader(b"content-type", b"application/json") 293 | token = request.args.get(b"token", [None])[0] 294 | result = "" 295 | if (token is None or token == b""): 296 | result = "Invalid Token" 297 | command = request.args.get(b"command", [None])[0] 298 | if (command is None or command == b""): 299 | result = f"Invalid Command: {command}" 300 | commandSplit = command.decode('UTF-8').split(" ") 301 | print(commandSplit) 302 | if (commandSplit[0] not in commands): 303 | result = f"Invalid Command: {commandSplit[0]}" 304 | else: 305 | parameters = commandSplit[1:] 306 | print(parameters) 307 | result = commands[commandSplit[0]](*parameters) 308 | result.addCallback(encodeResult) 309 | if type(result) is str: 310 | result = encodeResult(result) 311 | print(result) 312 | return result 313 | 314 | log.startLogging(sys.stdout) 315 | 316 | client = Client(EndPoints.PROTOBUF_LIVE_HOST if credentials["Host"].lower() == "live" else EndPoints.PROTOBUF_DEMO_HOST, EndPoints.PROTOBUF_PORT, TcpProtocol) 317 | client.setConnectedCallback(connected) 318 | client.setDisconnectedCallback(disconnected) 319 | client.setMessageReceivedCallback(onMessageReceived) 320 | client.startService() 321 | 322 | endpoint_description = f"tcp6:port={port}:interface={host}" 323 | endpoint = endpoints.serverFromString(reactor, endpoint_description) 324 | site = Site(app.resource()) 325 | site.displayTracebacks = True 326 | 327 | endpoint.listen(site) 328 | reactor.run() 329 | -------------------------------------------------------------------------------- /samples/KleinWebAppSample/markup/add_accounts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Open API Web App Sample 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /samples/KleinWebAppSample/markup/client_area.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Open API Web App Sample 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 |
35 |

36 | Welcome to cTrader Open API Pyhton Web App Sample 37 |

38 |

This is a sample web app built based on Twisted Klein framework.

39 |

You can send Open API commands and receive back the responses from API.

40 |

Commands (Parameters with an * are required):

41 |
    42 |
  • 43 | ProtoOAVersionReq: Returns the API server version 44 |
  • 45 |
  • 46 | setAccount *accountId: For all subsequent requests this account will be used 47 |
  • 48 |
  • 49 | ProtoOAGetAccountListByAccessTokenReq: Returns the list of authorized accounts for the token 50 |
  • 51 |
  • 52 | ProtoOAAssetListReq: Returns the list of account assets list 53 |
  • 54 |
  • 55 | ProtoOAAssetClassListReq: Returns the list of account asset classes 56 |
  • 57 |
  • 58 | ProtoOASymbolCategoryListReq: Returns the list of account symbol categories 59 |
  • 60 |
  • 61 | ProtoOASymbolsListReq: Returns the list of account symbols 62 |
  • 63 |
  • 64 | ProtoOATraderReq: Returns the token trader profile 65 |
  • 66 |
  • 67 | ProtoOAReconcileReq: Returns the account open positions/orders 68 |
  • 69 |
  • 70 | ProtoOAGetTrendbarsReq *weeks *period *symbolId: Returns the trend bar data of a symbol 71 |
  • 72 |
  • 73 | ProtoOAGetTickDataReq *days *type *symbolId: Returns the tick data of a symbol 74 |
  • 75 |
  • 76 | NewMarketOrder *symbolId *tradeSide *volume: Creates a new market order 77 |
  • 78 |
  • 79 | NewLimitOrder *symbolId *tradeSide *volume *price: Creates a new limit order 80 |
  • 81 |
  • 82 | NewStopOrder *symbolId *tradeSide *volume *price: Creates a new stop order 83 |
  • 84 |
  • 85 | ClosePosition *positionId *volume: Closes a position x amount of volume 86 |
  • 87 |
  • 88 | CancelOrder *orderId: Cancels a pending order 89 |
  • 90 |
91 |
92 |
93 | 94 | 95 | -------------------------------------------------------------------------------- /samples/KleinWebAppSample/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotware/OpenApiPy/e944996e6fe9436b8b4a1b668da4ac41a826bb85/samples/KleinWebAppSample/screenshot.png -------------------------------------------------------------------------------- /samples/KleinWebAppSample/templates.py: -------------------------------------------------------------------------------- 1 | from twisted.web.template import Element, renderer, XMLFile 2 | from twisted.python.filepath import FilePath 3 | 4 | class AddAccountsElement(Element): 5 | loader = XMLFile(FilePath('./markup/add_accounts.xml')) 6 | 7 | def __init__(self, addAccountLink): 8 | self.addAccountLink = addAccountLink 9 | super().__init__() 10 | 11 | @renderer 12 | def addAccountButton(self, request, tag): 13 | tag.fillSlots(addAccountLink=self.addAccountLink) 14 | return tag 15 | 16 | class ClientAreaElement(Element): 17 | loader = XMLFile(FilePath('./markup/client_area.xml')) 18 | -------------------------------------------------------------------------------- /samples/jupyter/README.md: -------------------------------------------------------------------------------- 1 | # Jupyter Sample 2 | 3 | This is sample that will show you how to use the OpenApiPy Python package on a Jupyter notebook. 4 | 5 | In the notebook we get the daily bars data from cTrader Open API, then we use it to train a sklearn model. 6 | 7 | To use the sample you have to create a copy of "credentials.json" file and rename it to "credentials-dev.json". 8 | 9 | Then fill the file with your Open API application credentials, access token, and a trading account ID. 10 | -------------------------------------------------------------------------------- /samples/jupyter/credentials.json: -------------------------------------------------------------------------------- 1 | { 2 | "ClientId": "", 3 | "Secret": "", 4 | "HostType": "demo", 5 | "AccessToken": "", 6 | "AccountId": 0 7 | } 8 | -------------------------------------------------------------------------------- /samples/jupyter/main.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "56f1559b", 6 | "metadata": {}, 7 | "source": [ 8 | "# OpenApiPy Jupyter Sample\n", 9 | "\n", 10 | "In this Jupyter notebook we will use the Python package \"ctrader-open-api\" to get daily trend bars data from cTrader Open API.\n", 11 | "\n", 12 | "Let's start." 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "id": "cca68e74", 18 | "metadata": {}, 19 | "source": [ 20 | "If you haven't already installed the \"ctrader-open-api\" package, run the next code cell to install it via pip:" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "id": "c53bfd78", 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "!pip install ctrader-open-api" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "id": "ca2b39c1", 36 | "metadata": {}, 37 | "source": [ 38 | "Then we have to import all necessary types:" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "id": "4ab2a19d", 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "from ctrader_open_api import Client, Protobuf, TcpProtocol, Auth, EndPoints\n", 49 | "from ctrader_open_api.messages.OpenApiCommonMessages_pb2 import *\n", 50 | "from ctrader_open_api.messages.OpenApiMessages_pb2 import *\n", 51 | "from ctrader_open_api.messages.OpenApiModelMessages_pb2 import *\n", 52 | "from twisted.internet import reactor\n", 53 | "import json\n", 54 | "import datetime\n", 55 | "import calendar" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "id": "d91f78fe", 61 | "metadata": {}, 62 | "source": [ 63 | "Now we use the \"credentials-dev.json\" file to get your Open API application credentials.\n", 64 | "Be sure to populate it with your API application credentials and access token before running next cell:" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "id": "2e67ade6", 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "credentialsFile = open(\"credentials-dev.json\")\n", 75 | "credentials = json.load(credentialsFile)" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "id": "f40aa449", 81 | "metadata": {}, 82 | "source": [ 83 | "Then we will create a client based our selected host type:" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "id": "a81eaee2", 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "host = EndPoints.PROTOBUF_LIVE_HOST if credentials[\"HostType\"].lower() == \"live\" else EndPoints.PROTOBUF_DEMO_HOST\n", 94 | "client = Client(host, EndPoints.PROTOBUF_PORT, TcpProtocol)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "id": "9e722b92", 100 | "metadata": {}, 101 | "source": [ 102 | "Now let's set the symbol name that you want to use, it must match to one of your trading account symbol names:" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "id": "8718a36a", 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "symbolName = \"EURUSD\"" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "id": "60d68c75", 118 | "metadata": {}, 119 | "source": [ 120 | "We will use the below list to store the daily bars data:" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "id": "cae2d042", 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "dailyBars = []" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "id": "dce5fdcd", 136 | "metadata": {}, 137 | "source": [ 138 | "We will use below method to transform the Open API trend bar to a tuple with bar open time, open price, high price, low price, and close price:" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "id": "1735a39c", 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "def transformTrendbar(trendbar):\n", 149 | " openTime = datetime.datetime.fromtimestamp(trendbar.utcTimestampInMinutes * 60, datetime.timezone.utc)\n", 150 | " openPrice = (trendbar.low + trendbar.deltaOpen) / 100000.0\n", 151 | " highPrice = (trendbar.low + trendbar.deltaHigh) / 100000.0\n", 152 | " lowPrice = trendbar.low / 100000.0\n", 153 | " closePrice = (trendbar.low + trendbar.deltaClose) / 100000.0\n", 154 | " return [openTime, openPrice, highPrice, lowPrice, closePrice, trendbar.volume]" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "id": "52058cf3", 160 | "metadata": {}, 161 | "source": [ 162 | "OpenApiPy uses Twisted to work asynchronously, so the execution flow is not sequential, we have to use a series of callbacks.\n", 163 | "\n", 164 | "The first callback we will use is the client \"Connected\" callback, its got triggered when client is connected and we should use it to send the Application authentication request.\n", 165 | "\n", 166 | "We will then use the Application authentication request returned Twisted deferred to set response callbacks, when we received the \"ProtoOAApplicationAuthRes\" the deferred assigned callback chain will be triggered.\n", 167 | "\n", 168 | "After we authenticated our API application then we will send an account authentication response, for the account ID you set on credentials file.\n", 169 | "\n", 170 | "Be sure your account type and host type match, otherwise account authentication will fail.\n", 171 | "\n", 172 | "Then we continue by using the retuned deferred to get trend bars data." 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "id": "cc492670", 178 | "metadata": {}, 179 | "source": [ 180 | "Now lets set the client callbacks:" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "id": "a41a6f36", 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "def trendbarsResponseCallback(result):\n", 191 | " print(\"\\nTrendbars received\")\n", 192 | " trendbars = Protobuf.extract(result)\n", 193 | " barsData = list(map(transformTrendbar, trendbars.trendbar))\n", 194 | " global dailyBars\n", 195 | " dailyBars.clear()\n", 196 | " dailyBars.extend(barsData)\n", 197 | " print(\"\\ndailyBars length:\", len(dailyBars))\n", 198 | " print(\"\\Stopping reactor...\")\n", 199 | " reactor.stop()\n", 200 | " \n", 201 | "def symbolsResponseCallback(result):\n", 202 | " print(\"\\nSymbols received\")\n", 203 | " symbols = Protobuf.extract(result)\n", 204 | " global symbolName\n", 205 | " symbolsFilterResult = list(filter(lambda symbol: symbol.symbolName == symbolName, symbols.symbol))\n", 206 | " if len(symbolsFilterResult) == 0:\n", 207 | " raise Exception(f\"There is symbol that matches to your defined symbol name: {symbolName}\")\n", 208 | " elif len(symbolsFilterResult) > 1:\n", 209 | " raise Exception(f\"More than one symbol matched with your defined symbol name: {symbolName}, match result: {symbolsFilterResult}\")\n", 210 | " symbol = symbolsFilterResult[0]\n", 211 | " request = ProtoOAGetTrendbarsReq()\n", 212 | " request.symbolId = symbol.symbolId\n", 213 | " request.ctidTraderAccountId = credentials[\"AccountId\"]\n", 214 | " request.period = ProtoOATrendbarPeriod.D1\n", 215 | " # We set the from/to time stamps to 50 weeks, you can load more data by sending multiple requests\n", 216 | " # Please check the ProtoOAGetTrendbarsReq documentation for more detail\n", 217 | " request.fromTimestamp = int(calendar.timegm((datetime.datetime.utcnow() - datetime.timedelta(weeks=50)).utctimetuple())) * 1000\n", 218 | " request.toTimestamp = int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) * 1000\n", 219 | " deferred = client.send(request)\n", 220 | " deferred.addCallbacks(trendbarsResponseCallback, onError)\n", 221 | " \n", 222 | "def accountAuthResponseCallback(result):\n", 223 | " print(\"\\nAccount authenticated\")\n", 224 | " request = ProtoOASymbolsListReq()\n", 225 | " request.ctidTraderAccountId = credentials[\"AccountId\"]\n", 226 | " request.includeArchivedSymbols = False\n", 227 | " deferred = client.send(request)\n", 228 | " deferred.addCallbacks(symbolsResponseCallback, onError)\n", 229 | " \n", 230 | "def applicationAuthResponseCallback(result):\n", 231 | " print(\"\\nApplication authenticated\")\n", 232 | " request = ProtoOAAccountAuthReq()\n", 233 | " request.ctidTraderAccountId = credentials[\"AccountId\"]\n", 234 | " request.accessToken = credentials[\"AccessToken\"]\n", 235 | " deferred = client.send(request)\n", 236 | " deferred.addCallbacks(accountAuthResponseCallback, onError)\n", 237 | "\n", 238 | "def onError(client, failure): # Call back for errors\n", 239 | " print(\"\\nMessage Error: \", failure)\n", 240 | "\n", 241 | "def disconnected(client, reason): # Callback for client disconnection\n", 242 | " print(\"\\nDisconnected: \", reason)\n", 243 | "\n", 244 | "def onMessageReceived(client, message): # Callback for receiving all messages\n", 245 | " if message.payloadType in [ProtoHeartbeatEvent().payloadType, ProtoOAAccountAuthRes().payloadType, ProtoOAApplicationAuthRes().payloadType, ProtoOASymbolsListRes().payloadType, ProtoOAGetTrendbarsRes().payloadType]:\n", 246 | " return\n", 247 | " print(\"\\nMessage received: \\n\", Protobuf.extract(message))\n", 248 | " \n", 249 | "def connected(client): # Callback for client connection\n", 250 | " print(\"\\nConnected\")\n", 251 | " request = ProtoOAApplicationAuthReq()\n", 252 | " request.clientId = credentials[\"ClientId\"]\n", 253 | " request.clientSecret = credentials[\"Secret\"]\n", 254 | " deferred = client.send(request)\n", 255 | " deferred.addCallbacks(applicationAuthResponseCallback, onError)\n", 256 | " \n", 257 | "# Setting optional client callbacks\n", 258 | "client.setConnectedCallback(connected)\n", 259 | "client.setDisconnectedCallback(disconnected)\n", 260 | "client.setMessageReceivedCallback(onMessageReceived)" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "id": "fbcde03f", 266 | "metadata": {}, 267 | "source": [ 268 | "The last step is to run our client service, it will run inside Twisted reactor loop asynchronously:" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "id": "98c9510e", 275 | "metadata": {}, 276 | "outputs": [], 277 | "source": [ 278 | "# Starting the client service\n", 279 | "client.startService()\n", 280 | "# Run Twisted reactor, we imported it earlier\n", 281 | "reactor.run()" 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "id": "c8e4a97b", 287 | "metadata": {}, 288 | "source": [ 289 | "If the above cell code executed without any error and the daily bars length printed as last message with more than 0 elements then it means we were able to successfully get the daily trend bars data." 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "id": "a7835d65", 295 | "metadata": {}, 296 | "source": [ 297 | "Now we have the price data, let's do some cool stuff with it, we will transform it to a pandas DataFrame:" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": null, 303 | "id": "5a75f17c", 304 | "metadata": {}, 305 | "outputs": [], 306 | "source": [ 307 | "import pandas as pd\n", 308 | "import numpy as np" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": null, 314 | "id": "6f6bdcd7", 315 | "metadata": {}, 316 | "outputs": [], 317 | "source": [ 318 | "df = pd.DataFrame(np.array(dailyBars),\n", 319 | " columns=['Time', 'Open', 'High', 'Low', 'Close', 'Volume'])\n", 320 | "df[\"Open\"] = pd.to_numeric(df[\"Open\"])\n", 321 | "df[\"High\"] = pd.to_numeric(df[\"High\"])\n", 322 | "df[\"Low\"] = pd.to_numeric(df[\"Low\"])\n", 323 | "df[\"Close\"] = pd.to_numeric(df[\"Close\"])\n", 324 | "df[\"Volume\"] = pd.to_numeric(df[\"Volume\"])" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "id": "63eda941", 330 | "metadata": {}, 331 | "source": [ 332 | "Now let's create a labels series, we will use it later for our ML model.\n", 333 | "This will have a 1 if the close price was higher than open price and 0 otherwise.\n", 334 | "We will use the today price data to predict the tomorrow bar type." 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "execution_count": null, 340 | "id": "02856070", 341 | "metadata": {}, 342 | "outputs": [], 343 | "source": [ 344 | "df[\"Labels\"] = (df[\"Close\"] > df[\"Open\"]).astype(int)\n", 345 | "df[\"Labels\"] = df[\"Labels\"].shift(-1)\n", 346 | "df.drop(df.tail(1).index,inplace=True)" 347 | ] 348 | }, 349 | { 350 | "cell_type": "markdown", 351 | "id": "46ad4ae2", 352 | "metadata": {}, 353 | "source": [ 354 | "Let's take a look on our data frame: " 355 | ] 356 | }, 357 | { 358 | "cell_type": "code", 359 | "execution_count": null, 360 | "id": "cb75ff4f", 361 | "metadata": {}, 362 | "outputs": [], 363 | "source": [ 364 | "df.tail()" 365 | ] 366 | }, 367 | { 368 | "cell_type": "markdown", 369 | "id": "5d5ce793", 370 | "metadata": {}, 371 | "source": [ 372 | "Now let's create a sklearn LogisticRegression model and use our data to train and test it:" 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "execution_count": null, 378 | "id": "fc9f1fe5", 379 | "metadata": {}, 380 | "outputs": [], 381 | "source": [ 382 | "from sklearn.linear_model import LogisticRegression\n", 383 | "from sklearn.model_selection import train_test_split\n", 384 | "from sklearn.metrics import accuracy_score\n", 385 | "\n", 386 | "model = LogisticRegression()\n", 387 | "\n", 388 | "x = df.loc[:, [\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n", 389 | "y = df.Labels\n", 390 | "\n", 391 | "x_train, x_test, y_train, y_test = train_test_split(x, y, train_size=0.7)\n", 392 | "\n", 393 | "model.fit(x_train, y_train)\n", 394 | "\n", 395 | "y_pred= model.predict(x_test)\n", 396 | "\n", 397 | "print(\"Our Model accuracy score is: \", accuracy_score(y_test, y_pred))" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": null, 403 | "id": "75402129", 404 | "metadata": {}, 405 | "outputs": [], 406 | "source": [] 407 | } 408 | ], 409 | "metadata": { 410 | "kernelspec": { 411 | "display_name": "Python 3 (ipykernel)", 412 | "language": "python", 413 | "name": "python3" 414 | }, 415 | "language_info": { 416 | "codemirror_mode": { 417 | "name": "ipython", 418 | "version": 3 419 | }, 420 | "file_extension": ".py", 421 | "mimetype": "text/x-python", 422 | "name": "python", 423 | "nbconvert_exporter": "python", 424 | "pygments_lexer": "ipython3", 425 | "version": "3.9.6" 426 | } 427 | }, 428 | "nbformat": 4, 429 | "nbformat_minor": 5 430 | } 431 | --------------------------------------------------------------------------------