diff --git a/.github/workflows/dxapi-python-build.yml b/.github/workflows/dxapi-python-build.yml index aef2b79..2cb77d2 100644 --- a/.github/workflows/dxapi-python-build.yml +++ b/.github/workflows/dxapi-python-build.yml @@ -13,6 +13,7 @@ on: - "!release-*" jobs: + # build dxapi build-dxapi: runs-on: ${{ matrix.os }} strategy: @@ -28,7 +29,7 @@ jobs: submodules: 'recursive' - name: Build Dxapi run: make -C ./dxapi/. - env: + env: CC: clang OS: ${{ matrix.env_os }} - name: Archive artifacts @@ -36,7 +37,7 @@ jobs: with: name: dxapi-${{ matrix.os }} path: ./dxapi/bin/libdxapi-x64.a - + build-windows-dxapi: runs-on: windows-2019 steps: @@ -53,8 +54,9 @@ jobs: with: name: dxapi-windows path: ./dxapi/bin/dxapi-x64.lib - - build-dxapi-python-linux: + + # build tbapi python + build-linux: runs-on: ubuntu-latest needs: [build-dxapi] strategy: @@ -94,7 +96,7 @@ jobs: sudo add-apt-repository ppa:deadsnakes/ppa sudo apt-get update sudo apt-get install python${{ matrix.py }}-dev - - name: Build Dxapi + - name: Build Tbapi python run: | make -C . cp ./dfp/lib/linux/64/libDecimalNative.so ./bin/release/linux/x64/py${{ matrix.py_env }}/ @@ -104,13 +106,13 @@ jobs: - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: dxapi-python-linux + name: tbapi-python-linux path: | ./bin/release/__init__.py - ./bin/release/linux/x64/py${{ matrix.py_env }}/_dxapi.so + ./bin/release/linux/x64/py${{ matrix.py_env }}/_tbapi.so ./bin/release/linux/x64/py${{ matrix.py_env }}/libDecimalNative.so - build-dxapi-python-windows: + build-windows: runs-on: windows-2019 needs: [build-windows-dxapi] strategy: @@ -152,17 +154,17 @@ jobs: python -c "import os, sys; print(os.path.dirname(sys.executable))" $Env:PYTHON${{ matrix.py_env }}_HOME=python -c "import os, sys; print(os.path.dirname(sys.executable))" $Env:PYTHON${{ matrix.py_env }}_HOME - msbuild ./dxapi-python.sln /p:configuration=Release${{ matrix.py_env }} /p:platform=x64 /t:rebuild + msbuild ./tbapi-python.sln /p:configuration=Release${{ matrix.py_env }} /p:platform=x64 /t:rebuild - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: dxapi-python-windows + name: tbapi-python-windows path: | ./bin/release/__init__.py - ./bin/release/windows/x64/py${{ matrix.py_env }}/_dxapi.pyd + ./bin/release/windows/x64/py${{ matrix.py_env }}/_tbapi.pyd ./bin/release/windows/x64/py${{ matrix.py_env }}/DecimalNative.dll - build-dxapi-python-macos: + build-macos: runs-on: macos-latest needs: [build-dxapi] strategy: @@ -172,23 +174,23 @@ jobs: - py: '3.6' py_env: '36' py_v: '3.6.15' - py_lib_prefix: '3.6m' + py_lib: 'python3.6m' - py: '3.7' py_env: '37' py_v: '3.7.12' - py_lib_prefix: '3.7m' + py_lib: 'python3.7m' - py: '3.8' py_env: '38' py_v: '3.8.12' - py_lib_prefix: '3.8' + py_lib: 'python3.8' - py: '3.9' py_env: '39' py_v: '3.9.10' - py_lib_prefix: '3.9' + py_lib: 'python3.9' - py: '3.10' py_env: '310' py_v: '3.10.2' - py_lib_prefix: '3.10' + py_lib: 'python3.10' steps: - name: Check out repository code uses: actions/checkout@v2 @@ -210,31 +212,78 @@ jobs: ./configure make make install - - name: Build Dxapi + - name: Build tbapi python run: | - mkdir /Library/Frameworks/Python.framework/Versions/${{ matrix.py }} - ln -s /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/include/python${{ matrix.py_lib_prefix }} /Library/Frameworks/Python.framework/Versions/${{ matrix.py }} - mv /Library/Frameworks/Python.framework/Versions/${{ matrix.py }}/python${{ matrix.py_lib_prefix }} /Library/Frameworks/Python.framework/Versions/${{ matrix.py }}/Headers - ln -s /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/lib /Library/Frameworks/Python.framework/Versions/${{ matrix.py }} make -C . cp ./dfp/lib/osx/64/libDecimalNative.dylib ./bin/release/darwin/x64/py${{ matrix.py_env }}/ + otool -L ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + install_name_tool -change /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/lib/lib${{ matrix.py_lib }}.dylib @rpath/lib${{ matrix.py_lib }}.dylib ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + otool -L ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + install_name_tool -add_rpath /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/lib ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + install_name_tool -add_rpath /usr/local/opt/python@${{ matrix.py }}/Frameworks/Python.framework/Versions/${{ matrix.py }}/lib ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + install_name_tool -add_rpath /Library/Frameworks/Python.framework/Versions/${{ matrix.py }}/lib ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + otool -l ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so env: CC: clang OS: MACOS PYTHON_VERSION: ${{ matrix.py_env }} + PYTHON_MACOS_HEADERS_PATH: /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/include/${{ matrix.py_lib }} + PYTHON_MACOS_LIB_PATH: /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/lib + PYTHON_MACOS_LIB: ${{ matrix.py_lib }} - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: dxapi-python-macos + name: tbapi-python-macos path: | ./bin/release/__init__.py - ./bin/release/darwin/x64/py${{ matrix.py_env }}/_dxapi.so + ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so ./bin/release/darwin/x64/py${{ matrix.py_env }}/libDecimalNative.dylib -# Tests - test-dxapi-python-linux: + # Gather artifacts + gather-artifacts: + runs-on: ubuntu-latest + needs: [build-linux, build-macos, build-windows] + steps: + - name: Check out repository code + uses: actions/checkout@v2 + - name: Download tbapi-python-linux artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python-linux + path: tbapi + - name: Download tbapi-python-windows artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python-windows + path: tbapi + - name: Download tbapi-python-windows artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python-macos + path: tbapi + - uses: actions/setup-python@v2 + with: + python-version: "3.10.2" + - name: Copy version file + run: | + # version file + cp ./project.properties ./tbapi/ + # pydoc + pip3 install pydoc-markdown + export LC_ALL=C.UTF-8 + export LANG=C.UTF-8 + pydoc-markdown > ./tbapi/tbapi.md + - name: Archive artifacts + uses: actions/upload-artifact@v2 + with: + name: tbapi-python + path: | + ./tbapi + + # Tests + test-linux: runs-on: ubuntu-latest - needs: [build-dxapi-python-linux] + needs: [gather-artifacts] strategy: matrix: py: ['3.6', '3.7', '3.8', '3.9', '3.10'] @@ -250,11 +299,11 @@ jobs: uses: actions/checkout@v2 with: submodules: 'recursive' - - name: Download dxapi-python-linux artifacts + - name: Download tbapi-python artifacts uses: actions/download-artifact@v2 with: - name: dxapi-python-linux - path: tests/dxapi + name: tbapi-python + path: tests/tbapi - uses: actions/setup-python@v2 with: python-version: '${{ matrix.py }}' @@ -262,6 +311,7 @@ jobs: run: | chmod -R 777 ./tests cd ./tests + python -c "import tbapi; print(tbapi.version())" python TestAll.py env: TIMEBASE_HOST: localhost @@ -269,6 +319,67 @@ jobs: - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: test-reports-dxapi-python-linux + name: test-reports-tbapi-python-linux path: | ./tests/reports + + test-windows-smoke: + runs-on: windows-2019 + needs: [gather-artifacts] + strategy: + matrix: + py: ['3.6', '3.7', '3.8', '3.9', '3.10'] + steps: + - name: Check out repository code + uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Download tbapi-python artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python + path: tests/tbapi + - uses: actions/setup-python@v2 + with: + python-version: '${{ matrix.py }}' + - name: Run tests + run: | + chmod -R 777 ./tests + cd ./tests + python -c "import tbapi; print(tbapi.version())" + + test-macos-smoke: + runs-on: macos-latest + needs: [gather-artifacts] + strategy: + matrix: + py: ['3.6', '3.7', '3.8', '3.9', '3.10'] + include: + - py: '3.6' + py_v: '3.6.15' + - py: '3.7' + py_v: '3.7.12' + - py: '3.8' + py_v: '3.8.12' + - py: '3.9' + py_v: '3.9.10' + - py: '3.10' + py_v: '3.10.2' + steps: + - name: Check out repository code + uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Download tbapi-python artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python + path: tests/tbapi + - uses: actions/setup-python@v2 + with: + python-version: '${{ matrix.py_v }}' + - name: Run tests + run: | + chmod -R 777 ./tests + cd ./tests + python -c "import tbapi; print(tbapi.version())" diff --git a/.github/workflows/dxapi-python-release.yml b/.github/workflows/dxapi-python-release.yml index 4b07005..76fd74c 100644 --- a/.github/workflows/dxapi-python-release.yml +++ b/.github/workflows/dxapi-python-release.yml @@ -5,7 +5,6 @@ on: branches: [release-*] jobs: - prepare: if: ${{ !contains(github.event.head_commit.message, '[skip-ci]') }} runs-on: ubuntu-latest @@ -27,6 +26,7 @@ jobs: git commit -am "[skip-ci] Generate next snapshot version" git push origin HEAD + # build dxapi build-dxapi: runs-on: ${{ matrix.os }} strategy: @@ -68,7 +68,8 @@ jobs: name: dxapi-windows path: ./dxapi/bin/dxapi-x64.lib - build-dxapi-python-linux: + # build tbapi python + build-linux: runs-on: ubuntu-latest needs: [build-dxapi] strategy: @@ -108,7 +109,7 @@ jobs: sudo add-apt-repository ppa:deadsnakes/ppa sudo apt-get update sudo apt-get install python${{ matrix.py }}-dev - - name: Build Dxapi + - name: Build Tbapi python run: | make -C . cp ./dfp/lib/linux/64/libDecimalNative.so ./bin/release/linux/x64/py${{ matrix.py_env }}/ @@ -118,13 +119,13 @@ jobs: - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: dxapi-python-linux + name: tbapi-python-linux path: | ./bin/release/__init__.py - ./bin/release/linux/x64/py${{ matrix.py_env }}/_dxapi.so + ./bin/release/linux/x64/py${{ matrix.py_env }}/_tbapi.so ./bin/release/linux/x64/py${{ matrix.py_env }}/libDecimalNative.so - build-dxapi-python-windows: + build-windows: runs-on: windows-2019 needs: [build-windows-dxapi] strategy: @@ -166,17 +167,17 @@ jobs: python -c "import os, sys; print(os.path.dirname(sys.executable))" $Env:PYTHON${{ matrix.py_env }}_HOME=python -c "import os, sys; print(os.path.dirname(sys.executable))" $Env:PYTHON${{ matrix.py_env }}_HOME - msbuild ./dxapi-python.sln /p:configuration=Release${{ matrix.py_env }} /p:platform=x64 /t:rebuild + msbuild ./tbapi-python.sln /p:configuration=Release${{ matrix.py_env }} /p:platform=x64 /t:rebuild - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: dxapi-python-windows + name: tbapi-python-windows path: | ./bin/release/__init__.py - ./bin/release/windows/x64/py${{ matrix.py_env }}/_dxapi.pyd + ./bin/release/windows/x64/py${{ matrix.py_env }}/_tbapi.pyd ./bin/release/windows/x64/py${{ matrix.py_env }}/DecimalNative.dll - build-dxapi-python-macos: + build-macos: runs-on: macos-latest needs: [build-dxapi] strategy: @@ -186,23 +187,23 @@ jobs: - py: '3.6' py_env: '36' py_v: '3.6.15' - py_lib_prefix: '3.6m' + py_lib: 'python3.6m' - py: '3.7' py_env: '37' py_v: '3.7.12' - py_lib_prefix: '3.7m' + py_lib: 'python3.7m' - py: '3.8' py_env: '38' py_v: '3.8.12' - py_lib_prefix: '3.8' + py_lib: 'python3.8' - py: '3.9' py_env: '39' py_v: '3.9.10' - py_lib_prefix: '3.9' + py_lib: 'python3.9' - py: '3.10' py_env: '310' py_v: '3.10.2' - py_lib_prefix: '3.10' + py_lib: 'python3.10' steps: - name: Check out repository code uses: actions/checkout@v2 @@ -224,82 +225,211 @@ jobs: ./configure make make install - - name: Build Dxapi + - name: Build tbapi python run: | - mkdir /Library/Frameworks/Python.framework/Versions/${{ matrix.py }} - ln -s /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/include/python${{ matrix.py_lib_prefix }} /Library/Frameworks/Python.framework/Versions/${{ matrix.py }} - mv /Library/Frameworks/Python.framework/Versions/${{ matrix.py }}/python${{ matrix.py_lib_prefix }} /Library/Frameworks/Python.framework/Versions/${{ matrix.py }}/Headers - ln -s /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/lib /Library/Frameworks/Python.framework/Versions/${{ matrix.py }} make -C . cp ./dfp/lib/osx/64/libDecimalNative.dylib ./bin/release/darwin/x64/py${{ matrix.py_env }}/ + otool -L ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + install_name_tool -change /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/lib/lib${{ matrix.py_lib }}.dylib @rpath/lib${{ matrix.py_lib }}.dylib ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + otool -L ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + install_name_tool -add_rpath /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/lib ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + install_name_tool -add_rpath /usr/local/opt/python@${{ matrix.py }}/Frameworks/Python.framework/Versions/${{ matrix.py }}/lib ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + install_name_tool -add_rpath /Library/Frameworks/Python.framework/Versions/${{ matrix.py }}/lib ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so + otool -l ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so env: CC: clang OS: MACOS PYTHON_VERSION: ${{ matrix.py_env }} + PYTHON_MACOS_HEADERS_PATH: /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/include/${{ matrix.py_lib }} + PYTHON_MACOS_LIB_PATH: /Users/runner/hostedtoolcache/Python/${{ matrix.py_v }}/x64/lib + PYTHON_MACOS_LIB: ${{ matrix.py_lib }} - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: dxapi-python-macos + name: tbapi-python-macos path: | ./bin/release/__init__.py - ./bin/release/darwin/x64/py${{ matrix.py_env }}/_dxapi.so + ./bin/release/darwin/x64/py${{ matrix.py_env }}/_tbapi.so ./bin/release/darwin/x64/py${{ matrix.py_env }}/libDecimalNative.dylib + # Gather artifacts gather-artifacts: runs-on: ubuntu-latest - needs: [build-dxapi-python-linux, build-dxapi-python-macos] + needs: [build-linux, build-macos, build-windows] steps: - name: Check out repository code uses: actions/checkout@v2 - - name: Download dxapi-python-linux artifacts + - name: Download tbapi-python-linux artifacts uses: actions/download-artifact@v2 with: - name: dxapi-python-linux - path: ./dxapi/ - - name: Download dxapi-python-macos artifacts + name: tbapi-python-linux + path: tbapi + - name: Download tbapi-python-windows artifacts uses: actions/download-artifact@v2 with: - name: dxapi-python-macos - path: ./dxapi/ -# - name: Download dxapi-python-windows artifacts -# uses: actions/download-artifact@v2 -# with: -# name: dxapi-python-windows -# path: ./dxapi/ + name: tbapi-python-windows + path: tbapi + - name: Download tbapi-python-windows artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python-macos + path: tbapi + - uses: actions/setup-python@v2 + with: + python-version: "3.10.2" + - name: Copy version file + run: | + # version file + cp ./project.properties ./tbapi/ + # pydoc + pip3 install pydoc-markdown + export LC_ALL=C.UTF-8 + export LANG=C.UTF-8 + pydoc-markdown > ./tbapi/tbapi.md + pip3 install wheel + python3 setup.py bdist_wheel --universal - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: dxapi-python + name: tbapi-python path: | - ./dxapi/__init__.py - ./dxapi/**/* - -## Tests + ./tbapi + - name: Wheel artifacts + uses: actions/upload-artifact@v2 + with: + name: tbapi-wheel + path: | + ./dist - release: - if: ${{ !contains(github.event.head_commit.message, '[skip-ci]') }} - needs: [gather-artifacts] + # Tests + test-linux: runs-on: ubuntu-latest + needs: [gather-artifacts] + strategy: + matrix: + py: ['3.6', '3.7', '3.8', '3.9', '3.10'] + services: + timebase: + image: finos/timebase-ce-server:6.1 + env: + JAVA_OPTS: "-DQuantServer.enableRemoteMonitoring=true -DTimeBase.version=5.0" + ports: + - 8011:8011 steps: - - name: Checkout code + - name: Check out repository code uses: actions/checkout@v2 - - name: Release branch + with: + submodules: 'recursive' + - name: Download tbapi-python artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python + path: tests/tbapi + - uses: actions/setup-python@v2 + with: + python-version: '${{ matrix.py }}' + - name: Run tests run: | - git config user.name github-actions - git config user.email github-actions@github.com - git fetch - git checkout -b workflow-$GITHUB_RUN_ID origin/workflow-$GITHUB_RUN_ID~1 - versionRelease=`grep 'version=' project.properties | sed 's/version=\([^-]*\)/\1/'` - echo $versionRelease - echo $versionRelease >> ./version.txt - git push origin origin/workflow-$GITHUB_RUN_ID:$GITHUB_REF - - name: Version artifact + chmod -R 777 ./tests + cd ./tests + python -c "import tbapi; print(tbapi.version())" + python TestAll.py + env: + TIMEBASE_HOST: localhost + TIMEBASE_PORT: 8011 + - name: Archive artifacts uses: actions/upload-artifact@v2 with: - name: version-file - path: ./version.txt - - publish: + name: test-reports-tbapi-python-linux + path: | + ./tests/reports + + test-windows-smoke: + runs-on: windows-2019 + needs: [gather-artifacts] + strategy: + matrix: + py: ['3.6', '3.7', '3.8', '3.9', '3.10'] + steps: + - name: Check out repository code + uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Download tbapi-python artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python + path: tests/tbapi + - uses: actions/setup-python@v2 + with: + python-version: '${{ matrix.py }}' + - name: Run tests + run: | + chmod -R 777 ./tests + cd ./tests + python -c "import tbapi; print(tbapi.version())" + + test-macos-smoke: + runs-on: macos-latest + needs: [gather-artifacts] + strategy: + matrix: + py: ['3.6', '3.7', '3.8', '3.9', '3.10'] + include: + - py: '3.6' + py_v: '3.6.15' + - py: '3.7' + py_v: '3.7.12' + - py: '3.8' + py_v: '3.8.12' + - py: '3.9' + py_v: '3.9.10' + - py: '3.10' + py_v: '3.10.2' + steps: + - name: Check out repository code + uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Download tbapi-python artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python + path: tests/tbapi + - uses: actions/setup-python@v2 + with: + python-version: '${{ matrix.py_v }}' + - name: Run tests + run: | + chmod -R 777 ./tests + cd ./tests + python -c "import tbapi; print(tbapi.version())" + + # Publish + publish-github: + needs: [gather-artifacts, test-linux, test-windows-smoke, test-macos-smoke] + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Download tbapi-python artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-python + - name: Download tbapi-python artifacts + uses: actions/download-artifact@v2 + with: + name: tbapi-wheel + path: dist + - name: Publish github + uses: ncipollo/release-action@v1 + with: + artifacts: "timebase_client-*.whl, LICENSE" + prerelease: true + tag: ${{ needs.package-nix.outputs.tag }} + token: ${{ secrets.GITHUB_TOKEN }} + + publish-pip: needs: [release] runs-on: windows-latest steps: @@ -316,10 +446,7 @@ jobs: name: version-file - name: Build PyPi package run: | - $Version = get-content .\version.txt - echo $Version pip install wheel - python3 setup.py bdist_wheel --universal # python3 -m twine upload --repository-url $Env:REPOSITORY_URL --username $Env:REPOSITORY_USER --password $Env:REPOSITORY_PASSWORD} dist/* env: REPOSITORY_URL: add repository url diff --git a/.gitignore b/.gitignore index 0e8f811..1e80c8a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +timebase_client.egg-info/ +dist/ + # User-specific files *.suo *.user diff --git a/Makefile b/Makefile index f2f5da5..276cddb 100644 --- a/Makefile +++ b/Makefile @@ -3,30 +3,30 @@ OS?=LINUX DXAPI_BIN=./dxapi/bin -PYTHONAPI_INTERFACE=dxapi.py -PYHTONAPI_LIB=_dxapi.so +PYTHONAPI_INTERFACE=tbapi.py +PYHTONAPI_LIB=_tbapi.so -PYTHON_VERSION?=36 +PYTHON_VERSION?=39 ifeq ($(PYTHON_VERSION),37) - PYTHON=python3.7 - PYTHON_VERSION_FULL=3.7 - PYTHON_LIB_SUFFIX=3.7m + PYTHON=python3.7 + PYTHON_VERSION_FULL=3.7 + PYTHON_MACOS_LIB?=python3.7m else ifeq ($(PYTHON_VERSION),38) - PYTHON=python3.8 - PYTHON_VERSION_FULL=3.8 - PYTHON_LIB_SUFFIX=3.8 + PYTHON=python3.8 + PYTHON_VERSION_FULL=3.8 + PYTHON_MACOS_LIB?=python3.8 else ifeq ($(PYTHON_VERSION),39) - PYTHON=python3.9 - PYTHON_VERSION_FULL=3.9 - PYTHON_LIB_SUFFIX=3.9 + PYTHON=python3.9 + PYTHON_VERSION_FULL=3.9 + PYTHON_MACOS_LIB?=python3.9 else ifeq ($(PYTHON_VERSION),310) - PYTHON=python3.10 - PYTHON_VERSION_FULL=3.10 - PYTHON_LIB_SUFFIX=3.10 + PYTHON=python3.10 + PYTHON_VERSION_FULL=3.10 + PYTHON_MACOS_LIB?=python3.10 else - PYTHON=python3.6 - PYTHON_VERSION_FULL=3.6 - PYTHON_LIB_SUFFIX=3.6m + PYTHON=python3.6 + PYTHON_VERSION_FULL=3.6 + PYTHON_MACOS_LIB?=python3.6m endif ifeq ($(OS),MACOS) @@ -34,8 +34,11 @@ ifeq ($(OS),MACOS) DFP_LIB=DecimalNative RPATH_PARAM= THIRD_PARTY_LIBS= - PYTHON_INCLUDES=/Library/Frameworks/Python.framework/Versions/$(PYTHON_VERSION_FULL)/Headers - PYTHON_LIBS=-L/Library/Frameworks/Python.framework/Versions/$(PYTHON_VERSION_FULL)/lib -lpython$(PYTHON_LIB_SUFFIX) + PYTHON_MACOS_PATH?=/Library/Frameworks/Python.framework/Versions/$(PYTHON_VERSION_FULL) + PYTHON_MACOS_HEADERS_PATH?=$(PYTHON_MACOS_PATH)/Headers + PYTHON_MACOS_LIB_PATH?=$(PYTHON_MACOS_PATH)/lib + PYTHON_INCLUDES=$(PYTHON_MACOS_HEADERS_PATH) + PYTHON_LIBS=-L$(PYTHON_MACOS_LIB_PATH) -l$(PYTHON_MACOS_LIB) BIN_SUBFOLDER=darwin else DFP_BIN=./dfp/lib/linux/64 @@ -50,7 +53,7 @@ BUILD_TARGETS=$(BINDIR)/$(PYHTONAPI_LIB) CLEAN_TARGETS=clean-$(PYHTONAPI_LIB) clean-$(PYTHONAPI_INTERFACE) # wrapper -WRAPPER_OBJ=dxapi_wrap +WRAPPER_OBJ=tbapi_wrap # Names of C/C++ source files to build, without path/extension OBJ_LIB=common python_common tick_cursor tick_loader message_codec $(WRAPPER_OBJ) @@ -163,9 +166,9 @@ $(OUTDIRS): mkdir -p $@ # python wrapper -$(WRAPPER_OBJDIR)/$(WRAPPER_OBJ).o: dxapi.i - swig -c++ -python -I./dxapi/include/native/dxapi -o $(WRAPDIR)/$(WRAPPER_OBJ).cxx -outdir $(INIT_PY_DIR) src/swig/dxapi.i - cp $(INIT_PY_DIR)/dxapi.py $(INIT_PY_DIR)/__init__.py +$(WRAPPER_OBJDIR)/$(WRAPPER_OBJ).o: tbapi.i + swig -c++ -python -I./dxapi/include/native/dxapi -o $(WRAPDIR)/$(WRAPPER_OBJ).cxx -outdir $(INIT_PY_DIR) src/swig/tbapi.i + cp $(INIT_PY_DIR)/tbapi.py $(INIT_PY_DIR)/__init__.py $(CXX) -c $(CXXFLAGS) -o $@ $(WRAPDIR)/$(WRAPPER_OBJ).cxx # c++ files @@ -180,12 +183,12 @@ $(OBJDIR)/%.o: %.c #============================================================================== # build lib -DXAPI_PYTHON_LIB_PATH=$(BINDIR)/$(PYHTONAPI_LIB) -DXAPI_PYTHON_LIB_OBJ_PATHS=$(OBJ_LIB:%=$(OBJDIR)/%.o) +TBAPI_PYTHON_LIB_PATH=$(BINDIR)/$(PYHTONAPI_LIB) +TBAPI_PYTHON_LIB_OBJ_PATHS=$(OBJ_LIB:%=$(OBJDIR)/%.o) # linking -$(BINDIR)/$(PYHTONAPI_LIB): $(DXAPI_PYTHON_LIB_OBJ_PATHS) - $(CXX) -shared $(DXAPI_PYTHON_LIB_OBJ_PATHS) $(LDFLAGS) -L$(DXAPI_BIN)/ -L$(DFP_BIN)/ -l$(DXAPI_LIB) -l$(DFP_LIB) $(RPATH_PARAM) $(PYTHON_LIBS) $(THIRD_PARTY_LIBS) -o $@ +$(BINDIR)/$(PYHTONAPI_LIB): $(TBAPI_PYTHON_LIB_OBJ_PATHS) + $(CXX) -shared $(TBAPI_PYTHON_LIB_OBJ_PATHS) $(LDFLAGS) -L$(DXAPI_BIN)/ -L$(DFP_BIN)/ -l$(DXAPI_LIB) -l$(DFP_LIB) $(RPATH_PARAM) $(PYTHON_LIBS) $(THIRD_PARTY_LIBS) -o $@ $(CLEAN_TARGETS): -rm $(@:clean-%=$(BINDIR)/%) diff --git a/project.properties b/project.properties index 2bcf672..d23e890 100644 --- a/project.properties +++ b/project.properties @@ -1 +1 @@ -version=6.0.15-SNAPSHOT +version=6.0.15-SNAPSHOT \ No newline at end of file diff --git a/projects/dxapi-python.vcxproj b/projects/tbapi-python.vcxproj similarity index 90% rename from projects/dxapi-python.vcxproj rename to projects/tbapi-python.vcxproj index 9c2669f..214b7d3 100644 --- a/projects/dxapi-python.vcxproj +++ b/projects/tbapi-python.vcxproj @@ -44,58 +44,58 @@ - + Document - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\release\__init__.py" -ren "$(ProjectDir)..\bin\release\dxapi.py" "__init__.py" - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\release\tbapi.py" "__init__.py" + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\release\__init__.py" -ren "$(ProjectDir)..\bin\release\dxapi.py" "__init__.py" - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\release\tbapi.py" "__init__.py" + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\release\__init__.py" -ren "$(ProjectDir)..\bin\release\dxapi.py" "__init__.py" - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\release\tbapi.py" "__init__.py" + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\release\__init__.py" -ren "$(ProjectDir)..\bin\release\dxapi.py" "__init__.py" - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\release\tbapi.py" "__init__.py" + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\release" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\release\__init__.py" -ren "$(ProjectDir)..\bin\release\dxapi.py" "__init__.py" - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\release\tbapi.py" "__init__.py" + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\release\__init__.py + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\debug\__init__.py" -ren "$(ProjectDir)..\bin\debug\dxapi.py" "__init__.py" - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\debug\tbapi.py" "__init__.py" + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\debug\__init__.py" -ren "$(ProjectDir)..\bin\debug\dxapi.py" "__init__.py" - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\debug\tbapi.py" "__init__.py" + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\debug\__init__.py" -ren "$(ProjectDir)..\bin\debug\dxapi.py" "__init__.py" - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\debug\tbapi.py" "__init__.py" + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\debug\__init__.py" -ren "$(ProjectDir)..\bin\debug\dxapi.py" "__init__.py" - rm "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) +ren "$(ProjectDir)..\bin\debug\tbapi.py" "__init__.py" + rm "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" +"$(SWIG_HOME)\swig.exe" -c++ -python -I$(ProjectDir)..\..\dxapi\include\native\dxapi -o "$(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx" -outdir "$(ProjectDir)..\bin\debug" -Fmicrosoft %(FullPath) del /f "$(ProjectDir)..\bin\debug\__init__.py" -ren "$(ProjectDir)..\bin\debug\dxapi.py" "__init__.py" - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py - $(ProjectDir)..\src\swig\wrappers\dxapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py +ren "$(ProjectDir)..\bin\debug\tbapi.py" "__init__.py" + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py + $(ProjectDir)..\src\swig\wrappers\tbapi_wrap.cxx;$(ProjectDir)..\bin\debug\__init__.py @@ -107,7 +107,7 @@ ren "$(ProjectDir)..\bin\debug\dxapi.py" "__init__.py" - + @@ -123,6 +123,7 @@ ren "$(ProjectDir)..\bin\debug\dxapi.py" "__init__.py" {F06CE5AA-B67E-4E38-8473-283FEBE7AE33} dxapipython 10.0.18362.0 + tbapi-python @@ -234,66 +235,66 @@ ren "$(ProjectDir)..\bin\debug\dxapi.py" "__init__.py" $(ProjectDir)../bin/debug/windows/$(PlatformTarget)/py36 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ - _dxapi_d + _tbapi_d .pyd $(ProjectDir)../bin/debug/windows/$(PlatformTarget)/py37 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ - _dxapi_d + _tbapi_d .pyd $(ProjectDir)../bin/debug/windows/$(PlatformTarget)/py38 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ - _dxapi_d + _tbapi_d .pyd $(ProjectDir)../bin/debug/windows/$(PlatformTarget)/py39 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ - _dxapi_d + _tbapi_d .pyd $(ProjectDir)../bin/debug/windows/$(PlatformTarget)/py310 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ - _dxapi_d + _tbapi_d .pyd $(ProjectDir)../bin/release/windows/$(PlatformTarget)/py36 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ .pyd - _dxapi + _tbapi $(ProjectDir)../bin/release/windows/$(PlatformTarget)/py37 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ .pyd - _dxapi + _tbapi $(ProjectDir)../bin/release/windows/$(PlatformTarget)/py38 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ .pyd - _dxapi + _tbapi $(ProjectDir)../bin/release/windows/$(PlatformTarget)/py39 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ .pyd - _dxapi + _tbapi $(ProjectDir)../bin/release/windows/$(PlatformTarget)/py310 $(ProjectDir)../obj/$(PlatformTarget)/$(Configuration)/$(ProjectName)/ .pyd - _dxapi + _tbapi diff --git a/projects/dxapi-python.vcxproj.filters b/projects/tbapi-python.vcxproj.filters similarity index 95% rename from projects/dxapi-python.vcxproj.filters rename to projects/tbapi-python.vcxproj.filters index ada277c..4d75740 100644 --- a/projects/dxapi-python.vcxproj.filters +++ b/projects/tbapi-python.vcxproj.filters @@ -35,14 +35,11 @@ - + swig - - swig\wrappers - src @@ -58,6 +55,9 @@ src + + swig\wrappers + diff --git a/pydoc-markdown.yml b/pydoc-markdown.yml index 251ab29..cee6ed2 100644 --- a/pydoc-markdown.yml +++ b/pydoc-markdown.yml @@ -3,9 +3,9 @@ renderer: loaders: - type: python - search_path: [./bin] + search_path: [.] modules: - - dxapi + - tbapi renderer: type: markdown diff --git a/setup.py b/setup.py index 37d5651..2a20313 100644 --- a/setup.py +++ b/setup.py @@ -1,35 +1,42 @@ -import os.path as path from setuptools import setup, find_packages, dist -import sys +import configparser -with open(path.join(path.abspath(path.dirname(__file__)), 'README.md')) as f: - long_description = f.read() +with open('project.properties', 'r') as f: + config_string = '[version_section]\n' + f.read() + +config = configparser.ConfigParser() +config.read_string(config_string) -project_version = open('version.txt', 'r').read() - -setup(name='dxapi', - version=project_version, +setup(name='timebase-client', + version=config['version_section']['version'], packages=find_packages(), - package_data={'dxapi': ['windows/x64/py27/*.pyd', + package_data={'tbapi': ['project.properties', + 'tbapi.md', 'windows/x64/py36/*.pyd', 'windows/x64/py37/*.pyd', 'windows/x64/py38/*.pyd', - 'windows/x64/py27/*.dll', + 'windows/x64/py39/*.pyd', + 'windows/x64/py310/*.pyd', 'windows/x64/py36/*.dll', 'windows/x64/py37/*.dll', 'windows/x64/py38/*.dll', - 'linux/x64/py27/*.so', + 'windows/x64/py39/*.dll', + 'windows/x64/py310/*.dll', 'linux/x64/py36/*.so', 'linux/x64/py37/*.so', 'linux/x64/py38/*.so', - 'darwin/x64/py27/*.so', + 'linux/x64/py39/*.so', + 'linux/x64/py310/*.so', 'darwin/x64/py36/*.so', 'darwin/x64/py37/*.so', 'darwin/x64/py38/*.so', - 'darwin/x64/py27/*.dylib', + 'darwin/x64/py39/*.so', + 'darwin/x64/py310/*.so', 'darwin/x64/py36/*.dylib', 'darwin/x64/py37/*.dylib', - 'darwin/x64/py38/*.dylib']}, + 'darwin/x64/py38/*.dylib', + 'darwin/x64/py39/*.dylib', + 'darwin/x64/py310/*.dylib']}, include_package_data=True, zip_safe=False, description='Python Timebase client API', @@ -42,11 +49,12 @@ "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Operating System :: MacOS", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8" + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10" ], - python_requires='>=2.7,!=2.8.*,!=2.9.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,<3.9', + python_requires='>=3.6,<3.11', platforms=['Windows', 'Linux', 'MacOS'] ) diff --git a/src/codecs/field_codecs.h b/src/codecs/field_codecs.h index cc09831..49ab8b8 100644 --- a/src/codecs/field_codecs.h +++ b/src/codecs/field_codecs.h @@ -37,7 +37,7 @@ uint64_t dfp_fromDouble(double value) { #endif -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { class FieldCodec { @@ -904,9 +904,9 @@ class ArrayFieldCodec : public FieldCodec { class ObjectFieldCodec : public FieldCodec { public: ObjectFieldCodec( - const char* field_name, PythonDxApiModule *dxapi_module, + const char* field_name, PythonTbApiModule *tbapi_module, std::vector types, std::vector codecs) - : FieldCodec(field_name), dxapi_module_(dxapi_module), types_(types), codecs_(codecs) + : FieldCodec(field_name), tbapi_module_(tbapi_module), types_(types), codecs_(codecs) { for (int i = 0; i < types.size(); ++i) { type_name_to_id_[types[i]] = i; @@ -921,10 +921,10 @@ class ObjectFieldCodec : public FieldCodec { if (type_id < 0 || type_id >= codecs_.size()) THROW_EXCEPTION("Can't find codec of type id '%d' for field: %s.", type_id, field_name_.c_str()); - if (dxapi_module_ == NULL) + if (tbapi_module_ == NULL) THROW("DxApi module is not initialized for object codec."); - PyObject *object = dxapi_module_->newInstrumentMessageObject(); + PyObject *object = tbapi_module_->newInstrumentMessageObject(); codecs_[type_id]->decode(object, reader); reader.readObjectEnd(); @@ -971,7 +971,7 @@ class ObjectFieldCodec : public FieldCodec { } private: - PythonDxApiModule *dxapi_module_; + PythonTbApiModule *tbapi_module_; std::vector types_; std::vector codecs_; std::unordered_map type_name_to_id_; diff --git a/src/codecs/message_codec.cpp b/src/codecs/message_codec.cpp index cf8a644..c3719dd 100644 --- a/src/codecs/message_codec.cpp +++ b/src/codecs/message_codec.cpp @@ -6,16 +6,16 @@ #include #include -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { MessageCodec::MessageCodec(const ClassDescriptors &descriptors, intptr_t num) { buildDecoders(descriptors, num); } -MessageCodec::MessageCodec(PythonDxApiModule *dxapi_module, const ClassDescriptors &descriptors, intptr_t num) +MessageCodec::MessageCodec(PythonTbApiModule *tbapi_module, const ClassDescriptors &descriptors, intptr_t num) { - dxapi_module_ = dxapi_module; + tbapi_module_ = tbapi_module; buildDecoders(descriptors, num); } @@ -193,12 +193,12 @@ FieldCodecPtr MessageCodec::createFieldCodec( if (index == INT32_MIN) THROW_EXCEPTION("Unknown object type: %s.", type.c_str()); - codecs.push_back(MessageCodecPtr(new MessageCodec(dxapi_module_, descriptors, index))); + codecs.push_back(MessageCodecPtr(new MessageCodec(tbapi_module_, descriptors, index))); types.push_back(descriptors[index].className); } return FieldCodecPtr( - new ObjectFieldCodec(field_name.c_str(), dxapi_module_, types, codecs) + new ObjectFieldCodec(field_name.c_str(), tbapi_module_, types, codecs) ); } else if (!temptype.compare(0, strlen("alphanumeric("), "alphanumeric(")) { diff --git a/src/codecs/message_codec.h b/src/codecs/message_codec.h index 7694424..f2ad116 100644 --- a/src/codecs/message_codec.h +++ b/src/codecs/message_codec.h @@ -10,11 +10,11 @@ #include #include -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { class FieldCodec; -class PythonDxApiModule; +class PythonTbApiModule; typedef std::vector ClassDescriptors; typedef std::shared_ptr FieldCodecPtr; @@ -22,7 +22,7 @@ typedef std::shared_ptr FieldCodecPtr; class MessageCodec { public: MessageCodec(const ClassDescriptors &descriptors, intptr_t num); - MessageCodec(PythonDxApiModule *dxapi_module, const ClassDescriptors &descriptors, intptr_t num); + MessageCodec(PythonTbApiModule *tbapi_module, const ClassDescriptors &descriptors, intptr_t num); ~MessageCodec(); void decode(PyObject *message, DxApi::DataReader &reader); @@ -51,7 +51,7 @@ class MessageCodec { private: std::vector field_codecs_; std::unordered_map codecs_search_map_; - PythonDxApiModule *dxapi_module_ = NULL; + PythonTbApiModule *tbapi_module_ = NULL; }; diff --git a/src/common.cpp b/src/common.cpp index e10860c..0f51c8c 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -4,7 +4,7 @@ #include #include -namespace DxApiImpl { +namespace TbApiImpl { std::string string_format(const std::string fmt_str, ...) { int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */ @@ -26,4 +26,4 @@ std::string string_format(const std::string fmt_str, ...) { return std::string(formatted.get()); } -} // namespace DxApiImpl \ No newline at end of file +} // namespace TbApiImpl \ No newline at end of file diff --git a/src/common.h b/src/common.h index b03dd7f..31e0ea5 100644 --- a/src/common.h +++ b/src/common.h @@ -5,7 +5,7 @@ #include #include -namespace DxApiImpl { +namespace TbApiImpl { std::string string_format(const std::string format, ...); @@ -30,6 +30,6 @@ class MutexHolder { std::mutex *mutex_; }; -} // DxApiImpl +} // TbApiImpl #endif //_COMMON_H_ \ No newline at end of file diff --git a/src/python_common.cpp b/src/python_common.cpp index 2c9bfd4..8179d88 100644 --- a/src/python_common.cpp +++ b/src/python_common.cpp @@ -1,6 +1,6 @@ #include "python_common.h" -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { bool getStringValue(PyObject *field_value, std::string &ret_value, bool &type_mismatch) { diff --git a/src/python_common.h b/src/python_common.h index 0984742..34b0531 100644 --- a/src/python_common.h +++ b/src/python_common.h @@ -7,10 +7,10 @@ #include -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { -const std::string MODULE_NAME = "dxapi"; +const std::string MODULE_NAME = "tbapi"; const std::string MESSAGE_OBJECT_CLASS_NAME = "InstrumentMessage"; const std::string TYPE_ID_PROPERTY = "typeId"; @@ -36,22 +36,22 @@ bool getInt64Value(PyObject *message, const std::string &field_name, PyObject *f //bool getBooleanValue(PyObject *message, const std::string &field_name, PyObject *field_key, bool &ret_value); //todo: should be singletone -class PythonDxApiModule { +class PythonTbApiModule { public: - PythonDxApiModule() { - dxapi_module_ = PyImport_ImportModule(MODULE_NAME.c_str()); - if (dxapi_module_ == NULL) + PythonTbApiModule() { + tbapi_module_ = PyImport_ImportModule(MODULE_NAME.c_str()); + if (tbapi_module_ == NULL) THROW_EXCEPTION("Module '%32s' is not loaded.", MODULE_NAME.c_str()); - module_dict_ = PyModule_GetDict(dxapi_module_); + module_dict_ = PyModule_GetDict(tbapi_module_); instrument_message_class_ = PyDict_GetItemString(module_dict_, MESSAGE_OBJECT_CLASS_NAME.c_str()); if (instrument_message_class_ == NULL) THROW_EXCEPTION("Class '%32s' not found in module '%32s'.", MESSAGE_OBJECT_CLASS_NAME.c_str(), MODULE_NAME.c_str()); } - ~PythonDxApiModule() { - Py_XDECREF(dxapi_module_); + ~PythonTbApiModule() { + Py_XDECREF(tbapi_module_); } PyObject * newInstrumentMessageObject() { @@ -63,7 +63,7 @@ class PythonDxApiModule { } private: - PyObject *dxapi_module_ = NULL; + PyObject *tbapi_module_ = NULL; PyObject *module_dict_ = NULL; PyObject *instrument_message_class_ = NULL; }; diff --git a/src/swig/common.i b/src/swig/common.i index a6416aa..e2cae49 100644 --- a/src/swig/common.i +++ b/src/swig/common.i @@ -17,7 +17,7 @@ namespace DxApi { Example: ``` - scope = dxapi.StreamScope('TRANSIENT') + scope = tbapi.StreamScope('TRANSIENT') ``` Possible values: @@ -60,7 +60,7 @@ TRUNCATE: Stream truncated every time when loader writes a messages earlier than Example: ``` - mode = dxapi.StreamScope('TRUNCATE') + mode = tbapi.StreamScope('TRUNCATE') ``` Possible values: @@ -98,7 +98,7 @@ public: Example: ``` - so = dxapi.SelectionOptions() + so = tbapi.SelectionOptions() so._from = 0 so.to = 100000 so.useCompression = False @@ -151,8 +151,8 @@ public: Example: ``` - lo = dxapi.LoadingOptions() - lo.writeMode = dxapi.WriteMode('TRUNCATE') + lo = tbapi.LoadingOptions() + lo.writeMode = tbapi.WriteMode('TRUNCATE') so.space = 'myspace' ... ```"); @@ -177,10 +177,10 @@ public: Example: ``` - so = dxapi.StreamOptions() + so = tbapi.StreamOptions() so.name = key so.description = key - so.scope = dxapi.StreamScope('DURABLE') + so.scope = tbapi.StreamScope('DURABLE') so.distributionFactor = 1 so.highAvailability = False so.polymorphic = False @@ -235,7 +235,7 @@ public: return distributionRuleName def metadata(self, metadata: str = None) -> None: - '''Stream metadata in XML format. To build metadata programatically, use dxapi.SchemaDef class.''' + '''Stream metadata in XML format. To build metadata programatically, use tbapi.SchemaDef class.''' if metadata == None: return self.__getMetadata() else: diff --git a/src/swig/dxapi.i b/src/swig/tbapi.i similarity index 87% rename from src/swig/dxapi.i rename to src/swig/tbapi.i index 8fdfbdf..e024bf4 100644 --- a/src/swig/dxapi.i +++ b/src/swig/tbapi.i @@ -25,12 +25,12 @@ else: if _swig_python_version_info >= (3, 7, 0): def swig_import_helper(): import importlib - mname = '.'.join((__name__, platform, 'x64', subdir, '_dxapi')).lstrip('.') + mname = '.'.join((__name__, platform, 'x64', subdir, '_tbapi')).lstrip('.') try: return importlib.import_module(mname) except ImportError: - return importlib.import_module('_dxapi') - _dxapi = swig_import_helper() + return importlib.import_module('_tbapi') + _tbapi = swig_import_helper() del swig_import_helper elif _swig_python_version_info >= (2, 6, 0): def swig_import_helper(): @@ -39,29 +39,45 @@ elif _swig_python_version_info >= (2, 6, 0): fp = None try: directory = '/'.join((dirname(__file__), platform, 'x64', subdir)) - fp, pathname, description = imp.find_module('_dxapi', [directory]) + fp, pathname, description = imp.find_module('_tbapi', [directory]) except ImportError: - import _dxapi - return _dxapi + import _tbapi + return _tbapi try: - _mod = imp.load_module('_dxapi', fp, pathname, description) + _mod = imp.load_module('_tbapi', fp, pathname, description) finally: if fp is not None: fp.close() return _mod - _dxapi = swig_import_helper() + _tbapi = swig_import_helper() del swig_import_helper else: - import _dxapi + import _tbapi del _swig_python_version_info del _swig_python_platform -version = (6, 1) + +from os import path + +def version(): + tbapi_dir = path.dirname(__file__) + if tbapi_dir != '': + tbapi_dir = tbapi_dir + '/project.properties' + with open(tbapi_dir) as file: + lines = file.readlines() + for line in lines: + split_line = line.split('=') + if len(split_line) == 2: + key = split_line[0].strip() + value = split_line[1].strip() + if key.strip() == 'version' and value != None: + return value + return 'UNKNOWN' " %enddef -%module(moduleimport=MODULEIMPORT, directors="1") dxapi +%module(moduleimport=MODULEIMPORT, directors="1") tbapi %include "stdint.i" %include "std_string.i" @@ -125,7 +141,7 @@ typedef int64_t TimestampNs; for (int i = 0; i < size; i++) { std::string str; bool type_mismatch = false; - DxApiImpl::Python::getStringValue(PyList_GetItem($input,i), str, type_mismatch); + TbApiImpl::Python::getStringValue(PyList_GetItem($input,i), str, type_mismatch); $1->push_back(str); if (type_mismatch) { delete $1; @@ -167,7 +183,7 @@ typedef int64_t TimestampNs; for (int i = 0; i < size; i++) { std::string str; bool type_mismatch = false; - DxApiImpl::Python::getStringValue(PyList_GetItem($input,i), str, type_mismatch); + TbApiImpl::Python::getStringValue(PyList_GetItem($input,i), str, type_mismatch); $1->push_back(str); if (type_mismatch) { delete $1; @@ -209,7 +225,7 @@ typedef int64_t TimestampNs; for (int i = 0; i < size; i++) { std::string str; bool type_mismatch = false; - DxApiImpl::Python::getStringValue(PyList_GetItem($input,i), str, type_mismatch); + TbApiImpl::Python::getStringValue(PyList_GetItem($input,i), str, type_mismatch); $1->push_back(str); if (type_mismatch) { delete $1; @@ -356,8 +372,8 @@ typedef int64_t TimestampNs; /* %typemap(out) DxApi::TickCursor * */ $result = SWIG_NewPointerObj( - SWIG_as_voidptr(new DxApiImpl::Python::TickCursor($1)), - SWIGTYPE_p_DxApiImpl__Python__TickCursor, + SWIG_as_voidptr(new TbApiImpl::Python::TickCursor($1)), + SWIGTYPE_p_TbApiImpl__Python__TickCursor, SWIG_POINTER_OWN | 0 ); } @@ -366,8 +382,8 @@ typedef int64_t TimestampNs; /* %typemap(out) DxApi::TickLoader * */ $result = SWIG_NewPointerObj( - SWIG_as_voidptr(new DxApiImpl::Python::TickLoader($1)), - SWIGTYPE_p_DxApiImpl__Python__TickLoader, + SWIG_as_voidptr(new TbApiImpl::Python::TickLoader($1)), + SWIGTYPE_p_TbApiImpl__Python__TickLoader, SWIG_POINTER_OWN | 0 ); } diff --git a/src/swig/tick_cursor.i b/src/swig/tick_cursor.i index f254efc..3fce52a 100644 --- a/src/swig/tick_cursor.i +++ b/src/swig/tick_cursor.i @@ -1,5 +1,5 @@ -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { enum NextResult { @@ -16,7 +16,7 @@ or call executeQuery to open cursor to QQL result set. Also cursor can be created with createCursor method, but it will be not initialized cursor, so cursor should be configured with types, entities and read time calling reset: ``` - options = dxapi.SelectionOptions() + options = tbapi.SelectionOptions() cursor = tickdb.createCursor(stream, options) cursor.subscribeToAllEntities() cursor.subscribeToAllTypes() diff --git a/src/swig/tick_db.i b/src/swig/tick_db.i index c0378d3..5865df0 100644 --- a/src/swig/tick_db.i +++ b/src/swig/tick_db.i @@ -9,13 +9,13 @@ Database engine. Instances of this class are created by static method createFromUrl: ``` -db = dxapi.TickDb.createFromUrl('dxtick://localhost:8011') +db = tbapi.TickDb.createFromUrl('dxtick://localhost:8011') ``` or ``` -db = dxapi.TickDb.createFromUrl('dxtick://localhost:8011', 'user', 'password') +db = tbapi.TickDb.createFromUrl('dxtick://localhost:8011', 'user', 'password') ```"); class TickDb { @@ -35,9 +35,9 @@ public: TickDb: An un-opened TickDB instance. ''' if user == None: - return _dxapi.TickDb___createFromUrl(url) + return _tbapi.TickDb___createFromUrl(url) else: - return _dxapi.TickDb___createFromUrlWithUser(url, user, password) + return _tbapi.TickDb___createFromUrlWithUser(url, user, password) @staticmethod @contextmanager diff --git a/src/swig/tick_loader.i b/src/swig/tick_loader.i index 7e946e1..a713d03 100644 --- a/src/swig/tick_loader.i +++ b/src/swig/tick_loader.i @@ -1,15 +1,15 @@ -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { %feature("autodoc", "Object which consumes messages. Create loader from TickDb: - options = dxapi.LoadingOptions() + options = tbapi.LoadingOptions() stream = tickdb.createLoader(stream, options) Create loader from TickStream: - options = dxapi.LoadingOptions() + options = tbapi.LoadingOptions() stream = stream.createLoader(options)"); class TickLoader { @@ -63,7 +63,7 @@ public: you could specify type id instead of type name, for example: ``` - message = dxapi.InstrumentMessage() + message = tbapi.InstrumentMessage() message.typeId = loader.registerType("deltix.timebase.api.messages.universal.PackageHeader") // as alternative, you could write: // message.typeName = "deltix.timebase.api.messages.universal.PackageHeader" @@ -83,7 +83,7 @@ public: you could specify instrument id instead of symbol and instrument type, for example: ``` - message = dxapi.InstrumentMessage() + message = tbapi.InstrumentMessage() message.instrumentId = loader.registerInstrument('AAPL') // as alternative, you could write: // message.symbol = 'AAPL' diff --git a/src/tick_cursor.cpp b/src/tick_cursor.cpp index 6a6c49d..116b61c 100644 --- a/src/tick_cursor.cpp +++ b/src/tick_cursor.cpp @@ -3,7 +3,7 @@ #include "python_common.h" #include "codecs/message_codec.h" -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { enum CursorState { @@ -118,13 +118,13 @@ void TickCursor::decodeCurrentMessage() { std::vector descriptors = Schema::TickDbClassDescriptor::parseDescriptors(*schema, true); - message_decoder = std::shared_ptr(new MessageCodec(&dxapi_module_, descriptors, 0)); + message_decoder = std::shared_ptr(new MessageCodec(&tbapi_module_, descriptors, 0)); message_decoders_[type_id] = message_decoder; } PyObject *message_object = message_objects_[type_id]; if (message_object == NULL) { - message_object = dxapi_module_.newInstrumentMessageObject(); + message_object = tbapi_module_.newInstrumentMessageObject(); //message_object = PyDict_New(); if (message_object == NULL) THROW_EXCEPTION("Can't create object of class '%32s'", MESSAGE_OBJECT_CLASS_NAME.c_str()); diff --git a/src/tick_cursor.h b/src/tick_cursor.h index 1ce4297..905d119 100644 --- a/src/tick_cursor.h +++ b/src/tick_cursor.h @@ -6,7 +6,7 @@ #include "python_common.h" #include "dxapi.h" -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { class MessageCodec; @@ -69,7 +69,7 @@ class TickCursor { std::vector> message_decoders_; std::vector message_objects_; - PythonDxApiModule dxapi_module_; + PythonTbApiModule tbapi_module_; PyObject * TYPE_ID_PROPERTY1 = PyUnicode_FromString("typeId"); PyObject * TYPE_NAME_PROPERTY1 = PyUnicode_FromString("typeName"); @@ -80,6 +80,6 @@ class TickCursor { }; // TickCursor } // namespace Python -} // namespace DxApiImpl +} // namespace TbApiImpl #endif //DELTIX_API_TICK_CURSOR_H_ \ No newline at end of file diff --git a/src/tick_loader.cpp b/src/tick_loader.cpp index 73ad627..aad9cb6 100644 --- a/src/tick_loader.cpp +++ b/src/tick_loader.cpp @@ -6,7 +6,7 @@ #include #include -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { TickLoader::TickLoader(DxApi::TickLoader *loader) { @@ -217,4 +217,4 @@ int32_t TickLoader::findDescriptor(const std::string &name) { } } // namespace Python -} // namespace DxApiImpl \ No newline at end of file +} // namespace TbApiImpl \ No newline at end of file diff --git a/src/tick_loader.h b/src/tick_loader.h index 89bd34d..427f403 100644 --- a/src/tick_loader.h +++ b/src/tick_loader.h @@ -11,7 +11,7 @@ #include #include -namespace DxApiImpl { +namespace TbApiImpl { namespace Python { class MessageCodec; diff --git a/dxapi-python.sln b/tbapi-python.sln similarity index 95% rename from dxapi-python.sln rename to tbapi-python.sln index 5ba06f6..fce458c 100644 --- a/dxapi-python.sln +++ b/tbapi-python.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30330.147 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dxapi-python", "projects\dxapi-python.vcxproj", "{F06CE5AA-B67E-4E38-8473-283FEBE7AE33}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tbapi-python", "projects\tbapi-python.vcxproj", "{F06CE5AA-B67E-4E38-8473-283FEBE7AE33}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/tbapi/__init__.py b/tbapi/__init__.py new file mode 100644 index 0000000..90ad1d0 --- /dev/null +++ b/tbapi/__init__.py @@ -0,0 +1,1953 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 3.0.12 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + + +from sys import version_info as _swig_python_version_info +from sys import platform as _swig_python_platform + +platform = 'windows' +if _swig_python_platform.startswith('linux'): + platform = 'linux' +elif _swig_python_platform.startswith('darwin'): + platform = 'darwin' + +if _swig_python_version_info >= (3, 6) and _swig_python_version_info < (3, 7): + subdir = 'py36' +elif _swig_python_version_info >= (3, 7) and _swig_python_version_info < (3, 8): + subdir = 'py37' +elif _swig_python_version_info >= (3, 8) and _swig_python_version_info < (3, 9): + subdir = 'py38' +elif _swig_python_version_info >= (3, 9) and _swig_python_version_info < (3, 10): + subdir = 'py39' +elif _swig_python_version_info >= (3, 10) and _swig_python_version_info < (3, 11): + subdir = 'py310' +else: + raise Exception('Version of python (' + str(_swig_python_version_info) + ') is not supported') + +if _swig_python_version_info >= (3, 7, 0): + def swig_import_helper(): + import importlib + mname = '.'.join((__name__, platform, 'x64', subdir, '_tbapi')).lstrip('.') + try: + return importlib.import_module(mname) + except ImportError: + return importlib.import_module('_tbapi') + _tbapi = swig_import_helper() + del swig_import_helper +elif _swig_python_version_info >= (2, 6, 0): + def swig_import_helper(): + from os.path import dirname + import imp + fp = None + try: + directory = '/'.join((dirname(__file__), platform, 'x64', subdir)) + fp, pathname, description = imp.find_module('_tbapi', [directory]) + except ImportError: + import _tbapi + return _tbapi + try: + _mod = imp.load_module('_tbapi', fp, pathname, description) + finally: + if fp is not None: + fp.close() + return _mod + _tbapi = swig_import_helper() + del swig_import_helper +else: + import _tbapi +del _swig_python_version_info +del _swig_python_platform + + +from os import path + +def version(): + tbapi_dir = path.dirname(__file__) + if tbapi_dir != '': + tbapi_dir = tbapi_dir + '/project.properties' + with open(tbapi_dir) as file: + lines = file.readlines() + for line in lines: + split_line = line.split('=') + if len(split_line) == 2: + key = split_line[0].strip() + value = split_line[1].strip() + if key.strip() == 'version' and value != None: + return value + return 'UNKNOWN' + +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. + +try: + import builtins as __builtin__ +except ImportError: + import __builtin__ + +def _swig_setattr_nondynamic(self, class_type, name, value, static=1): + if (name == "thisown"): + return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'SwigPyObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name, None) + if method: + return method(self, value) + if (not static): + if _newclass: + object.__setattr__(self, name, value) + else: + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + + +def _swig_setattr(self, class_type, name, value): + return _swig_setattr_nondynamic(self, class_type, name, value, 0) + + +def _swig_getattr(self, class_type, name): + if (name == "thisown"): + return self.this.own() + method = class_type.__swig_getmethods__.get(name, None) + if method: + return method(self) + raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name)) + + +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except __builtin__.Exception: + strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +try: + _object = object + _newclass = 1 +except __builtin__.Exception: + class _object: + pass + _newclass = 0 + +try: + import weakref + weakref_proxy = weakref.proxy +except __builtin__.Exception: + weakref_proxy = lambda x: x + + +class SwigPyIterator(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, SwigPyIterator, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, SwigPyIterator, name) + + def __init__(self, *args, **kwargs): + raise AttributeError("No constructor defined - class is abstract") + __repr__ = _swig_repr + __swig_destroy__ = _tbapi.delete_SwigPyIterator + __del__ = lambda self: None + + def value(self): + return _tbapi.SwigPyIterator_value(self) + + def incr(self, n=1): + return _tbapi.SwigPyIterator_incr(self, n) + + def decr(self, n=1): + return _tbapi.SwigPyIterator_decr(self, n) + + def distance(self, x): + return _tbapi.SwigPyIterator_distance(self, x) + + def equal(self, x): + return _tbapi.SwigPyIterator_equal(self, x) + + def copy(self): + return _tbapi.SwigPyIterator_copy(self) + + def next(self): + return _tbapi.SwigPyIterator_next(self) + + def __next__(self): + return _tbapi.SwigPyIterator___next__(self) + + def previous(self): + return _tbapi.SwigPyIterator_previous(self) + + def advance(self, n): + return _tbapi.SwigPyIterator_advance(self, n) + + def __eq__(self, x): + return _tbapi.SwigPyIterator___eq__(self, x) + + def __ne__(self, x): + return _tbapi.SwigPyIterator___ne__(self, x) + + def __iadd__(self, n): + return _tbapi.SwigPyIterator___iadd__(self, n) + + def __isub__(self, n): + return _tbapi.SwigPyIterator___isub__(self, n) + + def __add__(self, n): + return _tbapi.SwigPyIterator___add__(self, n) + + def __sub__(self, *args): + return _tbapi.SwigPyIterator___sub__(self, *args) + def __iter__(self): + return self +SwigPyIterator_swigregister = _tbapi.SwigPyIterator_swigregister +SwigPyIterator_swigregister(SwigPyIterator) + + +from contextlib import contextmanager + +JAVA_LONG_MIN_VALUE = -9223372036854775808 +JAVA_LONG_MAX_VALUE = 9223372036854775807 + +class InstrumentMessage(object): + def __str__(self): + return str(vars(self)) + +class StreamScope(_object): + """ + Determines the scope of a stream's durability, if any. + + Example: + ``` + scope = tbapi.StreamScope('TRANSIENT') + ``` + + Possible values: + ``` + DURABLE, + EXTERNAL_FILE, + TRANSIENT, + RUNTIME + ``` + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, StreamScope, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, StreamScope, name) + __repr__ = _swig_repr + DURABLE = _tbapi.StreamScope_DURABLE + EXTERNAL_FILE = _tbapi.StreamScope_EXTERNAL_FILE + TRANSIENT = _tbapi.StreamScope_TRANSIENT + RUNTIME = _tbapi.StreamScope_RUNTIME + + def __init__(self, *args): + this = _tbapi.new_StreamScope(*args) + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + + def __int__(self): + return _tbapi.StreamScope___int__(self) + + def __str__(self): + return _tbapi.StreamScope___str__(self) + __swig_destroy__ = _tbapi.delete_StreamScope + __del__ = lambda self: None +StreamScope_swigregister = _tbapi.StreamScope_swigregister +StreamScope_swigregister(StreamScope) + +class WriteMode(_object): + """ + APPEND: Adds only new data into a stream without truncations. + REPLACE: Adds data into a stream and removes previous data older that first message time + [truncate(first message time + 1)]. + REWRITE: Default. Adds data into a stream and removes previous data by truncating using first message time. + [truncate(first message time)]. + TRUNCATE: Stream truncated every time when loader writes a messages earlier than last message time. + + Example: + ``` + mode = tbapi.StreamScope('TRUNCATE') + ``` + + Possible values: + ``` + APPEND, + REPLACE, + REWRITE, + TRUNCATE + ``` + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, WriteMode, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, WriteMode, name) + __repr__ = _swig_repr + APPEND = _tbapi.WriteMode_APPEND + REPLACE = _tbapi.WriteMode_REPLACE + REWRITE = _tbapi.WriteMode_REWRITE + TRUNCATE = _tbapi.WriteMode_TRUNCATE + + def __init__(self, *args): + this = _tbapi.new_WriteMode(*args) + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + + def __int__(self): + return _tbapi.WriteMode___int__(self) + + def __str__(self): + return _tbapi.WriteMode___str__(self) + __swig_destroy__ = _tbapi.delete_WriteMode + __del__ = lambda self: None +WriteMode_swigregister = _tbapi.WriteMode_swigregister +WriteMode_swigregister(WriteMode) + +class SelectionOptions(_object): + """ + Options for selecting data from a stream. + + Example: + ``` + so = tbapi.SelectionOptions() + so._from = 0 + so.to = 100000 + so.useCompression = False + ... + ``` + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, SelectionOptions, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, SelectionOptions, name) + __repr__ = _swig_repr + + def __init__(self): + this = _tbapi.new_SelectionOptions() + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + __swig_setmethods__["_from"] = _tbapi.SelectionOptions__from_set + __swig_getmethods__["_from"] = _tbapi.SelectionOptions__from_get + if _newclass: + _from = _swig_property(_tbapi.SelectionOptions__from_get, _tbapi.SelectionOptions__from_set) + __swig_setmethods__["to"] = _tbapi.SelectionOptions_to_set + __swig_getmethods__["to"] = _tbapi.SelectionOptions_to_get + if _newclass: + to = _swig_property(_tbapi.SelectionOptions_to_get, _tbapi.SelectionOptions_to_set) + __swig_setmethods__["useCompression"] = _tbapi.SelectionOptions_useCompression_set + __swig_getmethods__["useCompression"] = _tbapi.SelectionOptions_useCompression_get + if _newclass: + useCompression = _swig_property(_tbapi.SelectionOptions_useCompression_get, _tbapi.SelectionOptions_useCompression_set) + __swig_setmethods__["live"] = _tbapi.SelectionOptions_live_set + __swig_getmethods__["live"] = _tbapi.SelectionOptions_live_get + if _newclass: + live = _swig_property(_tbapi.SelectionOptions_live_get, _tbapi.SelectionOptions_live_set) + __swig_setmethods__["reverse"] = _tbapi.SelectionOptions_reverse_set + __swig_getmethods__["reverse"] = _tbapi.SelectionOptions_reverse_get + if _newclass: + reverse = _swig_property(_tbapi.SelectionOptions_reverse_get, _tbapi.SelectionOptions_reverse_set) + __swig_setmethods__["allowLateOutOfOrder"] = _tbapi.SelectionOptions_allowLateOutOfOrder_set + __swig_getmethods__["allowLateOutOfOrder"] = _tbapi.SelectionOptions_allowLateOutOfOrder_get + if _newclass: + allowLateOutOfOrder = _swig_property(_tbapi.SelectionOptions_allowLateOutOfOrder_get, _tbapi.SelectionOptions_allowLateOutOfOrder_set) + __swig_setmethods__["realTimeNotification"] = _tbapi.SelectionOptions_realTimeNotification_set + __swig_getmethods__["realTimeNotification"] = _tbapi.SelectionOptions_realTimeNotification_get + if _newclass: + realTimeNotification = _swig_property(_tbapi.SelectionOptions_realTimeNotification_get, _tbapi.SelectionOptions_realTimeNotification_set) + __swig_setmethods__["minLatency"] = _tbapi.SelectionOptions_minLatency_set + __swig_getmethods__["minLatency"] = _tbapi.SelectionOptions_minLatency_get + if _newclass: + minLatency = _swig_property(_tbapi.SelectionOptions_minLatency_get, _tbapi.SelectionOptions_minLatency_set) + __swig_destroy__ = _tbapi.delete_SelectionOptions + __del__ = lambda self: None +SelectionOptions_swigregister = _tbapi.SelectionOptions_swigregister +SelectionOptions_swigregister(SelectionOptions) + +class LoadingOptions(_object): + """ + Options for loading data into a stream. + + Example: + ``` + lo = tbapi.LoadingOptions() + lo.writeMode = tbapi.WriteMode('TRUNCATE') + so.space = 'myspace' + ... + ``` + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, LoadingOptions, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, LoadingOptions, name) + __repr__ = _swig_repr + __swig_setmethods__["writeMode"] = _tbapi.LoadingOptions_writeMode_set + __swig_getmethods__["writeMode"] = _tbapi.LoadingOptions_writeMode_get + if _newclass: + writeMode = _swig_property(_tbapi.LoadingOptions_writeMode_get, _tbapi.LoadingOptions_writeMode_set) + __swig_setmethods__["minLatency"] = _tbapi.LoadingOptions_minLatency_set + __swig_getmethods__["minLatency"] = _tbapi.LoadingOptions_minLatency_get + if _newclass: + minLatency = _swig_property(_tbapi.LoadingOptions_minLatency_get, _tbapi.LoadingOptions_minLatency_set) + __swig_setmethods__["space"] = _tbapi.LoadingOptions_space_set + __swig_getmethods__["space"] = _tbapi.LoadingOptions_space_get + if _newclass: + space = _swig_property(_tbapi.LoadingOptions_space_get, _tbapi.LoadingOptions_space_set) + + def __init__(self): + this = _tbapi.new_LoadingOptions() + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + __swig_destroy__ = _tbapi.delete_LoadingOptions + __del__ = lambda self: None +LoadingOptions_swigregister = _tbapi.LoadingOptions_swigregister +LoadingOptions_swigregister(LoadingOptions) + +class StreamOptions(_object): + """ + Stream definition attributes. + + Example: + ``` + so = tbapi.StreamOptions() + so.name = key + so.description = key + so.scope = tbapi.StreamScope('DURABLE') + so.distributionFactor = 1 + so.highAvailability = False + so.polymorphic = False + so.metadata = schema + + db.createStream(key, options) + ``` + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, StreamOptions, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, StreamOptions, name) + __repr__ = _swig_repr + + def name(self, name: str = None) -> None: + '''Optional user-readable name.''' + if name == None: + return self.__getName() + else: + self.__setName(name) + return name + + def description(self, description: str = None) -> None: + '''Optional multi-line description.''' + if description == None: + return self.__getDescription() + else: + self.__setDescription(description) + return description + + def owner(self, owner: str = None) -> None: + '''Optional owner of stream. + During stream creation it will be set + equals to authenticated user name. + ''' + if owner == None: + return self.__getOwner() + else: + self.__setOwner(owner) + return owner + + def location(self, location: str = None) -> None: + '''Location of the stream (by default null). When defined this attribute provides alternative stream location (rather than default location under QuantServerHome)''' + if location == None: + return self.__getLocation() + else: + self.__setLocation(location) + return location + + def distributionRuleName(self, distributionRuleName: str = None) -> None: + '''Class name of the distribution rule''' + if distributionRuleName == None: + return self.__getDistributionRuleName() + else: + self.__setDistributionRuleName(distributionRuleName) + return distributionRuleName + + def metadata(self, metadata: str = None) -> None: + '''Stream metadata in XML format. To build metadata programatically, use tbapi.SchemaDef class.''' + if metadata == None: + return self.__getMetadata() + else: + self.__setMetadata(metadata) + return metadata + + __swig_setmethods__["scope"] = _tbapi.StreamOptions_scope_set + __swig_getmethods__["scope"] = _tbapi.StreamOptions_scope_get + if _newclass: + scope = _swig_property(_tbapi.StreamOptions_scope_get, _tbapi.StreamOptions_scope_set) + __swig_setmethods__["distributionFactor"] = _tbapi.StreamOptions_distributionFactor_set + __swig_getmethods__["distributionFactor"] = _tbapi.StreamOptions_distributionFactor_get + if _newclass: + distributionFactor = _swig_property(_tbapi.StreamOptions_distributionFactor_get, _tbapi.StreamOptions_distributionFactor_set) + __swig_setmethods__["duplicatesAllowed"] = _tbapi.StreamOptions_duplicatesAllowed_set + __swig_getmethods__["duplicatesAllowed"] = _tbapi.StreamOptions_duplicatesAllowed_get + if _newclass: + duplicatesAllowed = _swig_property(_tbapi.StreamOptions_duplicatesAllowed_get, _tbapi.StreamOptions_duplicatesAllowed_set) + __swig_setmethods__["highAvailability"] = _tbapi.StreamOptions_highAvailability_set + __swig_getmethods__["highAvailability"] = _tbapi.StreamOptions_highAvailability_get + if _newclass: + highAvailability = _swig_property(_tbapi.StreamOptions_highAvailability_get, _tbapi.StreamOptions_highAvailability_set) + __swig_setmethods__["unique"] = _tbapi.StreamOptions_unique_set + __swig_getmethods__["unique"] = _tbapi.StreamOptions_unique_get + if _newclass: + unique = _swig_property(_tbapi.StreamOptions_unique_get, _tbapi.StreamOptions_unique_set) + __swig_setmethods__["polymorphic"] = _tbapi.StreamOptions_polymorphic_set + __swig_getmethods__["polymorphic"] = _tbapi.StreamOptions_polymorphic_get + if _newclass: + polymorphic = _swig_property(_tbapi.StreamOptions_polymorphic_get, _tbapi.StreamOptions_polymorphic_set) + __swig_setmethods__["periodicity"] = _tbapi.StreamOptions_periodicity_set + __swig_getmethods__["periodicity"] = _tbapi.StreamOptions_periodicity_get + if _newclass: + periodicity = _swig_property(_tbapi.StreamOptions_periodicity_get, _tbapi.StreamOptions_periodicity_set) + + def __eq__(self, value): + return _tbapi.StreamOptions___eq__(self, value) + + def __init__(self): + this = _tbapi.new_StreamOptions() + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + + def __getName(self): + return _tbapi.StreamOptions___getName(self) + + def __setName(self, name): + return _tbapi.StreamOptions___setName(self, name) + + def __getDescription(self): + return _tbapi.StreamOptions___getDescription(self) + + def __setDescription(self, description): + return _tbapi.StreamOptions___setDescription(self, description) + + def __getOwner(self): + return _tbapi.StreamOptions___getOwner(self) + + def __setOwner(self, owner): + return _tbapi.StreamOptions___setOwner(self, owner) + + def __getLocation(self): + return _tbapi.StreamOptions___getLocation(self) + + def __setLocation(self, location): + return _tbapi.StreamOptions___setLocation(self, location) + + def __getDistributionRuleName(self): + return _tbapi.StreamOptions___getDistributionRuleName(self) + + def __setDistributionRuleName(self, distributionRuleName): + return _tbapi.StreamOptions___setDistributionRuleName(self, distributionRuleName) + + def __getMetadata(self): + return _tbapi.StreamOptions___getMetadata(self) + + def __setMetadata(self, metadata): + return _tbapi.StreamOptions___setMetadata(self, metadata) + __swig_destroy__ = _tbapi.delete_StreamOptions + __del__ = lambda self: None +StreamOptions_swigregister = _tbapi.StreamOptions_swigregister +StreamOptions_swigregister(StreamOptions) + +class QueryParameter(_object): + """Input parameter definition for a prepared statement.""" + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, QueryParameter, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, QueryParameter, name) + __repr__ = _swig_repr + __swig_setmethods__["name"] = _tbapi.QueryParameter_name_set + __swig_getmethods__["name"] = _tbapi.QueryParameter_name_get + if _newclass: + name = _swig_property(_tbapi.QueryParameter_name_get, _tbapi.QueryParameter_name_set) + __swig_setmethods__["type"] = _tbapi.QueryParameter_type_set + __swig_getmethods__["type"] = _tbapi.QueryParameter_type_get + if _newclass: + type = _swig_property(_tbapi.QueryParameter_type_get, _tbapi.QueryParameter_type_set) + + def __init__(self, *args): + this = _tbapi.new_QueryParameter(*args) + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + + def value(self, *args): + return _tbapi.QueryParameter_value(self, *args) + __swig_destroy__ = _tbapi.delete_QueryParameter + __del__ = lambda self: None +QueryParameter_swigregister = _tbapi.QueryParameter_swigregister +QueryParameter_swigregister(QueryParameter) + +class TickDb(_object): + """ + The top-level implementation to the methods of the Deltix Tick + Database engine. Instances of this class are created by static method + createFromUrl: + + ``` + db = tbapi.TickDb.createFromUrl('dxtick://localhost:8011') + ``` + + or + + ``` + db = tbapi.TickDb.createFromUrl('dxtick://localhost:8011', 'user', 'password') + ``` + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, TickDb, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, TickDb, name) + + def __init__(self, *args, **kwargs): + raise AttributeError("No constructor defined") + __repr__ = _swig_repr + + + @staticmethod + def createFromUrl(url: str, user: str = None, password: str = None) -> "TickDb": + '''Creates a new database instance with the specified root folder, or URL. + + Args: + url (str): Connection URL. + user (str): User. + password (str): Password. + + Returns: + TickDb: An un-opened TickDB instance. + ''' + if user == None: + return _tbapi.TickDb___createFromUrl(url) + else: + return _tbapi.TickDb___createFromUrlWithUser(url, user, password) + + @staticmethod + @contextmanager + def openFromUrl(url: str, readonly: bool, user: str = None, password: str = None): + '''Creates a new database instance with the specified root folder, or URL, and opens it. + + Args: + url (str): Connection URL. + readonly (bool): Open data store in read-only mode. + user (str): User. + password (str): Password. + + Returns: + TickDb: An opened TickDB instance. + ''' + db = TickDb.createFromUrl(url, user, password) + try: + db.open(readonly) + yield db + finally: + if db.isOpen(): + db.close() + + def isReadOnly(self) -> bool: + '''Determines whether the store is open as read-only.''' + return self.__isReadOnly() + + def isOpen(self) -> bool: + '''Determines whether the store is open.''' + return self.__isOpen() + + def open(self, readOnlyMode: bool) -> bool: + '''Open the data store. + + Args: + readOnlyMode (bool): Open data store in read-only mode. + ''' + return self.__open(readOnlyMode) + + def close(self) -> None: + '''Closes data store.''' + return self.__close() + + def format(self) -> bool: + '''Create a new object on disk and format internally. + The data store is left open for read-write at the end of this method. + ''' + return self.__format() + + def listStreams(self) -> 'list[TickStream]': + '''Enumerates existing streams. + + Returns: + list[TickStream]: An array of existing stream objects. + ''' + return self.__listStreams() + + def getStream(self, key: str) -> 'TickStream': + '''Looks up an existing stream by key. + + Args: + key (str): Identifies the stream. + + Returns: + TickStream: A stream object, or None if the key was not found. + ''' + return self.__getStream(key) + + def createStream(self, key: str, options: StreamOptions) -> 'TickStream': + '''Creates a new stream within the database. + + Args: + key (str): A required key later used to identify the stream. + options (StreamOptions): Options for creating the stream. + + Returns: + TickStream: A new instance of TickStream. + ''' + return self.__createStream(key, options) + + def createFileStream(self, key: str, dataFile: str) -> 'TickStream': + '''Creates a new stream mount to the given data file. + + Args: + key (str): A required key later used to identify the stream. + dataFile (str): Path to the data file (on server side). + + Returns: + TickStream: A new instance of TickStream. + ''' + return self.__createFileStream(key) + + def createCursor(self, stream: 'TickStream', options: SelectionOptions) -> 'TickCursor': + '''Opens an initially empty cursor for reading data from multiple streams, + according to the specified options. The messages + are returned from the cursor strictly ordered by time. Within the same + exact timestamp, the order of messages is undefined and may vary from + call to call, i.e. it is non-deterministic. + + The cursor is returned initially empty and must be reset. + The TickCursor class provides + methods for dynamically re-configuring the subscription, or jumping to + a different timestamp. + + Args: + stream (TickStream): Stream from which data will be selected. + options (SelectionOptions): Selection options. + + Returns: + TickCursor: A cursor used to read messages. + ''' + return self.__createCursor(stream, options) + + @contextmanager + def tryCursor(self, stream: 'TickStream', options: SelectionOptions) -> 'TickCursor': + '''contextmanager version of createCursor. Usage: + ``` + with db.newCursor(stream, options) as cursor: + while cursor.next(): + message = cursor.getMessage() + ``` + ''' + cursor = None + try: + cursor = self.__createCursor(stream, options) + yield cursor + finally: + cursor.close() + + def select(self, timestamp: int, streams: 'list[TickStream]', options: SelectionOptions, types: 'list[str]', entities: 'list[str]') -> 'TickCursor': + '''Opens a cursor for reading data from multiple streams, + according to the specified options. The messages + are returned from the cursor strictly ordered by time. Within the same + exact time stamp, the order of messages is undefined and may vary from + call to call, i.e. it is non-deterministic. + + Note that the arguments of this method only determine the initial + configuration of the cursor. The TickCursor clsas provides + methods for dynamically re-configuring the subscription, or jumping to + a different timestamp. + + Args: + timestamp (int): The start timestamp in millis. + streams (list[TickStream]): Streams from which data will be selected. + options (SelectionOptions): Selection options. + types (list[str]): Specified message types to be subscribed. If null, then all types will be subscribed. + entities (list[str]): Specified entities to be subscribed. If null, then all entities will be subscribed. + + Returns: + TickCursor: A cursor used to read messages. + ''' + return self.__select(timestamp, streams, options, types, entities) + + @contextmanager + def trySelect(self, timestamp: int, streams: 'list[TickStream]', options: SelectionOptions, types: 'list[str]', entities: 'list[str]') -> 'TickCursor': + '''Contextmanager version of select. Usage: + ``` + with db.newSelect(timestamp, streams, options, types, entities) as cursor: + while cursor.next(): + message = cursor.getMessage() + ``` + ''' + cursor = None + try: + cursor = self.__select(timestamp, streams, options, types, entities) + yield cursor + finally: + cursor.close() + + def createLoader(self, stream: 'TickStream', options: LoadingOptions) -> 'TickLoader': + '''Creates a channel for loading data. The loader must be closed + when the loading process is finished. + + Args: + stream (TickStream): stream for loading data. + options (SelectionOptions): Loading Options. + + Returns: + TickLoader: created loader. + ''' + return self.__createLoader(stream, options) + + @contextmanager + def tryLoader(self, stream: 'TickStream', options: LoadingOptions) -> 'TickLoader': + '''Contextmanager version of createLoader. Usage: + + with db.newLoader(stream, options) as loader: + loader.send(message) + ''' + loader = None + try: + loader = self.__createLoader(stream, options) + yield loader + finally: + loader.close() + + def executeQuery(self, query: str, options: SelectionOptions = None, timestamp: int = JAVA_LONG_MIN_VALUE, entities: 'list[str]' = None, params: 'list[QueryParameter]' = []) -> 'TickCursor': + '''Execute Query and creates a message source for reading data from it, + according to the specified options. The messages + are returned from the cursor strictly ordered by time. Within the same + exact time stamp, the order of messages is undefined and may vary from + call to call, i.e. it is non-deterministic. + + Args: + query (str): Query text element. + options (SelectionOptions): Selection options. + timestamp (int): The start timestamp in millis. + entities (list[str]): Specified entities to be subscribed. + If null, then all entities will be subscribed. + params (list[QueryParameter]): The parameter values of the query. + + Returns: + TickCursor: An iterable message source to read messages. + ''' + if options == None: + return self.__executeQuery(query) + else: + return self.__executeQueryFull(query, options, timestamp, entities, params); + + @contextmanager + def tryExecuteQuery(self, query: str, options: SelectionOptions = None, timestamp: int = JAVA_LONG_MIN_VALUE, entities: 'list[str]' = None, params: 'list[QueryParameter]' = []) -> 'TickCursor': + '''Contextmanager version of executeQuery. Usage: + ``` + with db.newExecuteQuery('select * from stream') as cursor: + while cursor.next(): + message = cursor.getMessage() + ``` + ''' + cursor = None + try: + if options == None: + cursor = self.__executeQuery(query) + else: + cursor = self.__executeQueryFull(query, options, timestamp, entities, params); + yield cursor + finally: + cursor.close() + + if _newclass: + __createFromUrl = staticmethod(_tbapi.TickDb___createFromUrl) + else: + __createFromUrl = _tbapi.TickDb___createFromUrl + if _newclass: + __createFromUrlWithUser = staticmethod(_tbapi.TickDb___createFromUrlWithUser) + else: + __createFromUrlWithUser = _tbapi.TickDb___createFromUrlWithUser + + def __isReadOnly(self): + return _tbapi.TickDb___isReadOnly(self) + + def __isOpen(self): + return _tbapi.TickDb___isOpen(self) + + def __open(self, readOnlyMode): + return _tbapi.TickDb___open(self, readOnlyMode) + + def __close(self): + return _tbapi.TickDb___close(self) + + def __format(self): + return _tbapi.TickDb___format(self) + + def __listStreams(self): + return _tbapi.TickDb___listStreams(self) + + def __getStream(self, key): + return _tbapi.TickDb___getStream(self, key) + + def __createStream(self, key, options): + return _tbapi.TickDb___createStream(self, key, options) + + def __createFileStream(self, key, dataFile): + return _tbapi.TickDb___createFileStream(self, key, dataFile) + + def __createCursor(self, stream, options): + return _tbapi.TickDb___createCursor(self, stream, options) + + def __select(self, time, streams, options, types, entities): + return _tbapi.TickDb___select(self, time, streams, options, types, entities) + + def __createLoader(self, stream, options): + return _tbapi.TickDb___createLoader(self, stream, options) + + def __executeQueryFull(self, qql, options, time, instruments, params): + return _tbapi.TickDb___executeQueryFull(self, qql, options, time, instruments, params) + __swig_destroy__ = _tbapi.delete_TickDb + __del__ = lambda self: None + + def __executeQuery(self, qql): + return _tbapi.TickDb___executeQuery(self, qql) +TickDb_swigregister = _tbapi.TickDb_swigregister +TickDb_swigregister(TickDb) + +def TickDb___createFromUrl(url): + return _tbapi.TickDb___createFromUrl(url) +TickDb___createFromUrl = _tbapi.TickDb___createFromUrl + +def TickDb___createFromUrlWithUser(url, username, password): + return _tbapi.TickDb___createFromUrlWithUser(url, username, password) +TickDb___createFromUrlWithUser = _tbapi.TickDb___createFromUrlWithUser + +class TickStream(_object): + """ + The stream is a time series of messages for a number of + financial instruments ('entities'). Messages can be price bars, trade ticks, + bid/offer ticks, or any of the many more built-in and user-defined types. + In the simplest case, a database will have a single stream of data. + Multiple streams can be used to represent data of different frequencies, or completely + different factors. For instance, separate streams can represent + 1-minute price bars and ticks for the same set of entities. Or, + you can have price bars and volatility bars in separate streams. + + Get stream: + ``` + stream = tickdb.getStream('stream_key') + ``` + + List stream: + ``` + streams = tickdb.listStreams() + ``` + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, TickStream, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, TickStream, name) + + def __init__(self, *args, **kwargs): + raise AttributeError("No constructor defined") + __repr__ = _swig_repr + + def key(self) -> str: + '''Returns the key, which uniquely identifies the stream within its database.''' + return self.__key() + + def name(self) -> str: + '''Returns a user-readable short name.''' + return self.__name() + + def distributionFactor(self) -> int: + '''Returns the target number of files to be used for storing data.''' + return self.__distributionFactor() + + def description(self) -> str: + '''Returns a user-readable multi-line description.''' + return self.__description() + + def owner(self) -> str: + '''Returns stream owner.''' + return self.__owner() + + def location(self) -> str: + '''Returns stream location.''' + return self.__location() + + def metadata(self) -> str: + '''Returns stream schema (in xml format).''' + return self.__metadata() + + def scope(self) -> StreamScope: + '''Returns stream schema (in xml format).''' + return self.__scope() + + def highAvailability(self) -> bool: + '''Returns stream memory caching parameter. High availability durable streams are cached on startup.''' + return self.__highAvailability() + + def unique(self) -> bool: + '''Unique streams maintain in-memory cache of resent messages. + This concept assumes that stream messages will have some field(s) marked as primary key. + Primary key may be a simple field (e.g. symbol) or composite (e.g. symbol and portfolio ID). + For each key TimeBase runtime maintains a copy of the last message received for this key (cache). + Each new consumer will receive a snapshot of current cache at the beginning of live data subscription. + ''' + return self.__unique() + + def polymorphic(self) -> bool: + '''Returns whether the stream is configured as polymorphic.''' + return self.__polymorphic() + + def periodicity(self) -> str: + '''Returns Stream periodicity, if known.''' + return self.__periodicity() + + def options(self) -> StreamOptions: + '''Returns stream options object.''' + return self.__options() + + def describe(self) -> str: + '''Returns stream DDL description.''' + return self.__describe() + + def setSchema(self, options: StreamOptions) -> bool: + '''Changes stream schema. + + Args: + options (StreamOptions): Stream options, that contains new schema xml. + + Returns + bool: True, if schema was changed successfully. + ''' + return self.__setSchema(options) + + def listEntities(self) -> 'list[str]': + '''Return an inclusive range of times for which the specified entities + have data in the database. + + Returns: + list[str]: selected entities. + ''' + return self.__listEntities() + + def truncate(self, timestamp: int, entities: 'list[str]' = None) -> bool: + '''Truncates stream data for the given entities from given time + + Args: + timestamp (int): Timestamp in millis. If time less than stream start time, then all stream data will be deleted. + entities (list[str]): A list of entities. If None, all stream entities will be used. + + Returns: + bool: true, if stream was truncated successfully. + ''' + if entities == None: + return self.__truncate(timestamp) + else: + return self.__truncateEntities(timestamp, entities) + + def clear(self, entities: 'list[str]' = None) -> bool: + '''Clear stream data for the given entities. + + Args: + entities (list[str]): A list of entities. If None, all stream entities will be used. + ''' + if entities == None: + return self.__clear() + else: + return self.__clearEntities(entities) + + def purge(self, timestamp: int) -> bool: + '''Deletes stream data that is older than a specified time + + Args: + timestamp (int):Purge time in milliseconds. + + Returns: + bool: true, if stream was purged successfully. + ''' + return self.__purge(timestamp) + + def deleteStream(self) -> bool: + '''Deletes this stream + + Returns: + bool: true, if stream was deleted successfully. + ''' + return self.__deleteStream() + + def abortBackgroundProcess(self) -> bool: + '''Aborts active background process if any exists''' + return self.__abortBackgroundProcess() + + def select(self, timestamp: int, options: SelectionOptions, types: 'list[str]', entities: 'list[str]') -> 'TickCursor': + '''Opens a cursor for reading data from this stream, according to the + specified options. The messages + are returned from the cursor strictly ordered by time. Within the same + exact time stamp, the order of messages is undefined and may vary from + call to call, i.e. it is non-deterministic. + + Note that the arguments of this method only determine the initial + configuration of the cursor. The TickCursor interface provides + methods for dynamically re-configuring the subscription, or jumping to + a different timestamp. + + Args: + timestamp (int): The start timestamp in millis. + options (SelectionOptions): Selection options. + types (list[str]): Specified message types to be subscribed. If null, then all types will be subscribed. + entities (list[str]): Specified entities to be subscribed. If null, then all entities will be subscribed. + + Returns: + TickCursor: A cursor used to read messages. + ''' + return self.__select(timestamp, options, types, entities) + + @contextmanager + def trySelect(self, timestamp: int, options: SelectionOptions, types: 'list[str]', entities: 'list[str]') -> 'TickCursor': + '''Contextmanager version of select. Usage: + ``` + with stream.newSelect(timestamp, options, types, entities) as cursor: + while cursor.next(): + message = cursor.getMessage() + ``` + ''' + cursor = None + try: + cursor = self.__select(timestamp, options, types, entities) + yield cursor + finally: + cursor.close() + + def createCursor(self, options: SelectionOptions) -> 'TickCursor': + '''Creates a cursor for reading data from this stream, according to the + specified options, but initially with a fully restricted filter. + The user must call TickCursor.reset at least once, in order to + begin retrieving data. This method is equivalent to (but is + slightly more optimal than) calling createCursor(options) + + Args: + options (SelectionOptions): Selection Options. + + Returns: + A cursor used to read messages. Never null. + ''' + return self.__createCursor(options) + + @contextmanager + def tryCursor(self, options: SelectionOptions) -> 'TickCursor': + '''contextmanager version of createCursor. Usage: + ``` + with stream.newCursor(options) as cursor: + while cursor.next(): + message = cursor.getMessage() + ``` + ''' + cursor = None + try: + cursor = self.__createCursor(options) + yield cursor + finally: + cursor.close() + + def createLoader(self, options: LoadingOptions) -> 'TickLoader': + '''Creates a channel for loading data. The loader must be closed + when the loading process is finished. + + Args: + options (SelectionOptions): Loading Options. + + Returns: + TickLoader: created loader. + ''' + return self.__createLoader(options) + + @contextmanager + def tryLoader(self, options: LoadingOptions) -> 'TickLoader': + '''Contextmanager version of createLoader. Usage: + ``` + with stream.newLoader(options) as loader: + loader.send(message) + ``` + ''' + loader = None + try: + loader = self.__createLoader(options) + yield loader + finally: + loader.close() + + def listSpaces(self) -> 'list[str]': + '''Returns all created "spaces" for the stream. + Default space returns as "" (empty string). + If backing stream does not support spaces None will be returned. + ''' + return self.__listSpaces() + + def renameSpace(self, newName: str, oldName: str) -> None: + '''Rename existing space. + + Args: + nameName (str): space to rename. + oldName (str): new space name. + ''' + return self.__renameSpace(newName, oldName) + + def deleteSpaces(self, spaces: 'list[str]') -> None: + '''Removed given 'spaces' permanently. + + Args: + spaces (list[str]): list of spaces names to delete. + ''' + return self.__deleteSpaces(spaces) + + def getTimeRange(self, entities: 'list[str]' = None) -> 'list[int]': + '''Return an inclusive range of times for which the specified entities + have data in the database. + + Args: + entities (list[str]): A list of entities. If empty, return for all. + ''' + if entities == None: + return self.__getTimeRange() + else: + return self.__getTimeRangeEntities(entities) + + def getSpaceTimeRange(self, space: str) -> 'list[int]': + '''An array consisting of two long timestamps (from and to) or None if no data was found. + + Args: + space (str): space name. + ''' + return self.__getSpaceTimeRange(space) + + + def __key(self): + return _tbapi.TickStream___key(self) + + def __distributionFactor(self): + return _tbapi.TickStream___distributionFactor(self) + + def __name(self): + return _tbapi.TickStream___name(self) + + def __description(self): + return _tbapi.TickStream___description(self) + + def __owner(self): + return _tbapi.TickStream___owner(self) + + def __location(self): + return _tbapi.TickStream___location(self) + + def __metadata(self): + return _tbapi.TickStream___metadata(self) + + def __scope(self): + return _tbapi.TickStream___scope(self) + + def __highAvailability(self): + return _tbapi.TickStream___highAvailability(self) + + def __unique(self): + return _tbapi.TickStream___unique(self) + + def __polymorphic(self): + return _tbapi.TickStream___polymorphic(self) + + def __periodicity(self): + return _tbapi.TickStream___periodicity(self) + + def __options(self): + return _tbapi.TickStream___options(self) + + def __describe(self): + return _tbapi.TickStream___describe(self) + + def __setSchema(self, options): + return _tbapi.TickStream___setSchema(self, options) + + def __listEntities(self): + return _tbapi.TickStream___listEntities(self) + + def __truncate(self, millisecondTime): + return _tbapi.TickStream___truncate(self, millisecondTime) + + def __truncateEntities(self, millisecondTime, entities): + return _tbapi.TickStream___truncateEntities(self, millisecondTime, entities) + + def __clear(self): + return _tbapi.TickStream___clear(self) + + def __clearEntities(self, entities): + return _tbapi.TickStream___clearEntities(self, entities) + + def __purge(self, millisecondTime): + return _tbapi.TickStream___purge(self, millisecondTime) + + def __deleteStream(self): + return _tbapi.TickStream___deleteStream(self) + + def __abortBackgroundProcess(self): + return _tbapi.TickStream___abortBackgroundProcess(self) + + def __select(self, millisecondTime, options, types, entities): + return _tbapi.TickStream___select(self, millisecondTime, options, types, entities) + + def __createCursor(self, options): + return _tbapi.TickStream___createCursor(self, options) + + def __createLoader(self, options): + return _tbapi.TickStream___createLoader(self, options) + + def __listSpaces(self): + return _tbapi.TickStream___listSpaces(self) + + def __renameSpace(self, newName, oldName): + return _tbapi.TickStream___renameSpace(self, newName, oldName) + + def __deleteSpaces(self, spaces): + return _tbapi.TickStream___deleteSpaces(self, spaces) + __swig_destroy__ = _tbapi.delete_TickStream + __del__ = lambda self: None + + def __getTimeRange(self): + return _tbapi.TickStream___getTimeRange(self) + + def __getTimeRangeEntities(self, entities): + return _tbapi.TickStream___getTimeRangeEntities(self, entities) + + def __getSpaceTimeRange(self, space): + return _tbapi.TickStream___getSpaceTimeRange(self, space) +TickStream_swigregister = _tbapi.TickStream_swigregister +TickStream_swigregister(TickStream) + +OK = _tbapi.OK +END_OF_CURSOR = _tbapi.END_OF_CURSOR +UNAVAILABLE = _tbapi.UNAVAILABLE +class TickCursor(_object): + """ + A cursor (also known as iterator, or result set) for reading data from a + stream. This class provides methods for dynamically reconfiguring the feed, + as well as method reset for essentially re-opening the cursor on a completely different timestamp. + + To get a cursor, use select method from TickDb or TickStream objects, + or call executeQuery to open cursor to QQL result set. + + Also cursor can be created with createCursor method, but it will be not initialized cursor, + so cursor should be configured with types, entities and read time calling reset: + ``` + options = tbapi.SelectionOptions() + cursor = tickdb.createCursor(stream, options) + cursor.subscribeToAllEntities() + cursor.subscribeToAllTypes() + cursor.reset(timestamp) + ``` + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, TickCursor, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, TickCursor, name) + + def __init__(self, *args, **kwargs): + raise AttributeError("No constructor defined") + __repr__ = _swig_repr + __swig_destroy__ = _tbapi.delete_TickCursor + __del__ = lambda self: None + + def next(self) -> bool: + '''Moves cursor on to the next message. This method blocks until the next message becomes available, + or until the cursor is determined to be at the end of the sequence. + This method is illegal to call if isAtEnd() returns true. + + Returns: + bool: false if at the end of the cursor. + ''' + return self.__next() + + def getMessage(self) -> 'InstrumentMessage': + '''Returns an InstrumentMessage object cursor points at.''' + return self.__getMessage() + + def isAtEnd(self) -> bool: + '''Returns true if the last call to next() returned false. Returns false if next() has not been called yet. + This method is legal to call any number of times at any point in the cursor's lifecycle. + ''' + return self.__isAtEnd() + + def nextIfAvailable(self) -> int: + '''Moves cursor on to the next message, but this method NOT blocks until the next message becomes available. + + Returns: + NextResult: OK (0) if new message is available, + END_OF_CURSOR (1) if cursor was closed, + otherwise, UNAVAILABLE (2) + ''' + return self.__nextIfAvailable() + + def isClosed(self) -> bool: + '''Returns true, if cursor was closed''' + return self.__isClosed() + + def close(self) -> None: + '''Close the cursor''' + return self.__close() + + def getCurrentStreamKey(self) -> str: + '''Return the key of the stream that is the source of the current message.''' + return self.__getCurrentStreamKey() + + def reset(self, timestamp: int, entities: 'list[str]' = None) -> None: + '''Reposition the message source to a new point in time, while + preserving current subscription. + + Args: + timestamp (int): The new position in time in millis. + entities ('list[str]'): list of entities to reset + ''' + if entities == None: + return self.__reset(timestamp) + else: + return self.__resetEntities(timestamp, entities) + + def subscribeToAllEntities(self) -> None: + '''Subscribe to all available entities.''' + return self.__subscribeToAllEntities() + + def clearAllEntities(self) -> None: + '''Switch to selective subscription mode (if necessary) and clear the list.''' + return self.__clearAllEntities() + + def addEntity(self, entity: str) -> None: + '''Add the specified entity to subscription. The type and symbol are copied + from the incoming object, if necessary, so the argument can be re-used + after the call. + + Special note about options: + The following fragment will subscribe to specific option contract "DAV 100417P00085000": + cursor.addEntity('DAV 100417P00085000'); + + While the following will subscribe to option root (and you will get all instruments with this root): + cursor.addEntity("DAV "); + ''' + return self.__addEntity(entity) + + def addEntities(self, entities: 'list[str]') -> None: + '''Bulk add the specified entities to subscription. The type and symbol are copied + from the incoming objects, if necessary, so the arguments can be re-used + after the call. + ''' + return self.__addEntities(entities) + + def removeEntities(self, entities: 'list[str]') -> None: + '''Remove the specified entities from subscription. The type and symbol are copied + from the incoming objects, if necessary, so the arguments can be re-used + after the call. + ''' + return self.__removeEntities(entities) + + def removeEntity(self, entity: str) -> None: + '''Remove the specified entity from subscription. The type and symbol are copied + from the incoming object, if necessary, so the argument can be re-used + after the call. + ''' + return self.__removeEntity(entity) + + def subscribeToAllTypes(self) -> None: + '''Subscribe to all available types (no filtering).''' + return self.__subscribeToAllTypes() + + def addTypes(self, types: 'list[str]') -> None: + '''Add the specified type names to subscription.''' + return self.__addTypes(types) + + def removeTypes(self, types: 'list[str]') -> None: + '''Remove the specified types from subscription.''' + return self.__removeTypes(types) + + def setTypes(self, types: 'list[str]') -> None: + '''Subscribe to specified types.''' + return self.__setTypes(types) + + def add(self, types: 'list[str]', entities: 'list[str]') -> None: + '''Add the specified entities and types to subscription. The type and symbol are copied + from the incoming object, if necessary, so the argument can be re-used + after the call. + + Args: + types (list[str]): not-null array of type names to subscribe. + entities (list[str]): not-null array of instruments to subscribe. + ''' + return self.__add(types, entities) + + def remove(self, types: 'list[str]', entities: 'list[str]') -> None: + '''Remove the specified entities and types from subscription. The type and symbol are copied + from the incoming objects, if necessary, so the arguments can be re-used + after the call. + + Args: + types (list[str]): not-null array of type names to unsubscribe. + entities (list[str]): not-null array of instruments to unsubscribe. + ''' + return self.__remove(types, entities) + + def addStreams(self, streams: 'list[TickStream]') -> None: + '''Add streams to subscription. Current time and filter is used to query + data from new sources. + + Args: + streams ('list[TickStream]'): Streams to add. + ''' + return self.__addStreams(streams) + + def removeStreams(self, streams: 'list[TickStream]') -> None: + '''Remove streams from subscription. + + Args: + streams (list[TickStream]): Streams to remove. + ''' + return self.__removeStreams(streams) + + def removeAllStreams(self) -> None: + '''Remove all streams from subscription.''' + return self.__removeAllStreams() + + def setTimeForNewSubscriptions(self, timestamp: int) -> None: + '''This method affects subsequent "add subscription" methods, + such as, for instance, addEntity(). New subscriptions start at + the specified time. + + Args: + timestamp (int): The time to use. + ''' + return self.__setTimeForNewSubscriptions(timestamp) + + + def __next(self): + return _tbapi.TickCursor___next(self) + + def __nextIfAvailable(self): + return _tbapi.TickCursor___nextIfAvailable(self) + + def __getMessage(self): + return _tbapi.TickCursor___getMessage(self) + + def __isAtEnd(self): + return _tbapi.TickCursor___isAtEnd(self) + + def __isClosed(self): + return _tbapi.TickCursor___isClosed(self) + + def __close(self): + return _tbapi.TickCursor___close(self) + + def __getCurrentStreamKey(self): + return _tbapi.TickCursor___getCurrentStreamKey(self) + + def __reset(self, dt): + return _tbapi.TickCursor___reset(self, dt) + + def __resetEntities(self, dt, entities): + return _tbapi.TickCursor___resetEntities(self, dt, entities) + + def __subscribeToAllEntities(self): + return _tbapi.TickCursor___subscribeToAllEntities(self) + + def __clearAllEntities(self): + return _tbapi.TickCursor___clearAllEntities(self) + + def __addEntities(self, entities): + return _tbapi.TickCursor___addEntities(self, entities) + + def __addEntity(self, entity): + return _tbapi.TickCursor___addEntity(self, entity) + + def __removeEntities(self, entities): + return _tbapi.TickCursor___removeEntities(self, entities) + + def __removeEntity(self, entity): + return _tbapi.TickCursor___removeEntity(self, entity) + + def __subscribeToAllTypes(self): + return _tbapi.TickCursor___subscribeToAllTypes(self) + + def __addTypes(self, types): + return _tbapi.TickCursor___addTypes(self, types) + + def __removeTypes(self, types): + return _tbapi.TickCursor___removeTypes(self, types) + + def __setTypes(self, types): + return _tbapi.TickCursor___setTypes(self, types) + + def __add(self, types, entities): + return _tbapi.TickCursor___add(self, types, entities) + + def __remove(self, types, entities): + return _tbapi.TickCursor___remove(self, types, entities) + + def __addStreams(self, streams): + return _tbapi.TickCursor___addStreams(self, streams) + + def __removeStreams(self, streams): + return _tbapi.TickCursor___removeStreams(self, streams) + + def __removeAllStreams(self): + return _tbapi.TickCursor___removeAllStreams(self) + + def __setTimeForNewSubscriptions(self, dt): + return _tbapi.TickCursor___setTimeForNewSubscriptions(self, dt) +TickCursor_swigregister = _tbapi.TickCursor_swigregister +TickCursor_swigregister(TickCursor) + +class TickLoader(_object): + """ + Object which consumes messages. + + Create loader from TickDb: + options = tbapi.LoadingOptions() + stream = tickdb.createLoader(stream, options) + + Create loader from TickStream: + options = tbapi.LoadingOptions() + stream = stream.createLoader(options) + """ + + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, TickLoader, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, TickLoader, name) + __repr__ = _swig_repr + + def __init__(self, loader): + this = _tbapi.new_TickLoader(loader) + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + __swig_destroy__ = _tbapi.delete_TickLoader + __del__ = lambda self: None + + + def send(self, message: InstrumentMessage) -> None: + '''This method is invoked to send a message to the object. + + Args: + message (InstrumentMessage): A temporary buffer with the message. + By convention, the message is only valid for the duration of this call. + ''' + return self.__send(message) + + def flush(self) -> None: + '''Flushes all buffered messages by sending them to server. + Note that calling 'send' method not guaranty that all messages will be delivered and stored to server. + ''' + return self.__flush() + + def close(self) -> None: + '''Flushes and closes the loader''' + return self.__close() + + def addListener(self, listener: 'ErrorListener') -> None: + '''Register error listener. All writing data errors will be delivered to the listener. + + Args: + listener (ErrorListener): error listener to register. + ''' + return self.__addListener(listener) + + def removeListener(self, listener: 'ErrorListener') -> None: + '''Unsubscribe registered error listener. + + Args: + listener (ErrorListener): error listener to unsubscribe. + ''' + return self.__removeListener(listener) + + def nErrorListeners(self) -> int: + '''Returns number of registered error listeners''' + return self.__nErrorListeners() + + def registerType(self, type: str) -> int: + '''Register type of sending message to get type id. For performance reasons, + you could specify type id instead of type name, for example: + + ``` + message = tbapi.InstrumentMessage() + message.typeId = loader.registerType("deltix.timebase.api.messages.universal.PackageHeader") + // as alternative, you could write: + // message.typeName = "deltix.timebase.api.messages.universal.PackageHeader" + loader.send(message) + ``` + + Args: + type (str): name of type to register. + + Returns: + int: id of registered type. + ''' + return self.__registerType(type) + + def registerInstrument(self, symbol: str) -> int: + '''Register instrument of sending message to get instrument id. For performance reasons, + you could specify instrument id instead of symbol and instrument type, for example: + + ``` + message = tbapi.InstrumentMessage() + message.instrumentId = loader.registerInstrument('AAPL') + // as alternative, you could write: + // message.symbol = 'AAPL' + loader.send(message) + ``` + + Args: + symbol (str): instrument ticker. + + Returns: + int: id of registered instrument. + ''' + return self.__registerInstrument(symbol) + + + + def __registerType(self, type_name): + return _tbapi.TickLoader___registerType(self, type_name) + + def __registerInstrument(self, instrument): + return _tbapi.TickLoader___registerInstrument(self, instrument) + + def __send(self, message): + return _tbapi.TickLoader___send(self, message) + + def __flush(self): + return _tbapi.TickLoader___flush(self) + + def __close(self): + return _tbapi.TickLoader___close(self) + + def __addListener(self, listener): + return _tbapi.TickLoader___addListener(self, listener) + + def __removeListener(self, listener): + return _tbapi.TickLoader___removeListener(self, listener) + + def __nErrorListeners(self): + return _tbapi.TickLoader___nErrorListeners(self) +TickLoader_swigregister = _tbapi.TickLoader_swigregister +TickLoader_swigregister(TickLoader) + +class ErrorListener(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, ErrorListener, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, ErrorListener, name) + __repr__ = _swig_repr + + def onError(self, errorClass: str, errorMsg: str) -> None: + return self.__onError() + + + def __onError(self, errorClass, errorMsg): + return _tbapi.ErrorListener___onError(self, errorClass, errorMsg) + __swig_destroy__ = _tbapi.delete_ErrorListener + __del__ = lambda self: None + + def __init__(self): + if self.__class__ == ErrorListener: + _self = None + else: + _self = self + this = _tbapi.new_ErrorListener(_self, ) + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + def __disown__(self): + self.this.disown() + _tbapi.disown_ErrorListener(self) + return weakref_proxy(self) +ErrorListener_swigregister = _tbapi.ErrorListener_swigregister +ErrorListener_swigregister(ErrorListener) + + + +from collections import defaultdict + +def stream_to_dict(db, stream, fields=None, ts_from=0, ts_to=JAVA_LONG_MAX_VALUE): + if ts_to > JAVA_LONG_MAX_VALUE: + ts_to = JAVA_LONG_MAX_VALUE + if not db.isOpen(): + raise Exception('Database is not opened.') + options = SelectionOptions() + options.to = ts_to + messages = [] + table = defaultdict(list) + with open_TickCursor(stream, ts_from, options) as cursor: + counter = 0 + while cursor.next(): + message = vars(cursor.getMessage()) + messages.append(message) + if fields is None: + def to_write(x): + return True + else: + def to_write(x): + return x in fields + for key in table.keys(): + if key in message: + table[key].append(message[key]) + del message[key] + else: + table[key].append(None) + for key in message: + if to_write(key): + table[key] = [None] * counter + table[key].append(message[key]) + counter += 1 + return table + + + +import xml.etree.ElementTree as ETree +import re + + +class ClassType: + ENUM_CLASS = 'enumClass' + RECORD_CLASS = 'recordClass' + + +class FieldType: + NONSTATIC = 'nonStaticDataField' + STATIC = 'staticDataField' + + +class SchemaDef: + def __init__(self, types, all_types): + self.types = types + self.all_types = all_types + + +class ClassDescDef: + def __init__(self, name): + self.name = name + + +class TypeDef(ClassDescDef): + def __init__(self, name, fields): + ClassDescDef.__init__(self, name) + self.fields = fields + + +class EnumDef(ClassDescDef): + def __init__(self, name, values): + ClassDescDef.__init__(self, name) + self.values = values + + +class FieldDef: + def __init__(self, name, data_type, nullable, field_type): + self.name = name + self.data_type = data_type + self.nullable = nullable + self.field_type = field_type + + +class SchemaParser: + + @staticmethod + def parse_Schema(stream): + xml = stream.metadata() + tree = ETree.fromstring(xml) + return SchemaParser.parse_classSet(tree) + + @staticmethod + def parse_value(value, prefix): + return value.find('prefix:symbol', prefix).text + + @staticmethod + def parse_field(field, prefix): + name = field.find('prefix:name', prefix) + field_type = SchemaParser.rm_brackets(list(field.attrib.values())[0]) + if field_type != FieldType.STATIC and field_type != FieldType.NONSTATIC: + raise Exception('Unknown field type {}'.format(field_type)) + data_type = field.find('prefix:type', prefix) + nullable = bool(data_type.find('prefix:nullable', prefix).text) + data_type = list(data_type.attrib.values())[0] + return FieldDef(name.text, data_type, nullable, field_type) + + @staticmethod + def parse_classDescriptor(class_desc, prefix): + name = class_desc.find('prefix:name', prefix) + type_ = list(class_desc.attrib.values())[0] + if type_ == ClassType.RECORD_CLASS: + fields = [SchemaParser.parse_field(field, prefix) for field in class_desc.findall('prefix:field', prefix)] + return TypeDef(name.text, fields) + elif type_ == ClassType.ENUM_CLASS: + values = [SchemaParser.parse_value(value, prefix) for value in class_desc.findall('prefix:value', prefix)] + return EnumDef(name.text, values) + else: + raise Exception('Unknown class descriptor type {}'.format(type_)) + + @staticmethod + def parse_classSet(class_set): + types = [] + all_types = [] + prefix = {} + key = class_set.tag + prefix['prefix'] = re.search(r'(?<={).*(?=})', key).group(0) + for class_desc in class_set.findall('prefix:classDescriptor', prefix): + class_desc = SchemaParser.parse_classDescriptor(class_desc, prefix) + all_types.append(class_desc) + if isinstance(class_desc, TypeDef): + types.append(class_desc) + return SchemaDef(types, all_types) + + @staticmethod + def rm_brackets(obj): + if isinstance(obj, str): + return re.sub('^{.*}', '', obj) + elif isinstance(obj, dict): + return {(SchemaParser.rm_brackets(key), value) for (key, value) in obj.items()} + else: + raise Exception('rm_brackets for {} is not implemented'.format(type(obj))) + + +def __getSchema(self): + return SchemaParser.parse_Schema(self) + + +TickStream.getSchema = __getSchema + +# This file is compatible with both classic and new-style classes. + + diff --git a/tbapi/project.properties b/tbapi/project.properties new file mode 100644 index 0000000..d23e890 --- /dev/null +++ b/tbapi/project.properties @@ -0,0 +1 @@ +version=6.0.15-SNAPSHOT \ No newline at end of file diff --git a/tbapi/tbapi.md b/tbapi/tbapi.md new file mode 100644 index 0000000..39d9b0c --- /dev/null +++ b/tbapi/tbapi.md @@ -0,0 +1,1310 @@ +# tbapi + +## StreamScope + +```python +class StreamScope(_object) +``` + +> Determines the scope of a stream's durability, if any. +> +> **Example**: +> +> ``` +> scope = tbapi.StreamScope('TRANSIENT') +> ``` +> +> Possible values: +> ``` +> DURABLE, +> EXTERNAL_FILE, +> TRANSIENT, +> RUNTIME +> ``` + +## WriteMode + +```python +class WriteMode(_object) +``` + +> APPEND: Adds only new data into a stream without truncations. +> REPLACE: Adds data into a stream and removes previous data older that first message time +> [truncate(first message time + 1)]. +> REWRITE: Default. Adds data into a stream and removes previous data by truncating using first message time. +> [truncate(first message time)]. +> TRUNCATE: Stream truncated every time when loader writes a messages earlier than last message time. +> +> **Example**: +> +> ``` +> mode = tbapi.StreamScope('TRUNCATE') +> ``` +> +> Possible values: +> ``` +> APPEND, +> REPLACE, +> REWRITE, +> TRUNCATE +> ``` + +## SelectionOptions + +```python +class SelectionOptions(_object) +``` + +> Options for selecting data from a stream. +> +> **Example**: +> +> ``` +> so = tbapi.SelectionOptions() +> so._from = 0 +> so.to = 100000 +> so.useCompression = False +> ... +> ``` + +## LoadingOptions + +```python +class LoadingOptions(_object) +``` + +> Options for loading data into a stream. +> +> **Example**: +> +> ``` +> lo = tbapi.LoadingOptions() +> lo.writeMode = tbapi.WriteMode('TRUNCATE') +> so.space = 'myspace' +> ... +> ``` + +## StreamOptions + +```python +class StreamOptions(_object) +``` + +> Stream definition attributes. +> +> **Example**: +> +> ``` +> so = tbapi.StreamOptions() +> so.name = key +> so.description = key +> so.scope = tbapi.StreamScope('DURABLE') +> so.distributionFactor = 1 +> so.highAvailability = False +> so.polymorphic = False +> so.metadata = schema +> +> db.createStream(key, options) +> ``` + +#### name + +```python +def name(name: str = None) -> None +``` + +> Optional user-readable name. + +#### description + +```python +def description(description: str = None) -> None +``` + +> Optional multi-line description. + +#### owner + +```python +def owner(owner: str = None) -> None +``` + +> Optional owner of stream. +> During stream creation it will be set +> equals to authenticated user name. + +#### location + +```python +def location(location: str = None) -> None +``` + +> Location of the stream (by default null). When defined this attribute provides alternative stream location (rather than default location under QuantServerHome) + +#### distributionRuleName + +```python +def distributionRuleName(distributionRuleName: str = None) -> None +``` + +> Class name of the distribution rule + +#### metadata + +```python +def metadata(metadata: str = None) -> None +``` + +> Stream metadata in XML format. To build metadata programatically, use tbapi.SchemaDef class. + +## QueryParameter + +```python +class QueryParameter(_object) +``` + +> Input parameter definition for a prepared statement. + +## TickDb + +```python +class TickDb(_object) +``` + +> The top-level implementation to the methods of the Deltix Tick +> Database engine. Instances of this class are created by static method +> createFromUrl: +> +> ``` +> db = tbapi.TickDb.createFromUrl('dxtick://localhost:8011') +> ``` +> +> or +> +> ``` +> db = tbapi.TickDb.createFromUrl('dxtick://localhost:8011', 'user', 'password') +> ``` + +#### createFromUrl + +```python +@staticmethod +def createFromUrl(url: str, user: str = None, password: str = None) -> "TickDb" +``` + +> Creates a new database instance with the specified root folder, or URL. +> +> **Arguments**: +> +> - `url` _str_ - Connection URL. +> - `user` _str_ - User. +> - `password` _str_ - Password. +> +> +> **Returns**: +> +> - `TickDb` - An un-opened TickDB instance. + +#### openFromUrl + +```python +@staticmethod +@contextmanager +def openFromUrl(url: str, readonly: bool, user: str = None, password: str = None) +``` + +> Creates a new database instance with the specified root folder, or URL, and opens it. +> +> **Arguments**: +> +> - `url` _str_ - Connection URL. +> - `readonly` _bool_ - Open data store in read-only mode. +> - `user` _str_ - User. +> - `password` _str_ - Password. +> +> +> **Returns**: +> +> - `TickDb` - An opened TickDB instance. + +#### isReadOnly + +```python +def isReadOnly() -> bool +``` + +> Determines whether the store is open as read-only. + +#### isOpen + +```python +def isOpen() -> bool +``` + +> Determines whether the store is open. + +#### open + +```python +def open(readOnlyMode: bool) -> bool +``` + +> Open the data store. +> +> **Arguments**: +> +> - `readOnlyMode` _bool_ - Open data store in read-only mode. + +#### close + +```python +def close() -> None +``` + +> Closes data store. + +#### format + +```python +def format() -> bool +``` + +> Create a new object on disk and format internally. +> The data store is left open for read-write at the end of this method. + +#### listStreams + +```python +def listStreams() -> 'list[TickStream]' +``` + +> Enumerates existing streams. +> +> **Returns**: +> +> - `list[TickStream]` - An array of existing stream objects. + +#### getStream + +```python +def getStream(key: str) -> 'TickStream' +``` + +> Looks up an existing stream by key. +> +> **Arguments**: +> +> - `key` _str_ - Identifies the stream. +> +> +> **Returns**: +> +> - `TickStream` - A stream object, or None if the key was not found. + +#### createStream + +```python +def createStream(key: str, options: StreamOptions) -> 'TickStream' +``` + +> Creates a new stream within the database. +> +> **Arguments**: +> +> - `key` _str_ - A required key later used to identify the stream. +> - `options` _StreamOptions_ - Options for creating the stream. +> +> +> **Returns**: +> +> - `TickStream` - A new instance of TickStream. + +#### createFileStream + +```python +def createFileStream(key: str, dataFile: str) -> 'TickStream' +``` + +> Creates a new stream mount to the given data file. +> +> **Arguments**: +> +> - `key` _str_ - A required key later used to identify the stream. +> - `dataFile` _str_ - Path to the data file (on server side). +> +> +> **Returns**: +> +> - `TickStream` - A new instance of TickStream. + +#### createCursor + +```python +def createCursor(stream: 'TickStream', options: SelectionOptions) -> 'TickCursor' +``` + +> Opens an initially empty cursor for reading data from multiple streams, +> according to the specified options. The messages +> are returned from the cursor strictly ordered by time. Within the same +> exact timestamp, the order of messages is undefined and may vary from +> call to call, i.e. it is non-deterministic. +> +> The cursor is returned initially empty and must be reset. +> The TickCursor class provides +> methods for dynamically re-configuring the subscription, or jumping to +> a different timestamp. +> +> **Arguments**: +> +> - `stream` _TickStream_ - Stream from which data will be selected. +> - `options` _SelectionOptions_ - Selection options. +> +> +> **Returns**: +> +> - `TickCursor` - A cursor used to read messages. + +#### tryCursor + +```python +@contextmanager +def tryCursor(stream: 'TickStream', options: SelectionOptions) -> 'TickCursor' +``` + +> contextmanager version of createCursor. Usage: +> ``` +> with db.newCursor(stream, options) as cursor: +> while cursor.next(): +> message = cursor.getMessage() +> ``` + +#### select + +```python +def select(timestamp: int, streams: 'list[TickStream]', options: SelectionOptions, types: 'list[str]', entities: 'list[str]') -> 'TickCursor' +``` + +> Opens a cursor for reading data from multiple streams, +> according to the specified options. The messages +> are returned from the cursor strictly ordered by time. Within the same +> exact time stamp, the order of messages is undefined and may vary from +> call to call, i.e. it is non-deterministic. +> +> Note that the arguments of this method only determine the initial +> configuration of the cursor. The TickCursor clsas provides +> methods for dynamically re-configuring the subscription, or jumping to +> a different timestamp. +> +> **Arguments**: +> +> - `timestamp` _int_ - The start timestamp in millis. +> - `streams` _list[TickStream]_ - Streams from which data will be selected. +> - `options` _SelectionOptions_ - Selection options. +> - `types` _list[str]_ - Specified message types to be subscribed. If null, then all types will be subscribed. +> - `entities` _list[str]_ - Specified entities to be subscribed. If null, then all entities will be subscribed. +> +> +> **Returns**: +> +> - `TickCursor` - A cursor used to read messages. + +#### trySelect + +```python +@contextmanager +def trySelect(timestamp: int, streams: 'list[TickStream]', options: SelectionOptions, types: 'list[str]', entities: 'list[str]') -> 'TickCursor' +``` + +> Contextmanager version of select. Usage: +> ``` +> with db.newSelect(timestamp, streams, options, types, entities) as cursor: +> while cursor.next(): +> message = cursor.getMessage() +> ``` + +#### createLoader + +```python +def createLoader(stream: 'TickStream', options: LoadingOptions) -> 'TickLoader' +``` + +> Creates a channel for loading data. The loader must be closed +> when the loading process is finished. +> +> **Arguments**: +> +> - `stream` _TickStream_ - stream for loading data. +> - `options` _SelectionOptions_ - Loading Options. +> +> +> **Returns**: +> +> - `TickLoader` - created loader. + +#### tryLoader + +```python +@contextmanager +def tryLoader(stream: 'TickStream', options: LoadingOptions) -> 'TickLoader' +``` + +> Contextmanager version of createLoader. Usage: +> +> with db.newLoader(stream, options) as loader: +> loader.send(message) + +#### executeQuery + +```python +def executeQuery(query: str, options: SelectionOptions = None, timestamp: int = JAVA_LONG_MIN_VALUE, entities: 'list[str]' = None, params: 'list[QueryParameter]' = []) -> 'TickCursor' +``` + +> Execute Query and creates a message source for reading data from it, +> according to the specified options. The messages +> are returned from the cursor strictly ordered by time. Within the same +> exact time stamp, the order of messages is undefined and may vary from +> call to call, i.e. it is non-deterministic. +> +> **Arguments**: +> +> - `query` _str_ - Query text element. +> - `options` _SelectionOptions_ - Selection options. +> - `timestamp` _int_ - The start timestamp in millis. +> - `entities` _list[str]_ - Specified entities to be subscribed. +> If null, then all entities will be subscribed. +> - `params` _list[QueryParameter]_ - The parameter values of the query. +> +> +> **Returns**: +> +> - `TickCursor` - An iterable message source to read messages. + +#### tryExecuteQuery + +```python +@contextmanager +def tryExecuteQuery(query: str, options: SelectionOptions = None, timestamp: int = JAVA_LONG_MIN_VALUE, entities: 'list[str]' = None, params: 'list[QueryParameter]' = []) -> 'TickCursor' +``` + +> Contextmanager version of executeQuery. Usage: +> ``` +> with db.newExecuteQuery('select * from stream') as cursor: +> while cursor.next(): +> message = cursor.getMessage() +> ``` + +## TickStream + +```python +class TickStream(_object) +``` + +> The stream is a time series of messages for a number of +> financial instruments ('entities'). Messages can be price bars, trade ticks, +> bid/offer ticks, or any of the many more built-in and user-defined types. +> In the simplest case, a database will have a single stream of data. +> Multiple streams can be used to represent data of different frequencies, or completely +> different factors. For instance, separate streams can represent +> 1-minute price bars and ticks for the same set of entities. Or, +> you can have price bars and volatility bars in separate streams. +> +> Get stream: +> ``` +> stream = tickdb.getStream('stream_key') +> ``` +> +> List stream: +> ``` +> streams = tickdb.listStreams() +> ``` + +#### key + +```python +def key() -> str +``` + +> Returns the key, which uniquely identifies the stream within its database. + +#### name + +```python +def name() -> str +``` + +> Returns a user-readable short name. + +#### distributionFactor + +```python +def distributionFactor() -> int +``` + +> Returns the target number of files to be used for storing data. + +#### description + +```python +def description() -> str +``` + +> Returns a user-readable multi-line description. + +#### owner + +```python +def owner() -> str +``` + +> Returns stream owner. + +#### location + +```python +def location() -> str +``` + +> Returns stream location. + +#### metadata + +```python +def metadata() -> str +``` + +> Returns stream schema (in xml format). + +#### scope + +```python +def scope() -> StreamScope +``` + +> Returns stream schema (in xml format). + +#### highAvailability + +```python +def highAvailability() -> bool +``` + +> Returns stream memory caching parameter. High availability durable streams are cached on startup. + +#### unique + +```python +def unique() -> bool +``` + +> Unique streams maintain in-memory cache of resent messages. +> This concept assumes that stream messages will have some field(s) marked as primary key. +> Primary key may be a simple field (e.g. symbol) or composite (e.g. symbol and portfolio ID). +> For each key TimeBase runtime maintains a copy of the last message received for this key (cache). +> Each new consumer will receive a snapshot of current cache at the beginning of live data subscription. + +#### polymorphic + +```python +def polymorphic() -> bool +``` + +> Returns whether the stream is configured as polymorphic. + +#### periodicity + +```python +def periodicity() -> str +``` + +> Returns Stream periodicity, if known. + +#### options + +```python +def options() -> StreamOptions +``` + +> Returns stream options object. + +#### describe + +```python +def describe() -> str +``` + +> Returns stream DDL description. + +#### setSchema + +```python +def setSchema(options: StreamOptions) -> bool +``` + +> Changes stream schema. +> +> **Arguments**: +> +> - `options` _StreamOptions_ - Stream options, that contains new schema xml. +> +> Returns +> - `bool` - True, if schema was changed successfully. + +#### listEntities + +```python +def listEntities() -> 'list[str]' +``` + +> Return an inclusive range of times for which the specified entities +> have data in the database. +> +> **Returns**: +> +> - `list[str]` - selected entities. + +#### truncate + +```python +def truncate(timestamp: int, entities: 'list[str]' = None) -> bool +``` + +> Truncates stream data for the given entities from given time +> +> **Arguments**: +> +> - `timestamp` _int_ - Timestamp in millis. If time less than stream start time, then all stream data will be deleted. +> - `entities` _list[str]_ - A list of entities. If None, all stream entities will be used. +> +> +> **Returns**: +> +> - `bool` - true, if stream was truncated successfully. + +#### clear + +```python +def clear(entities: 'list[str]' = None) -> bool +``` + +> Clear stream data for the given entities. +> +> **Arguments**: +> +> - `entities` _list[str]_ - A list of entities. If None, all stream entities will be used. + +#### purge + +```python +def purge(timestamp: int) -> bool +``` + +> Deletes stream data that is older than a specified time +> +> **Arguments**: +> +> timestamp (int):Purge time in milliseconds. +> +> +> **Returns**: +> +> - `bool` - true, if stream was purged successfully. + +#### deleteStream + +```python +def deleteStream() -> bool +``` + +> Deletes this stream +> +> **Returns**: +> +> - `bool` - true, if stream was deleted successfully. + +#### abortBackgroundProcess + +```python +def abortBackgroundProcess() -> bool +``` + +> Aborts active background process if any exists + +#### select + +```python +def select(timestamp: int, options: SelectionOptions, types: 'list[str]', entities: 'list[str]') -> 'TickCursor' +``` + +> Opens a cursor for reading data from this stream, according to the +> specified options. The messages +> are returned from the cursor strictly ordered by time. Within the same +> exact time stamp, the order of messages is undefined and may vary from +> call to call, i.e. it is non-deterministic. +> +> Note that the arguments of this method only determine the initial +> configuration of the cursor. The TickCursor interface provides +> methods for dynamically re-configuring the subscription, or jumping to +> a different timestamp. +> +> **Arguments**: +> +> - `timestamp` _int_ - The start timestamp in millis. +> - `options` _SelectionOptions_ - Selection options. +> - `types` _list[str]_ - Specified message types to be subscribed. If null, then all types will be subscribed. +> - `entities` _list[str]_ - Specified entities to be subscribed. If null, then all entities will be subscribed. +> +> +> **Returns**: +> +> - `TickCursor` - A cursor used to read messages. + +#### trySelect + +```python +@contextmanager +def trySelect(timestamp: int, options: SelectionOptions, types: 'list[str]', entities: 'list[str]') -> 'TickCursor' +``` + +> Contextmanager version of select. Usage: +> ``` +> with stream.newSelect(timestamp, options, types, entities) as cursor: +> while cursor.next(): +> message = cursor.getMessage() +> ``` + +#### createCursor + +```python +def createCursor(options: SelectionOptions) -> 'TickCursor' +``` + +> Creates a cursor for reading data from this stream, according to the +> specified options, but initially with a fully restricted filter. +> The user must call TickCursor.reset at least once, in order to +> begin retrieving data. This method is equivalent to (but is +> slightly more optimal than) calling createCursor(options) +> +> **Arguments**: +> +> - `options` _SelectionOptions_ - Selection Options. +> +> +> **Returns**: +> +> A cursor used to read messages. Never null. + +#### tryCursor + +```python +@contextmanager +def tryCursor(options: SelectionOptions) -> 'TickCursor' +``` + +> contextmanager version of createCursor. Usage: +> ``` +> with stream.newCursor(options) as cursor: +> while cursor.next(): +> message = cursor.getMessage() +> ``` + +#### createLoader + +```python +def createLoader(options: LoadingOptions) -> 'TickLoader' +``` + +> Creates a channel for loading data. The loader must be closed +> when the loading process is finished. +> +> **Arguments**: +> +> - `options` _SelectionOptions_ - Loading Options. +> +> +> **Returns**: +> +> - `TickLoader` - created loader. + +#### tryLoader + +```python +@contextmanager +def tryLoader(options: LoadingOptions) -> 'TickLoader' +``` + +> Contextmanager version of createLoader. Usage: +> ``` +> with stream.newLoader(options) as loader: +> loader.send(message) +> ``` + +#### listSpaces + +```python +def listSpaces() -> 'list[str]' +``` + +> Returns all created "spaces" for the stream. +> Default space returns as "" (empty string). +> If backing stream does not support spaces None will be returned. + +#### renameSpace + +```python +def renameSpace(newName: str, oldName: str) -> None +``` + +> Rename existing space. +> +> **Arguments**: +> +> - `nameName` _str_ - space to rename. +> - `oldName` _str_ - new space name. + +#### deleteSpaces + +```python +def deleteSpaces(spaces: 'list[str]') -> None +``` + +> Removed given 'spaces' permanently. +> +> **Arguments**: +> +> - `spaces` _list[str]_ - list of spaces names to delete. + +#### getTimeRange + +```python +def getTimeRange(entities: 'list[str]' = None) -> 'list[int]' +``` + +> Return an inclusive range of times for which the specified entities +> have data in the database. +> +> **Arguments**: +> +> - `entities` _list[str]_ - A list of entities. If empty, return for all. + +#### getSpaceTimeRange + +```python +def getSpaceTimeRange(space: str) -> 'list[int]' +``` + +> An array consisting of two long timestamps (from and to) or None if no data was found. +> +> **Arguments**: +> +> - `space` _str_ - space name. + +## TickCursor + +```python +class TickCursor(_object) +``` + +> A cursor (also known as iterator, or result set) for reading data from a +> stream. This class provides methods for dynamically reconfiguring the feed, +> as well as method reset for essentially re-opening the cursor on a completely different timestamp. +> +> To get a cursor, use select method from TickDb or TickStream objects, +> or call executeQuery to open cursor to QQL result set. +> +> Also cursor can be created with createCursor method, but it will be not initialized cursor, +> so cursor should be configured with types, entities and read time calling reset: +> ``` +> options = tbapi.SelectionOptions() +> cursor = tickdb.createCursor(stream, options) +> cursor.subscribeToAllEntities() +> cursor.subscribeToAllTypes() +> cursor.reset(timestamp) +> ``` + +#### next + +```python +def next() -> bool +``` + +> Moves cursor on to the next message. This method blocks until the next message becomes available, +> or until the cursor is determined to be at the end of the sequence. +> This method is illegal to call if isAtEnd() returns true. +> +> **Returns**: +> +> - `bool` - false if at the end of the cursor. + +#### getMessage + +```python +def getMessage() -> 'InstrumentMessage' +``` + +> Returns an InstrumentMessage object cursor points at. + +#### isAtEnd + +```python +def isAtEnd() -> bool +``` + +> Returns true if the last call to next() returned false. Returns false if next() has not been called yet. +> This method is legal to call any number of times at any point in the cursor's lifecycle. + +#### nextIfAvailable + +```python +def nextIfAvailable() -> int +``` + +> Moves cursor on to the next message, but this method NOT blocks until the next message becomes available. +> +> **Returns**: +> +> - `NextResult` - OK (0) if new message is available, +> END_OF_CURSOR (1) if cursor was closed, +> otherwise, UNAVAILABLE (2) + +#### isClosed + +```python +def isClosed() -> bool +``` + +> Returns true, if cursor was closed + +#### close + +```python +def close() -> None +``` + +> Close the cursor + +#### getCurrentStreamKey + +```python +def getCurrentStreamKey() -> str +``` + +> Return the key of the stream that is the source of the current message. + +#### reset + +```python +def reset(timestamp: int, entities: 'list[str]' = None) -> None +``` + +> Reposition the message source to a new point in time, while +> preserving current subscription. +> +> **Arguments**: +> +> - `timestamp` _int_ - The new position in time in millis. +> - `entities` _'list[str]'_ - list of entities to reset + +#### subscribeToAllEntities + +```python +def subscribeToAllEntities() -> None +``` + +> Subscribe to all available entities. + +#### clearAllEntities + +```python +def clearAllEntities() -> None +``` + +> Switch to selective subscription mode (if necessary) and clear the list. + +#### addEntity + +```python +def addEntity(entity: str) -> None +``` + +> Add the specified entity to subscription. The type and symbol are copied +> from the incoming object, if necessary, so the argument can be re-used +> after the call. +> +> Special note about options: +> The following fragment will subscribe to specific option contract "DAV 100417P00085000": +> cursor.addEntity('DAV 100417P00085000'); +> +> While the following will subscribe to option root (and you will get all instruments with this root): +> cursor.addEntity("DAV "); + +#### addEntities + +```python +def addEntities(entities: 'list[str]') -> None +``` + +> Bulk add the specified entities to subscription. The type and symbol are copied +> from the incoming objects, if necessary, so the arguments can be re-used +> after the call. + +#### removeEntities + +```python +def removeEntities(entities: 'list[str]') -> None +``` + +> Remove the specified entities from subscription. The type and symbol are copied +> from the incoming objects, if necessary, so the arguments can be re-used +> after the call. + +#### removeEntity + +```python +def removeEntity(entity: str) -> None +``` + +> Remove the specified entity from subscription. The type and symbol are copied +> from the incoming object, if necessary, so the argument can be re-used +> after the call. + +#### subscribeToAllTypes + +```python +def subscribeToAllTypes() -> None +``` + +> Subscribe to all available types (no filtering). + +#### addTypes + +```python +def addTypes(types: 'list[str]') -> None +``` + +> Add the specified type names to subscription. + +#### removeTypes + +```python +def removeTypes(types: 'list[str]') -> None +``` + +> Remove the specified types from subscription. + +#### setTypes + +```python +def setTypes(types: 'list[str]') -> None +``` + +> Subscribe to specified types. + +#### add + +```python +def add(types: 'list[str]', entities: 'list[str]') -> None +``` + +> Add the specified entities and types to subscription. The type and symbol are copied +> from the incoming object, if necessary, so the argument can be re-used +> after the call. +> +> **Arguments**: +> +> - `types` _list[str]_ - not-null array of type names to subscribe. +> - `entities` _list[str]_ - not-null array of instruments to subscribe. + +#### remove + +```python +def remove(types: 'list[str]', entities: 'list[str]') -> None +``` + +> Remove the specified entities and types from subscription. The type and symbol are copied +> from the incoming objects, if necessary, so the arguments can be re-used +> after the call. +> +> **Arguments**: +> +> - `types` _list[str]_ - not-null array of type names to unsubscribe. +> - `entities` _list[str]_ - not-null array of instruments to unsubscribe. + +#### addStreams + +```python +def addStreams(streams: 'list[TickStream]') -> None +``` + +> Add streams to subscription. Current time and filter is used to query +> data from new sources. +> +> **Arguments**: +> +> - `streams` _'list[TickStream]'_ - Streams to add. + +#### removeStreams + +```python +def removeStreams(streams: 'list[TickStream]') -> None +``` + +> Remove streams from subscription. +> +> **Arguments**: +> +> - `streams` _list[TickStream]_ - Streams to remove. + +#### removeAllStreams + +```python +def removeAllStreams() -> None +``` + +> Remove all streams from subscription. + +#### setTimeForNewSubscriptions + +```python +def setTimeForNewSubscriptions(timestamp: int) -> None +``` + +> This method affects subsequent "add subscription" methods, +> such as, for instance, addEntity(). New subscriptions start at +> the specified time. +> +> **Arguments**: +> +> - `timestamp` _int_ - The time to use. + +## TickLoader + +```python +class TickLoader(_object) +``` + +> Object which consumes messages. +> +> Create loader from TickDb: +> options = tbapi.LoadingOptions() +> stream = tickdb.createLoader(stream, options) +> +> Create loader from TickStream: +> options = tbapi.LoadingOptions() +> stream = stream.createLoader(options) + +#### send + +```python +def send(message: InstrumentMessage) -> None +``` + +> This method is invoked to send a message to the object. +> +> **Arguments**: +> +> - `message` _InstrumentMessage_ - A temporary buffer with the message. +> By convention, the message is only valid for the duration of this call. + +#### flush + +```python +def flush() -> None +``` + +> Flushes all buffered messages by sending them to server. +> Note that calling 'send' method not guaranty that all messages will be delivered and stored to server. + +#### close + +```python +def close() -> None +``` + +> Flushes and closes the loader + +#### addListener + +```python +def addListener(listener: 'ErrorListener') -> None +``` + +> Register error listener. All writing data errors will be delivered to the listener. +> +> **Arguments**: +> +> - `listener` _ErrorListener_ - error listener to register. + +#### removeListener + +```python +def removeListener(listener: 'ErrorListener') -> None +``` + +> Unsubscribe registered error listener. +> +> **Arguments**: +> +> - `listener` _ErrorListener_ - error listener to unsubscribe. + +#### nErrorListeners + +```python +def nErrorListeners() -> int +``` + +> Returns number of registered error listeners + +#### registerType + +```python +def registerType(type: str) -> int +``` + +> Register type of sending message to get type id. For performance reasons, +> you could specify type id instead of type name, for example: +> +> +> ``` +> message = tbapi.InstrumentMessage() +> message.typeId = loader.registerType("deltix.timebase.api.messages.universal.PackageHeader") +> // as alternative, you could write: +> // message.typeName = "deltix.timebase.api.messages.universal.PackageHeader" +> loader.send(message) +> ``` +> +> **Arguments**: +> +> - `type` _str_ - name of type to register. +> +> +> **Returns**: +> +> - `int` - id of registered type. + +#### registerInstrument + +```python +def registerInstrument(symbol: str) -> int +``` + +> Register instrument of sending message to get instrument id. For performance reasons, +> you could specify instrument id instead of symbol and instrument type, for example: +> +> +> ``` +> message = tbapi.InstrumentMessage() +> message.instrumentId = loader.registerInstrument('AAPL') +> // as alternative, you could write: +> // message.symbol = 'AAPL' +> loader.send(message) +> ``` +> +> **Arguments**: +> +> - `symbol` _str_ - instrument ticker. +> +> +> **Returns**: +> +> - `int` - id of registered instrument. + diff --git a/tests/TestAll.py b/tests/TestAll.py index 93488a3..cc6efa3 100644 --- a/tests/TestAll.py +++ b/tests/TestAll.py @@ -36,14 +36,9 @@ reportsDir = testdir + 'reports' if not os.path.exists(reportsDir): os.makedirs(reportsDir) -reportFile = reportsDir + '/dxapi-test-report-' + datetime.datetime.now().strftime('%d%m%Y-%H%M%S') + '.html' +reportFile = reportsDir + '/tbapi-test-report-' + datetime.datetime.now().strftime('%d%m%Y-%H%M%S') + '.html' fileStream = open(reportFile, 'w') -version = "2" -if sys.version.startswith("3.6"): - version = "3.6" -elif sys.version.startswith("3.7"): - version = "3.7" -title = 'Dxapi test report (Python version: ' + (version) + ')' +title = 'Tbapi test report (Python version: ' + (sys.version) + ')' runner = HTMLTestRunner.HTMLTestRunner(stream = fileStream, title = title) result = runner.run(suite) diff --git a/tests/TestCursor.py b/tests/TestCursor.py index 6a3760c..74831bd 100644 --- a/tests/TestCursor.py +++ b/tests/TestCursor.py @@ -1,7 +1,7 @@ import unittest import servertest import testutils -import dxapi +import tbapi class CursorTest(servertest.TestWithStreams): @@ -11,7 +11,7 @@ def test_ReadSmoke(self): def test_SelectFromTo(self): stream = self.db.getStream(self.streamKeys[0]) - options = dxapi.SelectionOptions() + options = tbapi.SelectionOptions() options._from = 5000 options.to = 6000 cursor = stream.createCursor(options) @@ -31,7 +31,7 @@ def test_SelectFromTo(self): def test_SelectFromToReverse(self): stream = self.db.getStream(self.streamKeys[0]) - options = dxapi.SelectionOptions() + options = tbapi.SelectionOptions() options._from = 5000 options.to = 6000 options.reverse = True @@ -51,7 +51,7 @@ def test_SelectFromToReverse(self): def test_Reset(self): stream = self.db.getStream(self.streamKeys[0]) - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) cursor.reset(9000) self.assertTrue(cursor.next()) @@ -71,7 +71,7 @@ def test_Reset(self): def test_SetTimeForNewSubscription(self): barStream = self.db.getStream(self.streamKeys[0]) - cursor = self.db.select(0, [barStream], dxapi.SelectionOptions(), None, []) + cursor = self.db.select(0, [barStream], tbapi.SelectionOptions(), None, []) cursor.addEntities([self.entities['AAPL']]) messages = self.read(cursor, 10) @@ -102,7 +102,7 @@ def test_SubscribeTypes(self): barStream = self.db.getStream(self.streamKeys[0]) tradeBBOStream = self.db.getStream(self.streamKeys[1]) l2Stream = self.db.getStream(self.streamKeys[2]) - cursor = self.db.select(0, [tradeBBOStream, barStream, l2Stream], dxapi.SelectionOptions(), None, None) + cursor = self.db.select(0, [tradeBBOStream, barStream, l2Stream], tbapi.SelectionOptions(), None, None) # all types typeSet = set(self.types.values()) @@ -137,7 +137,7 @@ def test_SubscribeTypes(self): def test_SubscribeEntities(self): tradeBBOStream = self.db.getStream(self.streamKeys[0]) barStream = self.db.getStream(self.streamKeys[1]) - cursor = self.db.select(0, [tradeBBOStream, barStream], dxapi.SelectionOptions(), None, None) + cursor = self.db.select(0, [tradeBBOStream, barStream], tbapi.SelectionOptions(), None, None) # all entities entitySet = set(self.entities.keys()) @@ -180,7 +180,7 @@ def test_SubscribeTypeAndEntities(self): l2Stream = self.db.getStream(self.streamKeys[2]) cursor = self.db.select(0, [tradeBBOStream, barStream, l2Stream], - dxapi.SelectionOptions(), + tbapi.SelectionOptions(), [self.types['bbo'], self.types['trade']], [self.entities['AAPL'], self.entities['IBM']]) @@ -211,7 +211,7 @@ def test_SubscribeStreams(self): tradeBBOStream = self.db.getStream(self.streamKeys[1]) l2Stream = self.db.getStream(self.streamKeys[2]) - cursor = self.db.select(0, [tradeBBOStream], dxapi.SelectionOptions(), None, None) + cursor = self.db.select(0, [tradeBBOStream], tbapi.SelectionOptions(), None, None) # trade streams typeSet = set([self.types['trade'], self.types['bbo']]) @@ -241,7 +241,7 @@ def test_GetCurrentStreamKey(self): tradeBBOStream = self.db.getStream(self.streamKeys[1]) l2Stream = self.db.getStream(self.streamKeys[2]) - cursor = self.db.select(0, [barStream, tradeBBOStream, l2Stream], dxapi.SelectionOptions(), None, None) + cursor = self.db.select(0, [barStream, tradeBBOStream, l2Stream], tbapi.SelectionOptions(), None, None) while cursor.next(): stream = cursor.getCurrentStreamKey() typeName = cursor.getMessage().typeName @@ -256,19 +256,19 @@ def test_GetCurrentStreamKey(self): def test_ContextManager(self): stream = self.db.getStream(self.streamKeys[0]) - with self.db.tryCursor(stream, dxapi.SelectionOptions()) as cursor: + with self.db.tryCursor(stream, tbapi.SelectionOptions()) as cursor: self.assertTrue(cursor.next()) self.assertTrue(cursor.getMessage() != None) - with stream.tryCursor(dxapi.SelectionOptions()) as cursor: + with stream.tryCursor(tbapi.SelectionOptions()) as cursor: self.assertTrue(cursor.next()) self.assertTrue(cursor.getMessage() != None) - with self.db.trySelect(0, [stream], dxapi.SelectionOptions(), None, None) as cursor: + with self.db.trySelect(0, [stream], tbapi.SelectionOptions(), None, None) as cursor: self.assertTrue(cursor.next()) self.assertTrue(cursor.getMessage() != None) - with stream.trySelect(0, dxapi.SelectionOptions(), None, None) as cursor: + with stream.trySelect(0, tbapi.SelectionOptions(), None, None) as cursor: self.assertTrue(cursor.next()) self.assertTrue(cursor.getMessage() != None) diff --git a/tests/TestEntities.py b/tests/TestEntities.py index 85a1972..c990c5d 100644 --- a/tests/TestEntities.py +++ b/tests/TestEntities.py @@ -3,13 +3,13 @@ import generators import testutils import time -import dxapi +import tbapi class TestEntities(servertest.TBServerTest): def test_LoadManyEntities(self): stream = self.createStream('bars1min', False) - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) try: loadCount = 0 generator = generators.BarGenerator(0, 0, 35000, ['AAPL']) @@ -27,7 +27,7 @@ def test_LoadManyEntities(self): if loader != None: loader.close() - cursor = stream.select(0, dxapi.SelectionOptions(), None, None) + cursor = stream.select(0, tbapi.SelectionOptions(), None, None) count = 0 while cursor.next(): count += 1 @@ -37,4 +37,4 @@ def test_LoadManyEntities(self): self.assertEqual(count, 35000) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestLoader.py b/tests/TestLoader.py index 10e7a6d..9843e3c 100644 --- a/tests/TestLoader.py +++ b/tests/TestLoader.py @@ -2,7 +2,7 @@ import servertest import testutils, generators import time -import dxapi +import tbapi class TestLoader(servertest.TBServerTest): @@ -80,10 +80,10 @@ def test_registerTypesAndEntities(self): self.assertIsNotNone(stream) self.assertEqual(self.streamCount(key), 0) # check stream is empty - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) self.assertIsNotNone(loader) - message = dxapi.InstrumentMessage() + message = tbapi.InstrumentMessage() # register types bboTypeId = loader.registerType('deltix.timebase.api.messages.BestBidOfferMessage') @@ -115,7 +115,7 @@ def test_registerTypesAndEntities(self): loader.close() - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) self.assertIsNotNone(cursor) self.assertTrue(cursor.next()) self.assertEqual(cursor.getMessage().typeName, 'deltix.timebase.api.messages.BestBidOfferMessage') @@ -143,7 +143,7 @@ def test_registerTypesAndEntities(self): def test_Flush(self): key = self.streamKeys[0] stream = self.createStreamQQL(key) - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) try: barGenerator = generators.BarGenerator(0, 1000000000, 21, ['EPAM']) @@ -196,7 +196,7 @@ def test_Flush(self): def test_MassFlush(self): key = self.streamKeys[0] stream = self.createStreamQQL(key) - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) try: count = 100000 barGenerator = generators.BarGenerator(0, 1000000000, count, ['EPAM']) @@ -213,7 +213,7 @@ def test_MassFlush(self): def streamCount(self, key): stream = self.db.getStream(key) - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) cursor.reset(0) try: count = 0 diff --git a/tests/TestMemoryManagement.py b/tests/TestMemoryManagement.py index 305584d..7de5f5a 100644 --- a/tests/TestMemoryManagement.py +++ b/tests/TestMemoryManagement.py @@ -1,6 +1,6 @@ import unittest import servertest -import dxapi +import tbapi import sys import generators @@ -66,7 +66,7 @@ def writeL2EmptyMessages(self): stream = self.db.getStream(key) self.assertIsNotNone(stream) - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) self.assertIsNotNone(loader) try: loaded = 0 @@ -87,7 +87,7 @@ def readL2EmptyMessages(self): stream = self.db.getStream(key) self.assertIsNotNone(stream) - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) self.assertIsNotNone(cursor) try: read = 0 @@ -129,7 +129,7 @@ def next(self): return True def newAction(self): - action = dxapi.InstrumentMessage() + action = tbapi.InstrumentMessage() action.typeName = None action.level = None action.isAsk = True @@ -143,4 +143,4 @@ def newAction(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestMultithreaded.py b/tests/TestMultithreaded.py index 9b00119..42793e4 100644 --- a/tests/TestMultithreaded.py +++ b/tests/TestMultithreaded.py @@ -2,7 +2,7 @@ import servertest import testutils import os, sys, threading, time -import dxapi +import tbapi from sys import version_info @@ -102,10 +102,10 @@ def test_MassiveLoads(self): def createStream(self, fileName, key, polymorphic = False): with open(testdir + 'testdata/' + fileName + '.xml', 'r') as schemaFile: schema = schemaFile.read() - options = dxapi.StreamOptions() + options = tbapi.StreamOptions() options.name(key) options.description(key) - options.scope = dxapi.StreamScope('DURABLE') + options.scope = tbapi.StreamScope('DURABLE') options.distributionFactor = 1 options.highAvailability = False options.polymorphic = polymorphic @@ -124,7 +124,7 @@ def loadTradeBBOThread(self, key, count): def readStream(self, results, num, key, startTime = 0, timeout = 30, readUntil = 1000000000): stream = self.db.getStream(key) - options = dxapi.SelectionOptions() + options = tbapi.SelectionOptions() options.live = True cursor = stream.createCursor(options) @@ -136,12 +136,12 @@ def readStream(self, results, num, key, startTime = 0, timeout = 30, readUntil = if time.time() - tStart > timeout: break state = cursor.nextIfAvailable() - if state == dxapi.OK: + if state == tbapi.OK: message = cursor.getMessage() messages += 1 if messages >= readUntil: break - elif state == dxapi.END_OF_CURSOR: + elif state == tbapi.END_OF_CURSOR: break print("Total read from " + key + ": " + str(messages)) results[num] = messages diff --git a/tests/TestNextIfAvailable.py b/tests/TestNextIfAvailable.py index b3cb6f3..e05dc1a 100644 --- a/tests/TestNextIfAvailable.py +++ b/tests/TestNextIfAvailable.py @@ -2,7 +2,7 @@ import servertest import testutils import copy -import dxapi +import tbapi class TestNextIfAvailable(servertest.TestWithStreams): @@ -18,12 +18,12 @@ def test_Multistream(self): barStream = self.db.getStream(self.streamKeys[0]) tradeBBOStream = self.db.getStream(self.streamKeys[1]) l2Stream = self.db.getStream(self.streamKeys[2]) - cursor = self.db.select(0, [tradeBBOStream, barStream, l2Stream], dxapi.SelectionOptions(), None, None) + cursor = self.db.select(0, [tradeBBOStream, barStream, l2Stream], tbapi.SelectionOptions(), None, None) self.assertEqual(self.readCursorIfAvailable(cursor), 30000) def test_Reset(self): stream = self.db.getStream(self.streamKeys[0]) - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) cursor.reset(9000) self.assertEqual(self.readMessage(cursor).timestamp, self.msToNs(9000)) @@ -41,7 +41,7 @@ def test_Reset(self): def test_SelectReverse(self): stream = self.db.getStream(self.streamKeys[0]) - options = dxapi.SelectionOptions() + options = tbapi.SelectionOptions() options.reverse = True cursor = stream.createCursor(options) @@ -62,9 +62,9 @@ def test_SelectReverse(self): def readMessage(self, cursor): while True: state = cursor.nextIfAvailable() - if state == dxapi.OK: + if state == tbapi.OK: return cursor.getMessage() - elif state == dxapi.END_OF_CURSOR: + elif state == tbapi.END_OF_CURSOR: return None def readMessagesCount(self, cursor, count): @@ -74,7 +74,7 @@ def readMessagesCount(self, cursor, count): return messages def readStreamIfAvailable(self, stream): - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) try: return self.readCursorIfAvailable(cursor) finally: @@ -85,11 +85,11 @@ def readCursorIfAvailable(self, cursor): readCount = 0 while True: state = cursor.nextIfAvailable() - if state == dxapi.OK: + if state == tbapi.OK: message = cursor.getMessage() readCount += 1 self.printReadingInfo(readCount) - elif state == dxapi.END_OF_CURSOR: + elif state == tbapi.END_OF_CURSOR: break print("Read " + str(readCount) + " messages") return readCount @@ -120,4 +120,4 @@ def checkSymbols(self, messages, symbols): self.assertEqual(smbSet, symbols) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestPackageHeader.py b/tests/TestPackageHeader.py index 9a52d27..45a83da 100644 --- a/tests/TestPackageHeader.py +++ b/tests/TestPackageHeader.py @@ -1,7 +1,7 @@ import unittest import servertest import testutils -import dxapi +import tbapi class TestPackageHeader(servertest.TBServerTest): @@ -19,7 +19,7 @@ def test_Decimal64(self): readCount = 0 currentPrice = 0 - cursor = stream.select(0, dxapi.SelectionOptions(), None, None) + cursor = stream.select(0, tbapi.SelectionOptions(), None, None) while cursor.next(): readCount += 1 message = cursor.getMessage() diff --git a/tests/TestQQL.py b/tests/TestQQL.py index 629df3c..23a0ecf 100644 --- a/tests/TestQQL.py +++ b/tests/TestQQL.py @@ -1,6 +1,6 @@ import unittest import servertest -import dxapi +import tbapi import sys class TestQQL(servertest.TestWithStreams): @@ -12,7 +12,7 @@ def test_ExecuteQuery(self): cursor.close() def test_ExecuteQueryWithOptions(self): - options = dxapi.SelectionOptions() + options = tbapi.SelectionOptions() options._from = 5000 options.to = 6000 cursor = self.db.executeQuery("select * from bars1min", options) @@ -28,7 +28,7 @@ def test_ExecuteQueryWithOptions(self): cursor.close() def test_ExecuteQueryWithInstruments(self): - cursor = self.db.executeQuery("select * from l2", dxapi.SelectionOptions(), 10000, [self.entities['IBM']], []) + cursor = self.db.executeQuery("select * from l2", tbapi.SelectionOptions(), 10000, [self.entities['IBM']], []) self.checkCursorSymbols(cursor, set(['IBM'])) cursor.close() diff --git a/tests/TestSpeed.py b/tests/TestSpeed.py index 49826ff..4e829f7 100644 --- a/tests/TestSpeed.py +++ b/tests/TestSpeed.py @@ -2,7 +2,7 @@ import servertest import generators import time -import dxapi +import tbapi class TestSpeed(servertest.TBServerTest): @@ -27,7 +27,7 @@ def test_FixedReadWriteSpeed(self): stream = self.db.getStream(key) self.assertIsNotNone(stream) - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) self.assertIsNotNone(loader) try: typeId = loader.registerType('deltix.timebase.api.messages.BarMessage') @@ -53,7 +53,7 @@ def test_FixedReadWriteSpeed(self): finally: loader.close() - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) self.assertIsNotNone(cursor) try: read = 0 @@ -80,7 +80,7 @@ def test_PolymorphicReadWriteSpeed(self): stream = self.db.getStream(key) self.assertIsNotNone(stream) - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) self.assertIsNotNone(loader) try: tradeId = loader.registerType('deltix.timebase.api.messages.TradeMessage') @@ -114,7 +114,7 @@ def test_PolymorphicReadWriteSpeed(self): finally: loader.close() - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) self.assertIsNotNone(cursor) try: read = 0 @@ -141,7 +141,7 @@ def test_L2ReadWriteSpeed(self): stream = self.db.getStream(key) self.assertIsNotNone(stream) - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) self.assertIsNotNone(loader) try: typeId = loader.registerType('deltix.timebase.api.messages.L2Message') @@ -167,7 +167,7 @@ def test_L2ReadWriteSpeed(self): finally: loader.close() - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) self.assertIsNotNone(cursor) try: read = 0 @@ -190,4 +190,4 @@ def test_L2ReadWriteSpeed(self): cursor.close() if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestStream.py b/tests/TestStream.py index 291cd3d..67e3bad 100644 --- a/tests/TestStream.py +++ b/tests/TestStream.py @@ -2,7 +2,7 @@ import servertest import testutils import time -import dxapi +import tbapi class TestStream(servertest.TestWithStreams): diff --git a/tests/TestTickDB.py b/tests/TestTickDB.py index 57022e0..f318558 100644 --- a/tests/TestTickDB.py +++ b/tests/TestTickDB.py @@ -1,7 +1,7 @@ import os import unittest import servertest -import dxapi +import tbapi testdir = os.path.dirname(__file__) if testdir != "": @@ -20,7 +20,7 @@ def test_isReadOnly(self): self.assertFalse(self.db.isReadOnly()) def test_open(self): - with dxapi.TickDb.openFromUrl(self.dxtickURL(), True) as tickdb: + with tbapi.TickDb.openFromUrl(self.dxtickURL(), True) as tickdb: self.assertTrue(tickdb.isOpen()) #def test_createStream(self): @@ -29,10 +29,10 @@ def test_open(self): # with open(testdir + 'testdata/' + key + '.xml', 'r') as schemaFile: # schema = schemaFile.read() - # options = dxapi.StreamOptions() + # options = tbapi.StreamOptions() # options.name(key) # options.description(key) - # options.scope = dxapi.StreamScope('DURABLE') + # options.scope = tbapi.StreamScope('DURABLE') # options.distributionFactor = 9 # options.highAvailability = False # options.polymorphic = False diff --git a/tests/generators.py b/tests/generators.py index 52741ce..c36e7a3 100644 --- a/tests/generators.py +++ b/tests/generators.py @@ -1,10 +1,10 @@ -import dxapi +import tbapi import random class BaseGenerator: def __init__(self, time, timeInterval, count, symbols): - self.message = dxapi.InstrumentMessage() + self.message = tbapi.InstrumentMessage() self.count = count self.currentTime = time @@ -133,7 +133,7 @@ def next(self): return True def newAction(self): - action = dxapi.InstrumentMessage() + action = tbapi.InstrumentMessage() action.typeName = self.actionTypeName action.level = random.randint(0, 100) action.isAsk = True @@ -172,7 +172,7 @@ def next(self): def newEntry(self, level): self.currentPrice += 1.1 - entry = dxapi.InstrumentMessage() + entry = tbapi.InstrumentMessage() entry.typeName = self.entryTypeName entry.level = level entry.side = 'BID' diff --git a/tests/servertest.py b/tests/servertest.py index 7440695..ccbd905 100644 --- a/tests/servertest.py +++ b/tests/servertest.py @@ -15,7 +15,7 @@ sys.path.append(testdir + "..") import testutils -import dxapi +import tbapi class TBServerTest(unittest.TestCase): @@ -59,7 +59,7 @@ def tearDownClass(cls): def setUp(self): url = self.dxtickURL() print('Connecting to ' + str(url) + "...") - self.db = dxapi.TickDb.createFromUrl(url) + self.db = tbapi.TickDb.createFromUrl(url) self.db.open(False) print('OK') @@ -109,7 +109,7 @@ def createStream(self, key, polymorphic = False): with open(testdir + 'testdata/' + key + '.xml', 'r') as schemaFile: schema = schemaFile.read() - options = dxapi.StreamOptions() + options = tbapi.StreamOptions() options.polymorphic = polymorphic options.metadata(schema) @@ -199,7 +199,7 @@ def read(self, cursor, count): return messages def readMessages(self, stream, _from, to): - options = dxapi.SelectionOptions() + options = tbapi.SelectionOptions() options._from = _from options.to = to cursor = stream.createCursor(options) diff --git a/tests/testutils.py b/tests/testutils.py index c50fb11..f3918b2 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -1,8 +1,8 @@ -import dxapi +import tbapi import generators def loadTradeBBO(stream, count, startTime = 0, timeInterval = 1000000000, symbols = ['MSFT', 'ORCL']): - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) try: loadCount = 0 tradeGenerator = generators.TradeGenerator(startTime, timeInterval, count, symbols) @@ -25,7 +25,7 @@ def loadTradeBBO(stream, count, startTime = 0, timeInterval = 1000000000, symbol loader.close() def loadBars(stream, count, startTime = 0, timeInterval = 1000000000, symbols = ['MSFT', 'ORCL'], space = None): - options = dxapi.LoadingOptions() + options = tbapi.LoadingOptions() if space != None: options.space = space loader = stream.createLoader(options) @@ -45,7 +45,7 @@ def loadBars(stream, count, startTime = 0, timeInterval = 1000000000, symbols = loader.close() def loadWithBars(stream, count, startTime = 0, timeInterval = 1000000000, symbols = ['MSFT', 'ORCL'], space = None): - options = dxapi.LoadingOptions() + options = tbapi.LoadingOptions() if space != None: options.space = space with stream.tryLoader(options) as loader: @@ -61,7 +61,7 @@ def loadWithBars(stream, count, startTime = 0, timeInterval = 1000000000, symbol return loadCount def loadL2(stream, count, actionsCount = 5, startTime = 0, timeInterval = 1000000000, symbols = ['MSFT', 'ORCL']): - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) try: loadCount = 0 barGenerator = generators.L2Generator(startTime, timeInterval, count, symbols, actionsCount) @@ -78,7 +78,7 @@ def loadL2(stream, count, actionsCount = 5, startTime = 0, timeInterval = 100000 loader.close() def loadUniversal(stream, count, entriesCount = 5, startTime = 0, timeInterval = 1000000000, symbols = ['BTCUSD']): - loader = stream.createLoader(dxapi.LoadingOptions()) + loader = stream.createLoader(tbapi.LoadingOptions()) try: loadCount = 0 universalGenerator = generators.UniversalGenerator(startTime, timeInterval, count, symbols, entriesCount) @@ -95,7 +95,7 @@ def loadUniversal(stream, count, entriesCount = 5, startTime = 0, timeInterval = loader.close() def readStream(stream): - cursor = stream.createCursor(dxapi.SelectionOptions()) + cursor = stream.createCursor(tbapi.SelectionOptions()) try: readCount = 0 while cursor.next():