├── .clang-format ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── CONTRIBUTING.md ├── GETTING_STARTED.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── aat ├── __init__.py ├── __main__.py ├── common.py ├── config │ ├── __init__.py │ ├── enums.py │ └── parser.py ├── core │ ├── __init__.py │ ├── data │ │ ├── __init__.py │ │ ├── cpp.py │ │ ├── data.py │ │ ├── error.py │ │ ├── event.py │ │ ├── order.py │ │ └── trade.py │ ├── exchange │ │ ├── __init__.py │ │ ├── cpp.py │ │ ├── db.py │ │ └── exchange.py │ ├── handler │ │ ├── __init__.py │ │ ├── handler.py │ │ └── print.py │ ├── instrument │ │ ├── __init__.py │ │ ├── calendar.py │ │ ├── cpp.py │ │ ├── db.py │ │ └── instrument.py │ ├── order_book │ │ ├── __init__.py │ │ ├── base.py │ │ ├── collector │ │ │ ├── __init__.py │ │ │ └── collector.py │ │ ├── cpp.py │ │ ├── order_book │ │ │ ├── __init__.py │ │ │ ├── accessors.py │ │ │ ├── lite.py │ │ │ └── order_book.py │ │ ├── price_level │ │ │ ├── __init__.py │ │ │ ├── price_level.py │ │ │ └── ro.py │ │ └── utils.py │ ├── position │ │ ├── __init__.py │ │ ├── account.py │ │ ├── cash.py │ │ ├── cpp.py │ │ ├── db.py │ │ └── position.py │ └── table.py ├── cpp │ ├── CMakeLists.txt │ ├── include │ │ └── aat │ │ │ ├── common.hpp │ │ │ ├── config │ │ │ ├── enums.hpp │ │ │ └── parser.hpp │ │ │ ├── core │ │ │ ├── data │ │ │ │ ├── data.hpp │ │ │ │ ├── event.hpp │ │ │ │ ├── order.hpp │ │ │ │ └── trade.hpp │ │ │ ├── exchange │ │ │ │ └── exchange.hpp │ │ │ ├── instrument │ │ │ │ └── instrument.hpp │ │ │ ├── order_book │ │ │ │ ├── collector.hpp │ │ │ │ ├── order_book.hpp │ │ │ │ └── price_level.hpp │ │ │ └── position │ │ │ │ ├── account.hpp │ │ │ │ ├── cash.hpp │ │ │ │ └── position.hpp │ │ │ └── python │ │ │ └── binding.hpp │ ├── src │ │ ├── config │ │ │ ├── enums.cpp │ │ │ └── parser.cpp │ │ ├── core │ │ │ ├── data │ │ │ │ ├── data.cpp │ │ │ │ ├── event.cpp │ │ │ │ ├── order.cpp │ │ │ │ └── trade.cpp │ │ │ ├── exchange │ │ │ │ └── exchange.cpp │ │ │ ├── instrument │ │ │ │ └── instrument.cpp │ │ │ ├── order_book │ │ │ │ ├── collector.cpp │ │ │ │ ├── order_book.cpp │ │ │ │ └── price_level.cpp │ │ │ └── position │ │ │ │ ├── account.cpp │ │ │ │ ├── cash.cpp │ │ │ │ └── position.cpp │ │ └── python │ │ │ └── binding.cpp │ └── third │ │ ├── date │ │ ├── chrono_io.h │ │ ├── date.h │ │ ├── ios.h │ │ ├── islamic.h │ │ ├── iso_week.h │ │ ├── julian.h │ │ ├── ptz.h │ │ ├── solar_hijri.h │ │ ├── tz.h │ │ └── tz_private.h │ │ ├── nlohmann_json │ │ └── nlohmann │ │ │ ├── adl_serializer.hpp │ │ │ ├── byte_container_with_subtype.hpp │ │ │ ├── detail │ │ │ ├── abi_macros.hpp │ │ │ ├── conversions │ │ │ │ ├── from_json.hpp │ │ │ │ ├── to_chars.hpp │ │ │ │ └── to_json.hpp │ │ │ ├── exceptions.hpp │ │ │ ├── hash.hpp │ │ │ ├── input │ │ │ │ ├── binary_reader.hpp │ │ │ │ ├── input_adapters.hpp │ │ │ │ ├── json_sax.hpp │ │ │ │ ├── lexer.hpp │ │ │ │ ├── parser.hpp │ │ │ │ └── position_t.hpp │ │ │ ├── iterators │ │ │ │ ├── internal_iterator.hpp │ │ │ │ ├── iter_impl.hpp │ │ │ │ ├── iteration_proxy.hpp │ │ │ │ ├── iterator_traits.hpp │ │ │ │ ├── json_reverse_iterator.hpp │ │ │ │ └── primitive_iterator.hpp │ │ │ ├── json_pointer.hpp │ │ │ ├── json_ref.hpp │ │ │ ├── macro_scope.hpp │ │ │ ├── macro_unscope.hpp │ │ │ ├── meta │ │ │ │ ├── call_std │ │ │ │ │ ├── begin.hpp │ │ │ │ │ └── end.hpp │ │ │ │ ├── cpp_future.hpp │ │ │ │ ├── detected.hpp │ │ │ │ ├── identity_tag.hpp │ │ │ │ ├── is_sax.hpp │ │ │ │ ├── std_fs.hpp │ │ │ │ ├── type_traits.hpp │ │ │ │ └── void_t.hpp │ │ │ ├── output │ │ │ │ ├── binary_writer.hpp │ │ │ │ ├── output_adapters.hpp │ │ │ │ └── serializer.hpp │ │ │ ├── string_concat.hpp │ │ │ ├── string_escape.hpp │ │ │ └── value_t.hpp │ │ │ ├── json.hpp │ │ │ ├── json_fwd.hpp │ │ │ ├── ordered_map.hpp │ │ │ └── thirdparty │ │ │ └── hedley │ │ │ ├── hedley.hpp │ │ │ └── hedley_undef.hpp │ │ ├── pybind11 │ │ └── pybind11 │ │ │ ├── attr.h │ │ │ ├── buffer_info.h │ │ │ ├── cast.h │ │ │ ├── chrono.h │ │ │ ├── common.h │ │ │ ├── complex.h │ │ │ ├── detail │ │ │ ├── class.h │ │ │ ├── common.h │ │ │ ├── descr.h │ │ │ ├── init.h │ │ │ ├── internals.h │ │ │ ├── type_caster_base.h │ │ │ └── typeid.h │ │ │ ├── eigen.h │ │ │ ├── eigen │ │ │ ├── matrix.h │ │ │ └── tensor.h │ │ │ ├── embed.h │ │ │ ├── eval.h │ │ │ ├── functional.h │ │ │ ├── gil.h │ │ │ ├── iostream.h │ │ │ ├── numpy.h │ │ │ ├── operators.h │ │ │ ├── options.h │ │ │ ├── pybind11.h │ │ │ ├── pytypes.h │ │ │ ├── stl.h │ │ │ ├── stl │ │ │ └── filesystem.h │ │ │ └── stl_bind.h │ │ └── pybind11_json │ │ └── pybind11_json │ │ └── pybind11_json.hpp ├── engine │ ├── __init__.py │ ├── dispatch │ │ ├── __init__.py │ │ ├── base.py │ │ ├── execution │ │ │ ├── __init__.py │ │ │ └── execution.py │ │ ├── manager.py │ │ ├── order_entry.py │ │ ├── periodic.py │ │ ├── portfolio │ │ │ ├── __init__.py │ │ │ ├── manager.py │ │ │ ├── mixin.py │ │ │ └── portfolio.py │ │ ├── risk │ │ │ ├── __init__.py │ │ │ ├── mixin.py │ │ │ └── risk.py │ │ └── utils.py │ └── engine.py ├── exchange │ ├── __init__.py │ ├── base │ │ ├── __init__.py │ │ ├── market_data.py │ │ └── order_entry.py │ ├── crypto │ │ ├── __init__.py │ │ └── coinbase │ │ │ ├── __init__.py │ │ │ ├── client.py │ │ │ └── coinbase.py │ ├── exchange.py │ ├── generic │ │ ├── __init__.py │ │ ├── csv.py │ │ └── kafka.py │ ├── public │ │ ├── __init__.py │ │ ├── ib │ │ │ ├── __init__.py │ │ │ ├── ib.py │ │ │ ├── spread.py │ │ │ └── utils.py │ │ ├── iex.py │ │ └── tda.py │ ├── synthetic │ │ ├── __init__.py │ │ ├── __main__.py │ │ └── server.py │ └── test │ │ ├── __init__.py │ │ └── harness.py ├── strategy │ ├── __init__.py │ ├── calculations.py │ ├── portfolio.py │ ├── risk.py │ ├── sample │ │ ├── __init__.py │ │ ├── coinbase │ │ │ ├── __init__.py │ │ │ ├── buy_and_hold.py │ │ │ └── readonly.py │ │ ├── csv │ │ │ ├── __init__.py │ │ │ ├── data │ │ │ │ └── aapl.csv │ │ │ ├── readonly.py │ │ │ ├── readonly_periodic.py │ │ │ └── received.py │ │ ├── ib │ │ │ ├── __init__.py │ │ │ ├── buy_and_hold.py │ │ │ └── readonly.py │ │ ├── iex │ │ │ ├── __init__.py │ │ │ ├── buy_and_hold.py │ │ │ ├── golden_death.py │ │ │ ├── momentum.py │ │ │ └── readonly.py │ │ ├── readonly.py │ │ └── sell_plus_percent.py │ ├── strategy.py │ └── utils.py ├── tests │ ├── __init__.py │ ├── config │ │ ├── __init__.py │ │ └── test_enums.py │ ├── core │ │ ├── __init__.py │ │ ├── instrument │ │ │ ├── __init__.py │ │ │ ├── test_calendar.py │ │ │ └── test_instrument.py │ │ ├── models │ │ │ ├── __init__.py │ │ │ ├── test_order.py │ │ │ └── test_trade.py │ │ └── order_book │ │ │ ├── __init__.py │ │ │ ├── helpers.py │ │ │ ├── test_collector.py │ │ │ ├── test_order_book.py │ │ │ ├── test_order_flags.py │ │ │ ├── test_order_types.py │ │ │ ├── test_price_level.py │ │ │ └── test_utils.py │ ├── engine │ │ ├── __init__.py │ │ └── test_periodic.py │ ├── exchange │ │ ├── __init__.py │ │ ├── public │ │ │ ├── __init__.py │ │ │ └── test_ib_spread_reconstitute.py │ │ └── test_ib_race.py │ ├── strategy │ │ ├── __init__.py │ │ ├── _aat_BACKTEST_test │ │ │ ├── MomentumStrategy-0.portfolio.active_by_inst.json │ │ │ ├── MomentumStrategy-0.portfolio.active_by_strat.json │ │ │ ├── MomentumStrategy-0.portfolio.prices.json │ │ │ └── MomentumStrategy-0.portfolio.trades.json │ │ ├── test_offline_calculations.py │ │ └── test_strategies │ │ │ ├── __init__.py │ │ │ └── test_cancel_all.py │ └── test_common.py └── ui │ ├── __init__.py │ ├── application.py │ └── handlers │ └── __init__.py ├── config └── synthetic.cfg ├── cpplint.cfg ├── docs ├── Makefile ├── api.md ├── conf.py ├── doxygen.conf ├── img │ ├── icon.png │ ├── nem.png │ ├── orderbook.gif │ ├── orderbook2.gif │ ├── rethist.png │ └── tearsheet.png └── make.bat ├── pyproject.toml ├── setup.cfg └── setup.py /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: WebKit 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: false 6 | AlignConsecutiveAssignments: false 7 | # AlignConsecutiveMacros: true 8 | AlignEscapedNewlinesLeft: false 9 | AlignOperands: false 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: true 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BreakBeforeBinaryOperators: All 23 | BreakBeforeBraces: Attach 24 | BreakBeforeTernaryOperators: true 25 | BreakConstructorInitializersBeforeComma: true 26 | ColumnLimit: 120 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 28 | ConstructorInitializerIndentWidth: 2 29 | ContinuationIndentWidth: 2 30 | Cpp11BracedListStyle: true 31 | DerivePointerAlignment: false 32 | DisableFormat: false 33 | ExperimentalAutoDetectBinPacking: false 34 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 35 | IndentCaseLabels: true 36 | IndentWidth: 2 37 | IndentWrappedFunctionNames: false 38 | KeepEmptyLinesAtTheStartOfBlocks: true 39 | MacroBlockBegin: '' 40 | MacroBlockEnd: '' 41 | MaxEmptyLinesToKeep: 1 42 | NamespaceIndentation: Inner 43 | ObjCBlockIndentWidth: 2 44 | ObjCSpaceAfterProperty: true 45 | ObjCSpaceBeforeProtocolList: true 46 | PenaltyBreakBeforeFirstCallParameter: 19 47 | PenaltyBreakComment: 300 48 | PenaltyBreakFirstLessLess: 120 49 | PenaltyBreakString: 1000 50 | PenaltyExcessCharacter: 1000000 51 | PenaltyReturnTypeOnItsOwnLine: 60 52 | PointerAlignment: Left 53 | SpaceAfterCStyleCast: false 54 | SpaceBeforeAssignmentOperators: true 55 | SpaceBeforeParens: ControlStatements 56 | SpaceInEmptyParentheses: false 57 | SpacesBeforeTrailingComments: 2 58 | SpacesInAngles: false 59 | SpacesInContainerLiterals: true 60 | SpacesInCStyleCastParentheses: false 61 | SpacesInParentheses: false 62 | SpacesInSquareBrackets: false 63 | Standard: Cpp11 64 | TabWidth: 2 65 | UseTab: Never 66 | SortIncludes: false 67 | ... 68 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/img/orderbook2.gif filter=lfs diff=lfs merge=lfs -text 2 | aat/cpp/third/* linguist-vendored 3 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at t.paine154@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Status 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | build: 16 | runs-on: ${{ matrix.os }} 17 | 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest, macos-latest] 21 | python-version: [3.9] 22 | node-version: [14.x] 23 | event-name: [push] 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - name: Set up Python ${{ matrix.python-version }} 29 | uses: actions/setup-python@v5 30 | with: 31 | python-version: ${{ matrix.python-version }} 32 | 33 | - name: Install system dependencies 34 | run: | 35 | sudo apt-get install libboost-dev 36 | wget https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz && tar xfz v2.5.0.tar.gz && cd pybind11-2.5.0 && mkdir build && cd build && cmake .. -DPYBIND11_TEST=OFF && sudo make -j4 install 37 | if: ${{ matrix.os == 'ubuntu-latest' }} 38 | 39 | - name: Install system dependencies 40 | run: | 41 | brew install boost cmake pybind11 42 | if: ${{ matrix.os == 'macos-latest' }} 43 | 44 | - name: Install dependencies 45 | run: | 46 | python -m pip install -U cpplint numpy pip pyarrow pyEX setuptools tqdm twine wheel 47 | python -m pip install -e .[dev] 48 | 49 | - name: Lint Python 50 | run: | 51 | make lintpy 52 | 53 | - name: Lint C++ 54 | run: | 55 | make lintcpp 56 | 57 | - name: Type Annotate 58 | run: | 59 | make annotate 60 | 61 | - name: Test 62 | run: | 63 | make tests 64 | if: ${{ github.event_name == matrix.event-name || matrix.os == 'ubuntu-latest' }} 65 | 66 | - name: Test C++ 67 | run: | 68 | make testpycpp 69 | if: ${{ github.event_name == matrix.event-name || matrix.os == 'ubuntu-latest' }} 70 | 71 | - name: Live tests 72 | run: | 73 | make testruns 74 | if: ${{ github.event_name == matrix.event-name || matrix.os == 'ubuntu-latest' }} 75 | 76 | - name: Twine check 77 | run: | 78 | make dist 79 | 80 | - name: Upload test results 81 | uses: actions/upload-artifact@v4 82 | with: 83 | name: pytest-results-${{ matrix.os }}-${{ matrix.python-version }} 84 | path: python_junit.xml 85 | if: ${{ always() }} 86 | 87 | - name: Upload coverage 88 | uses: codecov/codecov-action@v5 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | *.dll 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .coverage.* 40 | .cache 41 | nosetests.xml 42 | coverage.xml 43 | *,cover 44 | cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | *_keys 60 | horizon 61 | 62 | *.DS_Store 63 | js/node_modules/ 64 | js/build/ 65 | aat/ui/assets/static/ 66 | notes.txt 67 | ref 68 | coinbase.sh 69 | coinbase_sandbox.sh 70 | keys 71 | old 72 | 73 | .ipynb_checkpoints 74 | *.pkl 75 | notebooks/BTC_* 76 | .idea 77 | 78 | custom_strategies 79 | 80 | node_modules 81 | package-lock.json 82 | yarn.lock 83 | logo.ai 84 | logo.png 85 | coverage 86 | experiments 87 | aat_test 88 | docs/api 89 | docs/index.md 90 | tornado_sqlalchemy_login 91 | aat.db 92 | python_junit.xml 93 | .mypy_cache 94 | .vscode 95 | private_config 96 | _aat_BACKTEST* 97 | 98 | venv/ 99 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in contributing to aat! 4 | 5 | 6 | ## Reporting bugs, feature requests, etc. 7 | 8 | To report bugs, request new features or similar, please open an issue on the Github 9 | repository. 10 | 11 | A good bug report includes: 12 | 13 | - Expected behavior 14 | - Actual behavior 15 | - Steps to reproduce (preferably as minimal as possible) 16 | 17 | ## Minor changes, typos etc. 18 | 19 | Minor changes can be contributed by navigating to the relevant files on the Github repository, 20 | and clicking the "edit file" icon. By following the instructions on the page you should be able to 21 | create a pull-request proposing your changes. A repository maintainer will then review your changes, 22 | and either merge them, propose some modifications to your changes, or reject them (with a reason for 23 | the rejection). 24 | 25 | ## Setting up a development environment 26 | 27 | If you want to help resolve an issue by making some changes that are larger than that covered by the above paragraph, it is recommended that you: 28 | 29 | - Fork the repository on Github 30 | - Clone your fork to your computer 31 | - Run the following commands inside the cloned repository: 32 | - `pip install -e .[dev]` - This will install the Python package in development 33 | mode. 34 | - Validate the install by running the tests: 35 | - `py.test` - This command will run the Python tests. 36 | - `flake8 aat/` - This command will run the Python linters. 37 | 38 | Once you have such a development setup, you should: 39 | 40 | - Make the changes you consider necessary 41 | - Run the tests to ensure that your changes does not break anything 42 | - Run the linters to ensure that your changes does not break anything 43 | - Run mypy if necessary to alert for problems with type annotations 44 | - If you add new code, preferably write one or more tests for checking that your code works as expected. 45 | - Commit your changes and publish the branch to your github repo. 46 | - Open a pull-request (PR) back to the main repo on Github. 47 | 48 | ## Style 49 | ### Docstrings 50 | Docstrings are [google format](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). 51 | 52 | 53 | ### Python 54 | Python code is formatted with [black](https://github.com/psf/black) and [flake8](https://github.com/PyCQA/flake8) and annotated for typings with [mypy](https://github.com/python/mypy). 55 | 56 | #### Misc 57 | - Public methods should be `camelCase` 58 | - Public attributes should be `camelCase` 59 | - Private methods should be `snake_case` 60 | - Private attributes should be `snake_case` 61 | - JSON import/export should be `snake_case` 62 | 63 | ### JS 64 | JS Code is writted in `typescript` and formatted with `eslint` and `prettier`. 65 | 66 | ### C++ 67 | C++ Code is formateed with [cpplint](https://github.com/cpplint/cpplint) and [clang-format](https://clang.llvm.org/docs/ClangFormat.html). 68 | 69 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft aat 2 | include LICENSE 3 | include README.md 4 | 5 | include setup.cfg 6 | include pyproject.toml 7 | include .bumpversion.cfg 8 | include Makefile 9 | 10 | include aat/*.so 11 | include aat/*.dll 12 | include aat/*.dylib 13 | include *.csv 14 | 15 | graft aat/tests 16 | 17 | # C++ build 18 | graft aat/cpp 19 | 20 | # Patterns to exclude from any directory 21 | global-exclude *~ 22 | global-exclude *.pyc 23 | global-exclude *.pyo 24 | global-exclude .git 25 | global-exclude .ipynb_checkpoints 26 | global-exclude .DS_Store 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYTHON=python 2 | CONFIG=./config/synthetic.cfg 3 | 4 | 5 | run: ## Clean and make target, run target 6 | $(PYTHON) -m aat --config $(CONFIG) 7 | 8 | runcpp: build ## Clean and make target, run target 9 | AAT_USE_CPP=1 $(PYTHON) -m aat --config $(CONFIG) 10 | 11 | rundebug: debug ## Clean and make debug target, run target 12 | $(PYTHON) -m aat --config $(CONFIG) 13 | 14 | stratres: ## View strategy results offline 15 | $(PYTHON) -m aat.strategy.calculations 16 | 17 | buildextf: ## build the package extensions 18 | $(PYTHON) setup.py build_ext -j8 --inplace -f 19 | 20 | buildext: ## build the package extensions 21 | $(PYTHON) setup.py build_ext -j8 --inplace 22 | 23 | build: buildext ## build the package 24 | $(PYTHON) setup.py build 25 | 26 | debug: ## build debug build of the package 27 | DEBUG=1 $(PYTHON) setup.py build 28 | 29 | install: ## install the package 30 | $(PYTHON) -m pip install . 31 | 32 | tests: build testpy ## Make unit tests 33 | 34 | testpy: ## Make unit tests 35 | $(PYTHON) -m pytest -vvv ./aat/tests --cov=aat --junitxml=python_junit.xml --cov-report=xml --cov-branch 36 | 37 | testpycpp: ## Make unit tests 38 | # AAT_USE_CPP=1 $(PYTHON) -m pytest -vvv ./aat/tests --cov=aat --junitxml=python_junit.xml --cov-report=xml --cov-branch --capture=no 39 | AAT_USE_CPP=1 $(PYTHON) -m pytest -vs ./aat/tests 40 | 41 | testjs: ## Make js tests 42 | cd js; yarn test 43 | 44 | testruns: testrunscsv testrunsiex testrunsother ## Run a few examples as a live end-to-end test 45 | 46 | testrunsother: 47 | $(PYTHON) -m aat.exchange.test.harness 48 | $(PYTHON) -m aat.tests.exchange.test_ib_race 49 | 50 | testrunscsv: 51 | $(PYTHON) -m aat.strategy.sample.csv.readonly 52 | $(PYTHON) -m aat.strategy.sample.csv.readonly_periodic 53 | $(PYTHON) -m aat.strategy.sample.csv.received 54 | 55 | testrunsiex: 56 | $(PYTHON) -m aat.strategy.sample.iex.readonly 57 | TESTING=1 $(PYTHON) -m aat.strategy.sample.iex.buy_and_hold 58 | TESTING=1 $(PYTHON) -m aat.strategy.sample.iex.momentum 59 | TESTING=1 $(PYTHON) -m aat.strategy.sample.iex.golden_death 60 | 61 | lint: lintpy lintcpp ## run all linters 62 | 63 | lintpy: ## run python linter 64 | $(PYTHON) -m flake8 aat setup.py 65 | 66 | lintcpp: ## run cpp linter 67 | cpplint --linelength=120 --recursive aat/cpp/{src,include} 68 | 69 | fix: fixpy fixcpp ## run all fixers 70 | 71 | fixpy: ## run autopep8 fix 72 | $(PYTHON) -m black aat/ setup.py 73 | 74 | fixcpp: ## run clang-format 75 | clang-format -i -style=file `find ./aat/cpp/{src,include} -name "*.*pp"` 76 | 77 | annotate: ## MyPy type annotation check 78 | $(PYTHON) -m mypy aat 79 | 80 | type_ignore: ## Count type ignores 81 | grep -rin "type: ignore" ./aat | wc -l 82 | 83 | type_ignore_list: ## List all type ignores 84 | grep -rin "type: ignore" ./aat 85 | 86 | docs: ## Build the sphinx docs 87 | make -C docs html 88 | open ./docs/_build/html/index.html 89 | 90 | dist: ## create dists 91 | rm -rf dist build 92 | python setup.py sdist bdist_wheel 93 | python -m twine check dist/* 94 | 95 | publish: dist ## dist to pypi and npm 96 | python -m twine upload dist/* --skip-existing 97 | 98 | clean: ## clean the repository 99 | find . -name "__pycache__" | xargs rm -rf 100 | find . -name "*.pyc" | xargs rm -rf 101 | rm -rf .coverage coverage cover htmlcov logs build dist *.egg-info coverage.xml .mypy_cache 102 | find . -name "*.so" | xargs rm -rf 103 | make -C ./docs clean 104 | rm -rf _aat_BACKTEST_* 105 | 106 | # Thanks to Francoise at marmelab.com for this 107 | .DEFAULT_GOAL := help 108 | help: 109 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 110 | 111 | print-%: 112 | @echo '$*=$($*)' 113 | 114 | .PHONY: run buildext build install tests lint fix docs dist clean help fixcpp 115 | 116 | -------------------------------------------------------------------------------- /aat/__init__.py: -------------------------------------------------------------------------------- 1 | from .common import AATException # noqa: F401 2 | from .config import * # noqa: F401, F403 3 | from .core import ( # noqa: F401 4 | EventHandler, 5 | Instrument, 6 | ExchangeType, 7 | Data, 8 | Event, 9 | Order, 10 | Account, 11 | Position, 12 | Trade, 13 | OrderBook, 14 | ) 15 | from .engine import TradingEngine # noqa: F401 16 | from .strategy import * # noqa: F401, F403 17 | 18 | __version__ = "0.1.0" 19 | -------------------------------------------------------------------------------- /aat/__main__.py: -------------------------------------------------------------------------------- 1 | from .config import parseConfig 2 | from .engine import TradingEngine 3 | 4 | 5 | def main() -> None: 6 | # Parse the command line config 7 | config = parseConfig() 8 | 9 | # Instantiate trading engine 10 | # 11 | # The engine is responsible for managing the different components, 12 | # including the strategies, the bank/risk engine, and the 13 | # exchange/backtest engine. 14 | engine = TradingEngine(**config) 15 | 16 | # Run the live trading engine 17 | engine.start() 18 | 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /aat/common.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, List 2 | 3 | import os 4 | import itertools 5 | import functools 6 | import pandas as pd # type: ignore 7 | 8 | 9 | class AATException(Exception): 10 | pass 11 | 12 | 13 | @functools.lru_cache() 14 | def _in_cpp() -> bool: 15 | _cpp = os.environ.get("AAT_USE_CPP", "").lower() in ("1", "on") 16 | 17 | try: 18 | from aat.binding import ( # type: ignore # noqa: F401 19 | SideCpp, 20 | EventTypeCpp, 21 | DataTypeCpp, 22 | InstrumentTypeCpp, 23 | OrderTypeCpp, 24 | OrderFlagCpp, 25 | OrderBookCpp, 26 | ExchangeTypeCpp, 27 | InstrumentCpp, 28 | DataCpp, 29 | EventCpp, 30 | OrderCpp, 31 | TradeCpp, 32 | ) 33 | except ImportError: 34 | if _cpp: 35 | # raise if being told to use c++ 36 | raise 37 | return False 38 | 39 | return _cpp 40 | 41 | 42 | def id_generator() -> Callable[[], int]: 43 | __c = itertools.count() 44 | 45 | def _gen_id() -> int: 46 | return next(__c) 47 | 48 | return _gen_id 49 | 50 | 51 | def _merge(lst1: List, lst2: List, sum: bool = True) -> List: 52 | """merge two lists of (val, datetime) and accumulate""" 53 | df1 = pd.DataFrame(lst1, columns=("val1", "date1")) 54 | df1.set_index("date1", inplace=True) 55 | # df1.drop_duplicates(inplace=True) 56 | 57 | df2 = pd.DataFrame(lst2, columns=("val2", "date2")) 58 | df2.set_index("date2", inplace=True) 59 | # df2.drop_duplicates(inplace=True) 60 | 61 | df = df1.join(df2, how="outer") 62 | 63 | # df = pd.concat([df1, df2], axis=1) 64 | df.fillna(method="ffill", inplace=True) 65 | df.fillna(0.0, inplace=True) 66 | 67 | if sum: 68 | df = df.sum(axis=1) 69 | else: 70 | df = df.mean(axis=1) 71 | 72 | df = df.reset_index().values.tolist() 73 | 74 | return [(b, a.to_pydatetime()) for a, b in df] 75 | -------------------------------------------------------------------------------- /aat/config/__init__.py: -------------------------------------------------------------------------------- 1 | from ..common import _in_cpp 2 | from .parser import parseConfig, getStrategies, getExchanges # noqa: F401 3 | 4 | if _in_cpp(): 5 | from ..binding import ( # type: ignore # noqa: F401 6 | TradingTypeCpp as TradingType, 7 | SideCpp as Side, 8 | InstrumentTypeCpp as InstrumentType, 9 | EventTypeCpp as EventType, 10 | DataTypeCpp as DataType, 11 | OrderTypeCpp as OrderType, 12 | OrderFlagCpp as OrderFlag, 13 | ExitRoutineCpp as ExitRoutine, 14 | ) 15 | else: 16 | from .enums import ( # noqa: F401 17 | TradingType, 18 | Side, 19 | InstrumentType, 20 | EventType, 21 | DataType, 22 | OrderFlag, 23 | OrderType, 24 | OptionType, 25 | ExitRoutine, 26 | ) 27 | -------------------------------------------------------------------------------- /aat/config/enums.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from enum import Enum 3 | 4 | 5 | class BaseEnum(Enum): 6 | def __str__(self) -> str: 7 | return f"{self.value}" 8 | 9 | @classmethod 10 | def members(cls) -> List[str]: 11 | return list(cls.__members__.keys()) 12 | 13 | 14 | class TradingType(BaseEnum): 15 | LIVE = "LIVE" 16 | SIMULATION = "SIMULATION" 17 | SANDBOX = "SANDBOX" 18 | BACKTEST = "BACKTEST" 19 | 20 | 21 | class Side(BaseEnum): 22 | BUY = "BUY" 23 | SELL = "SELL" 24 | 25 | 26 | class OptionType(BaseEnum): 27 | CALL = "CALL" 28 | PUT = "PUT" 29 | 30 | 31 | class EventType(BaseEnum): 32 | # Heartbeat events 33 | HEARTBEAT = "HEARTBEAT" 34 | 35 | # Trade events 36 | TRADE = "TRADE" 37 | 38 | # Order events 39 | OPEN = "OPEN" 40 | CANCEL = "CANCEL" 41 | CHANGE = "CHANGE" 42 | FILL = "FILL" 43 | 44 | # Other data events 45 | DATA = "DATA" 46 | 47 | # System events 48 | HALT = "HALT" 49 | CONTINUE = "CONTINUE" 50 | 51 | # Engine events 52 | ERROR = "ERROR" 53 | START = "START" 54 | EXIT = "EXIT" 55 | 56 | # Order Events 57 | BOUGHT = "BOUGHT" 58 | SOLD = "SOLD" 59 | RECEIVED = "RECEIVED" 60 | REJECTED = "REJECTED" 61 | CANCELED = "CANCELED" 62 | 63 | 64 | class DataType(BaseEnum): 65 | DATA = "DATA" 66 | ERROR = "ERROR" 67 | 68 | ORDER = "ORDER" 69 | TRADE = "TRADE" 70 | 71 | 72 | class InstrumentType(BaseEnum): 73 | OTHER = "OTHER" 74 | 75 | EQUITY = "EQUITY" 76 | 77 | # TODO ETF separate? 78 | 79 | BOND = "BOND" 80 | 81 | OPTION = "OPTION" 82 | 83 | FUTURE = "FUTURE" 84 | 85 | PAIR = "PAIR" 86 | 87 | SPREAD = "SPREAD" 88 | 89 | FUTURESOPTION = "FUTURESOPTION" 90 | 91 | MUTUALFUND = "MUTUALFUND" 92 | 93 | COMMODITY = "COMMODITY" 94 | 95 | # TODO Warrant? 96 | 97 | # Non-tradeable 98 | CURRENCY = "CURRENCY" 99 | INDEX = "INDEX" 100 | 101 | 102 | class OrderType(BaseEnum): 103 | # Order Types 104 | LIMIT = "LIMIT" 105 | MARKET = "MARKET" 106 | STOP = "STOP" 107 | 108 | 109 | class OrderFlag(BaseEnum): 110 | # Order Flag 111 | NONE = "NONE" 112 | FILL_OR_KILL = "FILL_OR_KILL" 113 | ALL_OR_NONE = "ALL_OR_NONE" 114 | IMMEDIATE_OR_CANCEL = "IMMEDIATE_OR_CANCEL" 115 | 116 | 117 | class ExitRoutine(BaseEnum): 118 | NONE = "NONE" 119 | CLOSE_ALL = "CLOSE_ALL" 120 | -------------------------------------------------------------------------------- /aat/core/__init__.py: -------------------------------------------------------------------------------- 1 | from .data import Data, Error, Event, Order, Trade 2 | from .exchange import ExchangeType 3 | 4 | # from .execution import OrderManager 5 | from .handler import EventHandler, PrintHandler 6 | from .instrument import Instrument, TradingDay 7 | from .order_book import OrderBook 8 | from .position import Account, CashPosition, Position 9 | 10 | # from .portfolio import Portfolio, PortfolioManager 11 | # from .risk import RiskManager 12 | from .table import TableHandler 13 | 14 | try: 15 | from ..binding import ( 16 | AccountCpp, 17 | DataCpp, 18 | EventCpp, 19 | InstrumentCpp, 20 | OrderBookCpp, 21 | OrderCpp, 22 | PositionCpp, 23 | TradeCpp, 24 | ) 25 | except ImportError: 26 | pass 27 | -------------------------------------------------------------------------------- /aat/core/data/__init__.py: -------------------------------------------------------------------------------- 1 | from .data import Data # noqa: F401 2 | from .error import Error # noqa: F401 3 | from .event import Event # noqa: F401 4 | from .order import Order # noqa: F401 5 | from .trade import Trade # noqa: F401 6 | -------------------------------------------------------------------------------- /aat/core/data/cpp.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from collections import deque 3 | from datetime import datetime 4 | from typing import TYPE_CHECKING, Any, List, Optional 5 | 6 | from aat.common import _in_cpp 7 | from aat.config import EventType, OrderFlag, OrderType, Side 8 | 9 | from ..exchange import ExchangeType 10 | from ..instrument import Instrument 11 | 12 | if TYPE_CHECKING: 13 | from .order import Order 14 | 15 | 16 | try: 17 | from aat.binding import DataCpp, EventCpp, OrderCpp, TradeCpp # type: ignore 18 | 19 | _CPP = _in_cpp() 20 | 21 | except ImportError: 22 | logging.critical("Could not load C++ extension") 23 | DataCpp, EventCpp, OrderCpp, TradeCpp = object, object, object, object 24 | _CPP = False 25 | 26 | 27 | def _make_cpp_data( 28 | id: str, 29 | timestamp: datetime, 30 | instrument: Instrument, 31 | exchange: ExchangeType, 32 | data: Any, 33 | ) -> DataCpp: 34 | """helper method to ensure all arguments are setup""" 35 | return DataCpp(id, timestamp, instrument, exchange, data) 36 | 37 | 38 | def _make_cpp_event(type: EventType, target: Any) -> EventCpp: 39 | """helper method to ensure all arguments are setup""" 40 | return EventCpp(type, target) 41 | 42 | 43 | def _make_cpp_order( 44 | volume: float, 45 | price: float, 46 | side: Side, 47 | instrument: Instrument, 48 | exchange: ExchangeType = ExchangeType(""), 49 | notional: float = 0.0, 50 | order_type: OrderType = OrderType.MARKET, 51 | flag: OrderFlag = OrderFlag.NONE, 52 | stop_target: Optional["Order"] = None, 53 | id: Optional[str] = None, 54 | timestamp: Optional[datetime] = None, 55 | ) -> OrderCpp: 56 | """helper method to ensure all arguments are setup""" 57 | return OrderCpp( 58 | id or "0", 59 | timestamp or datetime.now(), 60 | volume, 61 | price, 62 | side, 63 | instrument, 64 | exchange, 65 | notional, 66 | order_type, 67 | flag, 68 | stop_target, 69 | ) 70 | 71 | 72 | def _make_cpp_trade( 73 | id: str, 74 | timestamp: datetime, 75 | maker_orders: Optional[List["Order"]] = None, 76 | taker_order: Optional["Order"] = None, 77 | ) -> TradeCpp: 78 | """helper method to ensure all arguments are setup""" 79 | return TradeCpp(id, timestamp, maker_orders or deque(), taker_order) 80 | -------------------------------------------------------------------------------- /aat/core/data/data.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Mapping, Union, Type, Optional, Any, cast 3 | 4 | from .cpp import _CPP, _make_cpp_data 5 | from ..exchange import ExchangeType 6 | from ..instrument import Instrument 7 | from ...common import id_generator 8 | from ...config import DataType 9 | 10 | _ID_GENERATOR = id_generator() 11 | 12 | 13 | class Data(object): 14 | __slots__ = [ 15 | "__id", 16 | "__timestamp", 17 | "__type", 18 | "__instrument", 19 | "__exchange", 20 | "__data", 21 | ] 22 | 23 | def __new__(cls, *args, **kwargs): # type: ignore 24 | if _CPP: 25 | return _make_cpp_data(*args, **kwargs) 26 | return super(Data, cls).__new__(cls) 27 | 28 | def __init__( 29 | self, 30 | instrument: Optional[Instrument] = None, 31 | exchange: ExchangeType = ExchangeType(""), 32 | data: dict = {}, 33 | **kwargs: Union[int, datetime], 34 | ) -> None: 35 | self.__id: int = cast(int, kwargs.get("id", _ID_GENERATOR())) 36 | self.__timestamp: datetime = cast( 37 | datetime, kwargs.get("timestamp", datetime.now()) 38 | ) 39 | 40 | assert instrument is None or isinstance(instrument, Instrument) 41 | assert isinstance(exchange, ExchangeType) 42 | self.__type = DataType.DATA 43 | self.__instrument = instrument 44 | self.__exchange = exchange 45 | self.__data = data 46 | 47 | # ******** # 48 | # Readonly # 49 | # ******** # 50 | @property 51 | def id(self) -> int: 52 | return self.__id 53 | 54 | @property 55 | def timestamp(self) -> datetime: 56 | return self.__timestamp 57 | 58 | @property 59 | def type(self) -> DataType: 60 | return self.__type 61 | 62 | @property 63 | def instrument(self) -> Optional[Instrument]: 64 | return self.__instrument 65 | 66 | @property 67 | def exchange(self) -> ExchangeType: 68 | return self.__exchange 69 | 70 | @property 71 | def data(self) -> Any: 72 | return self.__data 73 | 74 | def __repr__(self) -> str: 75 | return f"Data( id={self.id}, timestamp={self.timestamp}, instrument={self.instrument}, exchange={self.exchange})" 76 | 77 | def __eq__(self, other: object) -> bool: 78 | if not isinstance(other, Data): 79 | raise TypeError() 80 | return self.id == other.id 81 | 82 | def json(self, flat: bool = False) -> Mapping[str, Union[str, int, float]]: 83 | if flat: 84 | # TODO 85 | raise NotImplementedError() 86 | 87 | return { 88 | "id": self.id, 89 | "timestamp": self.timestamp.timestamp(), 90 | "type": self.type.value, 91 | "instrument": str(self.instrument), 92 | "exchange": str(self.exchange), 93 | } 94 | 95 | @staticmethod 96 | def schema() -> Mapping[str, Type]: 97 | return { 98 | "id": int, 99 | "timestamp": int, 100 | "type": str, 101 | "instrument": str, 102 | "exchange": str, 103 | } 104 | -------------------------------------------------------------------------------- /aat/core/data/error.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from traceback import format_exception 3 | from typing import Any, Callable, Dict, Union 4 | from ...config import DataType 5 | 6 | 7 | class Error(object): 8 | __slots__ = [ 9 | "__target", 10 | "__exception", 11 | "__callback", 12 | "__handler", 13 | "__timestamp", 14 | "__type", 15 | ] 16 | 17 | def __init__( 18 | self, 19 | target: Any, 20 | exception: BaseException, 21 | callback: Callable, 22 | handler: Callable, 23 | **kwargs: Any, 24 | ) -> None: 25 | self.__timestamp = kwargs.get("timestamp", datetime.now()) 26 | self.__type = DataType.ERROR 27 | self.__target = target 28 | self.__exception = exception 29 | self.__callback = callback 30 | self.__handler = handler 31 | 32 | # ******** # 33 | # Readonly # 34 | # ******** # 35 | @property 36 | def timestamp(self) -> int: 37 | return self.__timestamp 38 | 39 | @timestamp.setter 40 | def timestamp(self, timestamp: datetime) -> None: 41 | assert isinstance(timestamp, datetime) 42 | self.__timestamp = timestamp 43 | 44 | @property 45 | def type(self) -> DataType: 46 | return self.__type 47 | 48 | @property 49 | def target(self) -> Any: 50 | return self.__target 51 | 52 | @property 53 | def exception(self) -> BaseException: 54 | return self.__exception 55 | 56 | @property 57 | def callback(self) -> Callable: 58 | return self.__callback 59 | 60 | @property 61 | def handler(self) -> Callable: 62 | return self.__handler 63 | 64 | def json(self, flat: bool = False) -> Dict[str, Union[str, int, float, dict]]: 65 | # TODO 66 | raise NotImplementedError() 67 | 68 | def __repr__(self) -> str: 69 | return f"Error( timestamp={self.timestamp}, callback={self.callback}, handler={self.handler}, exception={format_exception(type(self.exception), self.exception, self.exception.__traceback__)})" 70 | -------------------------------------------------------------------------------- /aat/core/data/event.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping, Union 2 | from .cpp import _CPP, _make_cpp_event 3 | from .data import Data 4 | from .order import Order 5 | from .trade import Trade 6 | from .error import Error 7 | from ...config import EventType 8 | 9 | 10 | class Event(object): 11 | __slots__ = ["__type", "__target"] 12 | 13 | # for convenience 14 | Types = EventType 15 | 16 | def __new__(cls, *args, **kwargs): # type: ignore 17 | if _CPP: 18 | return _make_cpp_event(*args, **kwargs) 19 | return super(Event, cls).__new__(cls) 20 | 21 | def __init__( 22 | self, type: EventType, target: Union[Data, Order, Trade, Error, None] 23 | ) -> None: 24 | self.__type = type 25 | self.__target = target 26 | 27 | # ******** # 28 | # Readonly # 29 | # ******** # 30 | @property 31 | def type(self) -> EventType: 32 | return self.__type 33 | 34 | @property 35 | def target(self) -> Union[Data, Order, Trade, Error, None]: 36 | return self.__target # type: ignore 37 | # ignore None type so typing is happy in other parts 38 | 39 | def __repr__(self) -> str: 40 | return f"Event(type={self.type}, target={self.target})" 41 | 42 | def json(self, flat: bool = False) -> Mapping[str, Union[str, int, float, dict]]: 43 | if flat: 44 | # TODO 45 | raise NotImplementedError() 46 | target = ( 47 | { 48 | "target." + k: v 49 | for k, v in ( 50 | self.target.json(flat=flat) if hasattr(self.target, "json") else {} 51 | ).items() 52 | } 53 | if self.target 54 | else {"target": ""} 55 | ) 56 | 57 | ret = {"type": self.type.value} 58 | 59 | ret.update(target) 60 | return ret 61 | -------------------------------------------------------------------------------- /aat/core/exchange/__init__.py: -------------------------------------------------------------------------------- 1 | from .exchange import ExchangeType # noqa: F401 2 | -------------------------------------------------------------------------------- /aat/core/exchange/cpp.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from aat.common import _in_cpp 4 | 5 | try: 6 | from aat.binding import ExchangeTypeCpp # type: ignore 7 | 8 | _CPP = _in_cpp() 9 | except ImportError: 10 | ExchangeTypeCpp = object 11 | _CPP = False 12 | 13 | 14 | def _make_cpp_exchangetype(*args: Any, **kwargs: Any) -> ExchangeTypeCpp: 15 | return ExchangeTypeCpp(*args, **kwargs) 16 | -------------------------------------------------------------------------------- /aat/core/exchange/db.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Dict, TYPE_CHECKING 2 | from ...config import InstrumentType 3 | 4 | if TYPE_CHECKING: 5 | # Circular import 6 | from .exchange import ExchangeType 7 | from ..instrument import Instrument 8 | 9 | 10 | class ExchangeDB(object): 11 | """exchange registration""" 12 | 13 | def __init__(self) -> None: 14 | self._name_map: Dict[str, "ExchangeType"] = {} 15 | self._map: Dict["Instrument", "ExchangeType"] = {} 16 | 17 | def add(self, exchange: "ExchangeType") -> None: 18 | if exchange.name in self._name_map: 19 | return 20 | self._name_map[exchange.name] = exchange 21 | 22 | def instruments( 23 | self, 24 | name: str = "", 25 | type: InstrumentType = InstrumentType.EQUITY, 26 | exchange: ExchangeType = ExchangeType(""), 27 | ) -> None: 28 | raise NotImplementedError() 29 | 30 | def get( 31 | self, name: str = "", instrument: Optional["Instrument"] = None 32 | ) -> "ExchangeType": 33 | if name: 34 | return self._name_map[name] 35 | raise NotImplementedError() 36 | -------------------------------------------------------------------------------- /aat/core/exchange/exchange.py: -------------------------------------------------------------------------------- 1 | from .cpp import _CPP, _make_cpp_exchangetype 2 | 3 | 4 | class ExchangeType(object): 5 | __slots__ = ["__name"] 6 | 7 | def __new__(cls, *args, **kwargs): # type: ignore 8 | if _CPP: 9 | return _make_cpp_exchangetype(*args, **kwargs) 10 | return super(ExchangeType, cls).__new__(cls) 11 | 12 | def __init__(self, name: str) -> None: 13 | assert isinstance(name, str) 14 | self.__name = name 15 | 16 | # ******** # 17 | # Readonly # 18 | # ******** # 19 | @property 20 | def name(self) -> str: 21 | return self.__name 22 | 23 | def __eq__(self, other: object) -> bool: 24 | if not isinstance(other, ExchangeType): 25 | return False 26 | return self.name == other.name 27 | 28 | def __bool__(self) -> bool: 29 | return bool(self.__name) 30 | 31 | def __hash__(self) -> int: 32 | return hash(str(self)) 33 | 34 | def json(self) -> dict: 35 | return {"name": self.name} 36 | 37 | @staticmethod 38 | def fromJson(jsn: dict) -> "ExchangeType": 39 | return ExchangeType(name=jsn["name"]) 40 | 41 | def __repr__(self) -> str: 42 | return "Exchange({})".format(self.__name) if self.__name else "No Exchange" 43 | -------------------------------------------------------------------------------- /aat/core/handler/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import EventHandler # noqa: F401 2 | from .print import PrintHandler # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/core/handler/print.py: -------------------------------------------------------------------------------- 1 | from .handler import EventHandler 2 | from ..data import Event 3 | 4 | 5 | class PrintHandler(EventHandler): 6 | ######################### 7 | # Event Handler Methods # 8 | ######################### 9 | async def onTrade(self, event: Event) -> None: 10 | """Called whenever a `Trade` event is received""" 11 | print(event) 12 | 13 | async def onOrder(self, event: Event) -> None: 14 | """Called whenever an Order `Open` event is received""" 15 | print(event) 16 | 17 | async def onData(self, event: Event) -> None: 18 | """Called whenever other data is received""" 19 | print(event) 20 | 21 | async def onHalt(self, event: Event) -> None: 22 | """Called whenever an exchange `Halt` event is received, i.e. an event to stop trading""" 23 | print(event) 24 | 25 | async def onContinue(self, event: Event) -> None: 26 | """Called whenever an exchange `Continue` event is received, i.e. an event to continue trading""" 27 | print(event) 28 | 29 | async def onError(self, event: Event) -> None: 30 | """Called whenever an internal error occurs""" 31 | print(event) 32 | 33 | async def onStart(self, event: Event) -> None: 34 | """Called once at engine initialization time""" 35 | print(event) 36 | 37 | async def onExit(self, event: Event) -> None: 38 | """Called once at engine exit time""" 39 | print(event) 40 | -------------------------------------------------------------------------------- /aat/core/instrument/__init__.py: -------------------------------------------------------------------------------- 1 | from .calendar import TradingDay # noqa: F401 2 | from .instrument import Instrument # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/core/instrument/calendar.py: -------------------------------------------------------------------------------- 1 | from datetime import time 2 | from typing import Optional, Tuple, Union, cast 3 | 4 | from aat.common import AATException 5 | from pytz import UTC 6 | 7 | 8 | class TradingDay(object): 9 | """Construct a representation of an instrument's trading day 10 | 11 | Args: 12 | open_times (Union[time, Tuple[time]]): time or tuple of times representing the time/s of market open. If missing tzinfo, will assume UTC. 13 | close_times (Union[time, Tuple[time]]): time or tuple of times representing the time/s of market close. If missing tzinfo, will assume UTC. 14 | """ 15 | 16 | __slots__ = [ 17 | "_open_times", 18 | "_close_times", 19 | ] 20 | 21 | def __init__( 22 | self, 23 | open_times: Optional[Union[time, Tuple[time, ...]]] = None, 24 | close_times: Optional[Union[time, Tuple[time, ...]]] = None, 25 | ): 26 | if open_times and not isinstance(open_times, (tuple, time)): 27 | # raise exception if wrong type 28 | raise AATException( 29 | "`open_times` must be time or tuple of times, got: {}".format( 30 | type(open_times) 31 | ) 32 | ) 33 | elif isinstance(open_times, time): 34 | # force tuple 35 | open_times = cast(Tuple[time, ...], (open_times,)) 36 | 37 | if open_times: 38 | # force tz 39 | open_times = tuple( 40 | tm if tm.tzinfo else time(tm.hour, tm.minute, tzinfo=UTC) 41 | for tm in open_times 42 | ) 43 | 44 | self._open_times: Optional[Tuple[time, ...]] = open_times 45 | 46 | if close_times and not isinstance(close_times, (tuple, time)): 47 | # raise exception if wrong type 48 | raise AATException( 49 | "`close_times` must be time or tuple of times, got: {}".format( 50 | type(close_times) 51 | ) 52 | ) 53 | elif isinstance(close_times, time): 54 | # force tuple 55 | close_times = cast(Tuple[time, ...], (close_times,)) 56 | 57 | if close_times: 58 | # force tz 59 | close_times = tuple( 60 | tm if tm.tzinfo else time(tm.hour, tm.minute, tzinfo=UTC) 61 | for tm in close_times 62 | ) 63 | 64 | self._close_times: Optional[Tuple[time, ...]] = close_times 65 | 66 | @property 67 | def open(self) -> Optional[Tuple[time, ...]]: 68 | return self._open_times 69 | 70 | @property 71 | def close(self) -> Optional[Tuple[time, ...]]: 72 | return self._close_times 73 | 74 | def __eq__(self, other: object) -> bool: 75 | if not isinstance(other, TradingDay): 76 | return False 77 | return self.open == other.open and self.close == other.close 78 | -------------------------------------------------------------------------------- /aat/core/instrument/cpp.py: -------------------------------------------------------------------------------- 1 | from typing import Any, List 2 | 3 | from aat.common import _in_cpp 4 | 5 | try: 6 | from ...binding import InstrumentCpp # type: ignore 7 | 8 | _CPP = _in_cpp() 9 | except ImportError: 10 | InstrumentCpp = object 11 | _CPP = False 12 | 13 | 14 | def _make_cpp_instrument(*args: Any, **kwargs: Any) -> InstrumentCpp: 15 | if kwargs: 16 | full_args: List[Any] = list(args) 17 | full_args.append(kwargs.get("name")) 18 | full_args.append(kwargs.get("type")) 19 | args = tuple(full_args) 20 | return InstrumentCpp(*args) 21 | -------------------------------------------------------------------------------- /aat/core/instrument/db.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional, Tuple, List, TYPE_CHECKING 2 | from ..exchange import ExchangeType 3 | from ...config import InstrumentType 4 | 5 | if TYPE_CHECKING: 6 | # Circular import 7 | from . import Instrument 8 | 9 | 10 | class InstrumentDB(object): 11 | """instrument registration""" 12 | 13 | def __init__(self) -> None: 14 | self._by_name: Dict[str, List["Instrument"]] = {} 15 | self._by_type: Dict[Tuple[str, InstrumentType], List["Instrument"]] = {} 16 | self._by_exchange: Dict[Tuple[str, ExchangeType], "Instrument"] = {} 17 | self._by_type_and_exchange: Dict[ 18 | Tuple[str, InstrumentType, ExchangeType], "Instrument" 19 | ] = {} 20 | 21 | def add(self, instrument: "Instrument") -> None: 22 | if instrument.name not in self._by_name: 23 | self._by_name[instrument.name] = [instrument] 24 | self._by_type[instrument.name, instrument.type] = [instrument] 25 | else: 26 | self._by_name[instrument.name].append(instrument) 27 | self._by_type[instrument.name, instrument.type].append(instrument) 28 | 29 | self._by_exchange[instrument.name, instrument.exchange] = instrument 30 | self._by_type_and_exchange[ 31 | instrument.name, instrument.type, instrument.exchange 32 | ] = instrument 33 | 34 | def instruments( 35 | self, 36 | name: str = "", 37 | type: InstrumentType = InstrumentType.EQUITY, 38 | exchange: Optional[ExchangeType] = ExchangeType(""), 39 | *args: Tuple, 40 | **kwargs: Dict 41 | ) -> List["Instrument"]: 42 | ret = [inst for values in self._by_name.values() for inst in values] 43 | if not name and not type and not exchange: 44 | return ret 45 | if name: 46 | ret = [r for r in ret if r.name == name] 47 | if type: 48 | ret = [r for r in ret if r.type == type] 49 | if exchange: 50 | ret = [r for r in ret if exchange in r.exchanges] 51 | return ret 52 | 53 | def get( 54 | self, 55 | name: str = "", 56 | type: InstrumentType = InstrumentType.EQUITY, 57 | exchange: Optional[ExchangeType] = ExchangeType(""), 58 | *args: Tuple, 59 | **kwargs: Dict 60 | ) -> Optional["Instrument"]: 61 | """Like `instruments` but only returns 1""" 62 | ret = [inst for values in self._by_name.values() for inst in values] 63 | if name: 64 | ret = [r for r in ret if r.name == name] 65 | if type: 66 | ret = [r for r in ret if r.type == type] 67 | if exchange: 68 | ret = [r for r in ret if exchange in r.exchanges] 69 | return ret[0] if ret else None 70 | -------------------------------------------------------------------------------- /aat/core/order_book/__init__.py: -------------------------------------------------------------------------------- 1 | from .order_book import OrderBook # noqa: F401 2 | -------------------------------------------------------------------------------- /aat/core/order_book/base.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Dict, Iterator, List, Optional, Tuple, Union 3 | 4 | from .price_level import PriceLevelRO 5 | from ..data import Order 6 | from ...config import Side 7 | 8 | 9 | class OrderBookBase(ABC): 10 | @abstractmethod 11 | def reset(self) -> None: 12 | pass 13 | 14 | @abstractmethod 15 | def add(self, order: Order) -> None: 16 | pass 17 | 18 | @abstractmethod 19 | def cancel(self, order: Order) -> None: 20 | pass 21 | 22 | @abstractmethod 23 | def change(self, order: Order) -> None: 24 | pass 25 | 26 | @abstractmethod 27 | def find(self, order: Order) -> Optional[Order]: 28 | pass 29 | 30 | @abstractmethod 31 | def topOfBook(self) -> Dict[Side, PriceLevelRO]: 32 | pass 33 | 34 | @abstractmethod 35 | def spread(self) -> float: 36 | pass 37 | 38 | @abstractmethod 39 | def level(self, level: int = 0, price: Optional[float] = None) -> Tuple: 40 | pass 41 | 42 | @abstractmethod 43 | def levels(self, levels: int = 0) -> Dict[Side, List[PriceLevelRO]]: 44 | pass 45 | 46 | @abstractmethod 47 | def bids( 48 | self, levels: int = 0 49 | ) -> Union[PriceLevelRO, List[Optional[PriceLevelRO]]]: 50 | pass 51 | 52 | @abstractmethod 53 | def asks( 54 | self, levels: int = 0 55 | ) -> Union[PriceLevelRO, List[Optional[PriceLevelRO]]]: 56 | pass 57 | 58 | @abstractmethod 59 | def __iter__(self) -> Iterator[Order]: 60 | pass 61 | -------------------------------------------------------------------------------- /aat/core/order_book/collector/__init__.py: -------------------------------------------------------------------------------- 1 | from .collector import _Collector # noqa: F401 2 | -------------------------------------------------------------------------------- /aat/core/order_book/cpp.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from aat.common import _in_cpp 4 | from aat.core import ExchangeType, Instrument 5 | 6 | try: 7 | from aat.binding import OrderBookCpp # type: ignore 8 | from aat.binding import _CollectorCpp # type: ignore 9 | from aat.binding import _PriceLevelCpp # type: ignore 10 | 11 | _CPP = _in_cpp() 12 | except ImportError: 13 | OrderBookCpp, _CollectorCpp, _PriceLevelCpp = object, object, object 14 | _CPP = False 15 | 16 | 17 | def _make_cpp_collector(callback: Callable = lambda *args: args) -> _CollectorCpp: 18 | """helper method to ensure all arguments are setup""" 19 | return _CollectorCpp(callback) 20 | 21 | 22 | def _make_cpp_orderbook( 23 | instrument: Instrument, 24 | exchange_name: str = "", 25 | callback: Callable = lambda x: print(x), 26 | ) -> OrderBookCpp: 27 | """helper method to ensure all arguments are setup""" 28 | return OrderBookCpp(instrument, exchange_name or ExchangeType(""), callback) 29 | 30 | 31 | def _make_cpp_price_level(price: float, collector: _CollectorCpp) -> _PriceLevelCpp: 32 | """helper method to ensure all arguments are setup""" 33 | return _PriceLevelCpp(price, collector) 34 | -------------------------------------------------------------------------------- /aat/core/order_book/order_book/__init__.py: -------------------------------------------------------------------------------- 1 | from .order_book import OrderBook, OrderBookBase # noqa: F401 2 | from .lite import OrderBookLite # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/core/order_book/order_book/accessors.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/core/order_book/order_book/accessors.py -------------------------------------------------------------------------------- /aat/core/order_book/price_level/__init__.py: -------------------------------------------------------------------------------- 1 | from .price_level import _PriceLevel # noqa: F401 2 | from .ro import PriceLevelRO # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/core/order_book/price_level/ro.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from typing import Optional, Deque, Dict, List, Union 3 | from aat.core import Order 4 | 5 | 6 | class PriceLevelRO(object): 7 | """Readonly Price Level""" 8 | 9 | __slots__ = [ 10 | "_price", 11 | "_volume", 12 | "_number_of_orders", 13 | "_orders", 14 | ] 15 | 16 | def __init__( 17 | self, 18 | price: float, 19 | volume: float, 20 | number_of_orders: int = 0, 21 | _orders: Optional[Deque[Order]] = None, 22 | ): 23 | self._price = price 24 | self._volume = volume 25 | self._number_of_orders = number_of_orders 26 | self._orders = _orders or deque() 27 | 28 | @property 29 | def price(self) -> float: 30 | return self._price 31 | 32 | @property 33 | def volume(self) -> float: 34 | return self._volume 35 | 36 | @property 37 | def orders(self) -> int: 38 | return self._number_of_orders 39 | 40 | def dict(self) -> Dict[str, Union[int, float]]: 41 | return {"price": self.price, "volume": self.volume, "orders": self.orders} 42 | 43 | def list(self) -> List[float]: 44 | return [self.price, self.volume] 45 | 46 | def __eq__(self, other: object) -> bool: 47 | if isinstance(other, list): 48 | return self.list() == other 49 | elif isinstance(other, dict): 50 | return self.dict() == other 51 | elif isinstance(other, PriceLevelRO): 52 | return ( 53 | self.price == other.price 54 | and self.volume == other.volume 55 | and self.orders == other.orders 56 | ) 57 | else: 58 | raise TypeError() 59 | -------------------------------------------------------------------------------- /aat/core/order_book/utils.py: -------------------------------------------------------------------------------- 1 | from typing import List, Any 2 | import bisect 3 | 4 | 5 | def _insort(a: List, x: Any) -> bool: 6 | """insert x into a if not currently there""" 7 | i = bisect.bisect_left(a, x) 8 | if i != len(a) and a[i] == x: 9 | # don't insert 10 | return False 11 | a.insert(i, x) 12 | return True 13 | -------------------------------------------------------------------------------- /aat/core/position/__init__.py: -------------------------------------------------------------------------------- 1 | from .account import Account # noqa: F401 2 | from .cash import CashPosition # noqa: F401 3 | from .position import Position # noqa: F401 4 | -------------------------------------------------------------------------------- /aat/core/position/account.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping, Type, Dict, Tuple, List, Optional 2 | 3 | from aat.core.exchange import ExchangeType 4 | 5 | from .cpp import _CPP, _make_cpp_account 6 | from .position import Position 7 | 8 | 9 | class Account(object): 10 | __slots__ = ["__id", "__exchange", "__positions"] 11 | 12 | def __new__(cls: Type, *args: Tuple, **kwargs: Dict) -> "Account": 13 | if _CPP: 14 | return _make_cpp_account(*args, **kwargs) 15 | return super(Account, cls).__new__(cls) 16 | 17 | def __init__( 18 | self, 19 | id: str, 20 | exchange: ExchangeType, 21 | positions: Optional[List[Position]] = None, 22 | ) -> None: 23 | assert isinstance(exchange, ExchangeType) 24 | 25 | self.__id = id 26 | self.__exchange = exchange 27 | self.__positions = positions or [] 28 | 29 | # ******** # 30 | # Readonly # 31 | # ******** # 32 | @property 33 | def id(self) -> str: 34 | return self.__id 35 | 36 | @property 37 | def exchange(self) -> ExchangeType: 38 | return self.__exchange 39 | 40 | @property 41 | def positions(self) -> List[Position]: 42 | return self.__positions 43 | 44 | # ***********# 45 | # Read/write # 46 | # ***********# 47 | def addPosition(self, position: Position) -> None: 48 | self.__positions.append(position) 49 | 50 | def json(self) -> dict: 51 | return { 52 | "id": self.id, 53 | "exchange": self.exchange.json(), 54 | "positions": [p.json() for p in self.positions], 55 | } 56 | 57 | @staticmethod 58 | def fromJson(jsn: dict) -> "Account": 59 | kwargs = {} 60 | kwargs["id"] = jsn["id"] 61 | kwargs["exchange"] = ExchangeType.fromJson(jsn["exchange"]) 62 | kwargs["positions"] = [Position.fromJson(x) for x in jsn["positions"]] 63 | 64 | ret = Account(**kwargs) 65 | return ret 66 | 67 | @staticmethod 68 | def schema() -> Mapping[str, Type]: 69 | return {"id": str, "exchange": str} 70 | 71 | def __repr__(self) -> str: 72 | return f"Account(id={self.id}, exchange={self.exchange})" 73 | -------------------------------------------------------------------------------- /aat/core/position/cpp.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from ...common import _in_cpp 4 | 5 | try: 6 | from aat.binding import AccountCpp, CashPositionCpp, PositionCpp # type: ignore 7 | 8 | _CPP = _in_cpp() 9 | 10 | except ImportError: 11 | PositionCpp, CashPositionCpp, AccountCpp = object, object, object 12 | _CPP = False 13 | 14 | 15 | def _make_cpp_position(*args: Any, **kwargs: Any) -> PositionCpp: 16 | return PositionCpp(*args, **kwargs) 17 | 18 | 19 | def _make_cpp_cash(*args: Any, **kwargs: Any) -> CashPositionCpp: 20 | return CashPositionCpp(*args, **kwargs) 21 | 22 | 23 | def _make_cpp_account(*args: Any, **kwargs: Any) -> AccountCpp: 24 | return AccountCpp(*args, **kwargs) 25 | -------------------------------------------------------------------------------- /aat/core/position/db.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping, Tuple, Union, TYPE_CHECKING 2 | from ..exchange import ExchangeType 3 | from ..instrument import Instrument 4 | from ...config import InstrumentType 5 | 6 | if TYPE_CHECKING: 7 | # Circular import 8 | from . import Position, CashPosition 9 | 10 | 11 | class PositionDB(object): 12 | """Position registration""" 13 | 14 | def __init__(self) -> None: 15 | self._inst_map: Mapping[Instrument, Union[Position, CashPosition]] = {} 16 | self._exch_map: Mapping[ 17 | Tuple[ExchangeType, Instrument], Union[Position, CashPosition] 18 | ] = {} 19 | 20 | def add(self, position: "Position") -> None: 21 | pass 22 | 23 | def positions( 24 | self, 25 | name: str = "", 26 | type: InstrumentType = InstrumentType.CURRENCY, 27 | exchange: ExchangeType = ExchangeType(""), 28 | ) -> None: 29 | pass 30 | 31 | def get( 32 | self, 33 | name: str = "", 34 | type: InstrumentType = InstrumentType.CURRENCY, 35 | exchange: ExchangeType = ExchangeType(""), 36 | ) -> None: 37 | pass 38 | -------------------------------------------------------------------------------- /aat/core/table.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Tuple 2 | from .data import Event, Order, Trade 3 | from .handler import EventHandler 4 | 5 | try: 6 | from perspective import Table # type: ignore 7 | except ImportError: 8 | 9 | class Table(object): # type: ignore 10 | def __init__(*args: Any, **kwargs: Any) -> None: 11 | pass 12 | 13 | def update(self, *args: Any) -> None: 14 | pass 15 | 16 | def remove(self, *args: Any) -> None: 17 | pass 18 | 19 | 20 | class TableHandler(EventHandler): 21 | onData = None # type: ignore 22 | onHalt = None # type: ignore 23 | onContinue = None # type: ignore 24 | onError = None # type: ignore 25 | onStart = None # type: ignore 26 | onExit = None # type: ignore 27 | 28 | def __init__(self) -> None: 29 | self._trades = Table(Trade.schema(), index="timestamp") 30 | self._orders = Table(Order.schema(), index="id") 31 | 32 | def installTables(self, manager: Any) -> None: 33 | manager.host_table("trades", self._trades) 34 | manager.host_table("orders", self._orders) 35 | 36 | def tables(self) -> Tuple[Table, Table]: 37 | return self._trades, self._orders 38 | 39 | async def onTrade(self, event: Event) -> None: 40 | """onTrade""" 41 | trade: Trade = event.target # type: ignore 42 | self._trades.update([trade.json()]) 43 | 44 | async def onOpen(self, event: Event) -> None: 45 | """onOpen""" 46 | order: Order = event.target # type: ignore 47 | self._orders.update([order.json()]) 48 | 49 | async def onCancel(self, event: Event) -> None: 50 | """onCancel""" 51 | order: Order = event.target # type: ignore 52 | self._orders.remove([order.id]) 53 | 54 | async def onChange(self, event: Event) -> None: 55 | """onChange""" 56 | order: Order = event.target # type: ignore 57 | self._orders.update([order.json()]) 58 | 59 | async def onFill(self, event: Event) -> None: 60 | """onFill""" 61 | order: Order = event.target # type: ignore 62 | self._orders.remove([order.id]) 63 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | using json = nlohmann::json; 12 | 13 | #ifdef AAT_PYTHON 14 | #include 15 | #define ENUM_TO_STRING(type) \ 16 | inline std::string type##_to_string(type typ) { return type##_names[static_cast(typ)]; } 17 | #define ENUM_FROM_STRING(type) \ 18 | type inline type##_from_string(char* s) { \ 19 | if (_##type##_mapping.find(s) == _##type##_mapping.end()) { \ 20 | throw py::value_error(s); \ 21 | } \ 22 | return _##type##_mapping[s]; \ 23 | } 24 | #else 25 | #define ENUM_TO_STRING(type) \ 26 | inline std::string type##_to_string(type typ) { return type##_names[static_cast(typ)]; } 27 | #define ENUM_FROM_STRING(type) \ 28 | type inline type##_from_string(char* s) { \ 29 | if (_##type##_mapping.find(s) == _##type##_mapping.end()) { \ 30 | throw AATCPPException(s); \ 31 | } \ 32 | return _##type##_mapping[s]; \ 33 | } 34 | #endif 35 | 36 | namespace aat { 37 | namespace common { 38 | class AATCPPException : public std::exception { 39 | private: 40 | std::string msg = ""; 41 | 42 | public: 43 | explicit AATCPPException(std::string msg) 44 | : msg(msg) {} 45 | 46 | const char* 47 | what() const noexcept override { 48 | return msg.c_str(); 49 | } 50 | }; 51 | 52 | typedef std::uint64_t uint_t; 53 | typedef std::string str_t; 54 | typedef std::stringstream sstream_t; 55 | 56 | typedef std::chrono::system_clock datetime; 57 | typedef std::chrono::system_clock::time_point timestamp_t; 58 | 59 | inline str_t 60 | format_timestamp(timestamp_t t) { 61 | return date::format("%F %T", t); 62 | } 63 | } // namespace common 64 | } // namespace aat 65 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/config/parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/data/data.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace aat::common; 12 | using namespace aat::config; 13 | 14 | namespace aat { 15 | namespace core { 16 | struct _EventTarget { 17 | virtual ~_EventTarget() {} 18 | virtual str_t toString() const = 0; 19 | virtual json toJson() const = 0; 20 | virtual json perspectiveSchema() const = 0; 21 | }; 22 | 23 | struct Data : public _EventTarget { 24 | public: 25 | Data(uint_t id, timestamp_t timestamp, Instrument& instrument, ExchangeType& exchange = NullExchange, 26 | json data = nullptr) 27 | : id(id) 28 | , timestamp(timestamp) 29 | , type(DataType::DATA) 30 | , instrument(instrument) 31 | , exchange(exchange) 32 | , data(data) {} 33 | virtual ~Data() {} 34 | bool operator==(const Data& other); 35 | virtual str_t toString() const; 36 | virtual json toJson() const; 37 | virtual json perspectiveSchema() const; 38 | 39 | uint_t id; 40 | timestamp_t timestamp; 41 | const DataType type; 42 | const Instrument instrument; 43 | const ExchangeType exchange; 44 | json data; 45 | }; 46 | 47 | } // namespace core 48 | } // namespace aat 49 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/data/event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace aat::common; 12 | using namespace aat::config; 13 | 14 | namespace aat { 15 | namespace core { 16 | class Event { 17 | public: 18 | Event(EventType type, std::shared_ptr<_EventTarget> target) 19 | : type(type) 20 | , target(target) {} 21 | 22 | str_t toString() const; 23 | json toJson() const; 24 | 25 | const EventType type; 26 | std::shared_ptr<_EventTarget> target; 27 | }; 28 | 29 | } // namespace core 30 | } // namespace aat 31 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/data/order.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace aat::common; 13 | using namespace aat::config; 14 | 15 | namespace aat { 16 | namespace core { 17 | struct Order : public _EventTarget { 18 | Order(str_t id, timestamp_t timestamp, double volume, double price, Side side, Instrument& instrument, 19 | ExchangeType& exchange = NullExchange, double notional = 0.0, OrderType order_type = OrderType::MARKET, 20 | OrderFlag flag = OrderFlag::NONE, std::shared_ptr stop_target = nullptr); 21 | virtual ~Order() {} 22 | 23 | virtual bool operator==(Order& other) const; 24 | 25 | bool finished() const; 26 | void finish(); 27 | 28 | virtual str_t toString() const; 29 | virtual json toJson() const; 30 | virtual json perspectiveSchema() const; 31 | 32 | str_t id; 33 | timestamp_t timestamp; 34 | const DataType type; 35 | const Instrument instrument; 36 | const ExchangeType exchange; 37 | 38 | double volume; 39 | double price; 40 | const Side side; 41 | 42 | const OrderType order_type = OrderType::MARKET; 43 | const OrderFlag flag = OrderFlag::NONE; 44 | const std::shared_ptr stop_target = nullptr; 45 | double notional = 0.0; 46 | 47 | double filled = 0.0; 48 | bool force_done = false; 49 | }; 50 | 51 | } // namespace core 52 | } // namespace aat 53 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/data/trade.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace aat::common; 14 | using namespace aat::config; 15 | 16 | namespace aat { 17 | namespace core { 18 | struct Trade : public _EventTarget { 19 | Trade(str_t id, double volume, double price, 20 | std::deque> maker_orders = std::deque>(), 21 | std::shared_ptr taker_order = nullptr) 22 | : id(id) 23 | , timestamp(taker_order->timestamp) 24 | , type(DataType::TRADE) 25 | , volume(volume) 26 | , price(price) 27 | , maker_orders(maker_orders) 28 | , taker_order(taker_order) 29 | , my_order(nullptr) 30 | , _slippage(0.0) 31 | , _transaction_cost(0.0) { 32 | // enforce that stop target match stop type 33 | // assert(maker_orders.size() > 0); // not necessarily 34 | } 35 | virtual ~Trade() {} 36 | 37 | double 38 | slippage() const { 39 | return 0.0; 40 | } 41 | 42 | double 43 | transactionCost() const { 44 | return 0.0; 45 | } 46 | 47 | bool finished() const; 48 | 49 | virtual str_t toString() const; 50 | virtual json toJson() const; 51 | virtual json perspectiveSchema() const; 52 | 53 | str_t id; 54 | timestamp_t timestamp; 55 | const DataType type; 56 | 57 | double volume; 58 | double price; 59 | 60 | std::deque> maker_orders; 61 | std::shared_ptr taker_order; 62 | std::shared_ptr my_order; // FIXME 63 | 64 | double _slippage; 65 | double _transaction_cost; 66 | }; 67 | 68 | } // namespace core 69 | } // namespace aat 70 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/exchange/exchange.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | using json = nlohmann::json; 13 | using namespace aat::common; 14 | using namespace aat::config; 15 | 16 | namespace aat { 17 | namespace core { 18 | 19 | struct ExchangeType { 20 | explicit ExchangeType(str_t name) 21 | : name(name) {} 22 | 23 | virtual ~ExchangeType() {} 24 | 25 | str_t toString() const; 26 | virtual json toJson() const; 27 | 28 | bool 29 | operator==(const ExchangeType& other) const { 30 | return name == other.name; 31 | } 32 | explicit operator bool() const { return name != ""; } 33 | str_t name; 34 | }; 35 | 36 | static ExchangeType NullExchange = ExchangeType(""); 37 | 38 | } // namespace core 39 | } // namespace aat 40 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/instrument/instrument.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace aat::common; 10 | using namespace aat::config; 11 | 12 | namespace aat { 13 | namespace core { 14 | 15 | struct Instrument { 16 | Instrument(const str_t& name, InstrumentType type, ExchangeType exchange = NullExchange) 17 | : name(name) 18 | , type(type) {} 19 | 20 | explicit Instrument(const str_t& name) 21 | : name(name) 22 | , type(InstrumentType::EQUITY) {} 23 | 24 | virtual ~Instrument() {} 25 | bool operator==(const Instrument& other) const; 26 | str_t toString() const; 27 | virtual json toJson() const; 28 | 29 | str_t name; 30 | InstrumentType type; 31 | std::vector exchanges; 32 | }; 33 | 34 | } // namespace core 35 | } // namespace aat 36 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/order_book/collector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace aat { 12 | namespace core { 13 | 14 | class PriceLevel; 15 | 16 | class Collector { 17 | public: 18 | Collector(); 19 | explicit Collector(std::function)> callback); 20 | 21 | void reset(); 22 | void setCallback(std::function)> callback); 23 | void push(std::shared_ptr event); 24 | void pushOpen(std::shared_ptr order); 25 | void pushFill(std::shared_ptr order, bool accumulate = false, double filled_in_txn = 0.0); 26 | void pushChange(std::shared_ptr order, bool accumulate = false, double filled_in_txn = 0.0); 27 | void pushCancel(std::shared_ptr order, bool accumulate = false, double filled_in_txn = 0.0); 28 | void pushTrade(std::shared_ptr taker_order, double filled_in_txn); 29 | std::uint64_t clearLevel(std::shared_ptr price_level); 30 | void commit(); 31 | void revert(); 32 | void clear(); 33 | double getPrice() const; 34 | double getVolume() const; 35 | std::shared_ptr getTakerOrder() const; 36 | std::deque> getOrders() const; 37 | std::deque> getEvents() const; 38 | std::deque> getPriceLevels() const; 39 | std::uint64_t getClearedLevels() const; 40 | 41 | private: 42 | void _accumulate(std::shared_ptr order, double filled_in_txn); 43 | 44 | std::function)> callback; 45 | double price; 46 | double volume; 47 | std::deque> events; 48 | std::shared_ptr taker_order; 49 | std::deque> orders; 50 | std::deque> price_levels; 51 | }; 52 | 53 | } // namespace core 54 | } // namespace aat 55 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/order_book/order_book.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace aat::common; 17 | 18 | namespace aat { 19 | namespace core { 20 | class OrderBook; // fwd declare 21 | 22 | class OrderBookIterator { 23 | public: 24 | explicit OrderBookIterator( 25 | const OrderBook& book, double price_level = 0.0, int index_in_level = 0, Side side = Side::SELL) 26 | : order_book(book) 27 | , price_level(price_level) 28 | , index_in_level(index_in_level) 29 | , side(side) {} 30 | 31 | OrderBookIterator& operator++(); 32 | std::shared_ptr operator*(); 33 | bool operator==(const OrderBookIterator& that); 34 | 35 | private: 36 | const OrderBook& order_book; 37 | double price_level; 38 | int index_in_level; 39 | Side side; 40 | }; 41 | 42 | class OrderBook { 43 | public: 44 | explicit OrderBook(const Instrument& instrument); 45 | OrderBook(const Instrument& instrument, const ExchangeType& exchange); 46 | OrderBook( 47 | const Instrument& instrument, const ExchangeType& exchange, std::function)> callback); 48 | 49 | void setCallback(std::function)> callback); 50 | 51 | Instrument 52 | getInstrument() const { 53 | return instrument; 54 | } 55 | ExchangeType 56 | getExchange() const { 57 | return exchange; 58 | } 59 | std::function)> 60 | getCallback() const { 61 | return callback; 62 | } 63 | 64 | void reset(); 65 | 66 | void add(std::shared_ptr order); 67 | void cancel(std::shared_ptr order); 68 | void change(std::shared_ptr order); 69 | 70 | std::shared_ptr find(std::shared_ptr order); 71 | 72 | std::vector topOfBook() const; 73 | std::map> topOfBookMap() const; // For Binding 74 | double spread() const; 75 | 76 | std::vector level(uint_t level) const; 77 | std::vector> level(double price) const; 78 | std::vector> levels(uint_t levels) const; 79 | std::map>> levelsMap(uint_t levels) const; // For Binding 80 | 81 | str_t toString() const; 82 | 83 | // iterator 84 | friend class OrderBookIterator; 85 | typedef OrderBookIterator iterator; 86 | iterator begin() const; 87 | iterator end() const; 88 | 89 | private: 90 | void clearOrders(std::shared_ptr order, uint_t amount); 91 | double getTop(Side side, uint_t cleared); 92 | bool insort(std::vector& levels, double value); // NOLINT 93 | 94 | Collector collector; 95 | const Instrument& instrument; 96 | const ExchangeType& exchange; 97 | std::function)> callback; 98 | 99 | std::vector buy_levels; 100 | std::vector sell_levels; 101 | 102 | std::unordered_map> buys; 103 | std::unordered_map> sells; 104 | }; 105 | } // namespace core 106 | } // namespace aat 107 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/order_book/price_level.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace aat { 12 | namespace core { 13 | 14 | class Collector; 15 | 16 | class PriceLevel { 17 | public: 18 | PriceLevel(double price, Collector& collector); // NOLINT 19 | 20 | double 21 | getPrice() const { 22 | return price; 23 | } 24 | double getVolume() const; 25 | 26 | void add(std::shared_ptr order); 27 | std::shared_ptr find(std::shared_ptr order); 28 | std::shared_ptr remove(std::shared_ptr order); 29 | std::shared_ptr modify(std::shared_ptr order); 30 | std::shared_ptr cross( 31 | std::shared_ptr taker_order, std::vector>& secondaries); // NOLINT 32 | 33 | void clear(); 34 | void commit(); 35 | void revert(); 36 | 37 | std::uint64_t 38 | size() const { 39 | return orders.size(); 40 | } 41 | 42 | std::shared_ptr 43 | operator[](int i) { 44 | return orders[i]; 45 | } 46 | explicit operator bool() const { return orders.size() > 0; } 47 | using iterator = std::deque>::iterator; 48 | using const_iterator = std::deque>::const_iterator; 49 | 50 | iterator 51 | begin() noexcept { 52 | return orders.begin(); 53 | } 54 | const_iterator 55 | cbegin() const noexcept { 56 | return orders.cbegin(); 57 | } 58 | iterator 59 | end() noexcept { 60 | return orders.end(); 61 | } 62 | const_iterator 63 | cend() const noexcept { 64 | return orders.cend(); 65 | } 66 | 67 | private: 68 | double price; 69 | Collector& collector; 70 | std::deque> orders{}; 71 | std::deque> orders_staged; 72 | std::deque orders_filled_staged; 73 | std::vector> stop_orders; 74 | std::vector> stop_orders_staged; 75 | }; 76 | } // namespace core 77 | } // namespace aat 78 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/position/account.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace aat::common; 13 | using namespace aat::config; 14 | 15 | namespace aat { 16 | namespace core { 17 | class Account { 18 | public: 19 | Account(str_t id, ExchangeType& exchange, std::vector>& positions); 20 | 21 | void addPosition(std::shared_ptr position); 22 | 23 | str_t toString() const; 24 | json toJson() const; 25 | json perspectiveSchema() const; 26 | 27 | str_t id; 28 | const ExchangeType exchange; 29 | std::vector> positions; 30 | }; 31 | 32 | } // namespace core 33 | } // namespace aat 34 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/position/cash.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace aat::common; 14 | using namespace aat::config; 15 | 16 | namespace aat { 17 | namespace core { 18 | class CashPosition { 19 | public: 20 | CashPosition(double notional, timestamp_t timestamp, Instrument& instrument, ExchangeType& exchange); 21 | 22 | str_t toString() const; 23 | json toJson() const; 24 | json perspectiveSchema() const; 25 | 26 | timestamp_t timestamp; 27 | const Instrument instrument; 28 | const ExchangeType exchange; 29 | 30 | double notional; 31 | std::vector notional_history; 32 | std::vector notional_timestamps; 33 | }; 34 | 35 | } // namespace core 36 | } // namespace aat 37 | -------------------------------------------------------------------------------- /aat/cpp/include/aat/core/position/position.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace aat::common; 14 | using namespace aat::config; 15 | 16 | namespace aat { 17 | namespace core { 18 | class Position { 19 | public: 20 | Position(double size, double price, timestamp_t timestamp, Instrument& instrument, ExchangeType& exchange, 21 | std::vector>& trades); 22 | 23 | str_t toString() const; 24 | json toJson() const; 25 | json perspectiveSchema() const; 26 | 27 | timestamp_t timestamp; 28 | const Instrument instrument; 29 | const ExchangeType exchange; 30 | 31 | double size; 32 | std::vector size_history; 33 | std::vector size_timestamps; 34 | 35 | double price; 36 | std::vector price_history; 37 | std::vector price_timestamps; 38 | 39 | double investment; 40 | std::vector investment_history; 41 | std::vector investment_timestamps; 42 | 43 | double notional; 44 | std::vector notional_history; 45 | std::vector notional_timestamps; 46 | 47 | double instrumentPrice; 48 | std::vector instrumentPrice_history; 49 | std::vector instrumentPrice_timestamps; 50 | 51 | double pnl; 52 | std::vector pnl_history; 53 | std::vector pnl_timestamps; 54 | 55 | double unrealizedPnl; 56 | std::vector unrealizedPnl_history; 57 | std::vector unrealizedPnl_timestamps; 58 | 59 | std::vector> trades; 60 | }; 61 | 62 | } // namespace core 63 | } // namespace aat 64 | -------------------------------------------------------------------------------- /aat/cpp/src/config/enums.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace aat { 4 | namespace config {} 5 | } // namespace aat 6 | -------------------------------------------------------------------------------- /aat/cpp/src/config/parser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace aat { 4 | namespace config {} 5 | } // namespace aat 6 | -------------------------------------------------------------------------------- /aat/cpp/src/core/data/data.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace aat { 7 | namespace core { 8 | bool 9 | Data::operator==(const Data& other) { 10 | return id == other.id; 11 | } 12 | 13 | str_t 14 | Data::toString() const { 15 | sstream_t ss; 16 | ss << "Data( id=" << id << ", timestamp=" << format_timestamp(timestamp) << ", instrument=" << instrument.toString() 17 | << ", exchange=" << exchange.toString() << ")"; 18 | return ss.str(); 19 | } 20 | 21 | json 22 | Data::toJson() const { 23 | json ret; 24 | ret["id"] = id; 25 | ret["timestamp"] = format_timestamp(timestamp); 26 | ret["type"] = DataType_to_string(type); 27 | ret["instrument"] = instrument.toString(); 28 | ret["exchange"] = exchange.toString(); 29 | return ret; 30 | } 31 | 32 | json 33 | Data::perspectiveSchema() const { 34 | json ret; 35 | ret["id"] = "int"; 36 | ret["timestamp"] = "int"; 37 | ret["type"] = "str"; 38 | ret["instrument"] = "str"; 39 | ret["exchange"] = "str"; 40 | return ret; 41 | } 42 | 43 | } // namespace core 44 | } // namespace aat 45 | -------------------------------------------------------------------------------- /aat/cpp/src/core/data/event.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace aat { 7 | namespace core { 8 | str_t 9 | Event::toString() const { 10 | sstream_t ss; 11 | ss << "Event+(type=" << EventType_to_string(type) << ", target="; 12 | if (target) { 13 | ss << target->toString(); 14 | } else { 15 | ss << "None"; 16 | } 17 | ss << ")"; 18 | return ss.str(); 19 | } 20 | 21 | json 22 | Event::toJson() const { 23 | json ret; 24 | ret["type"] = EventType_to_string(type); 25 | 26 | if (target) { 27 | ret["target"] = target->toJson(); 28 | } 29 | return ret; 30 | } 31 | 32 | } // namespace core 33 | } // namespace aat 34 | -------------------------------------------------------------------------------- /aat/cpp/src/core/data/order.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace aat::common; 8 | 9 | namespace aat { 10 | namespace core { 11 | Order::Order(str_t id, timestamp_t timestamp, double volume, double price, Side side, Instrument& instrument, 12 | ExchangeType& exchange, double notional, OrderType order_type, OrderFlag flag, std::shared_ptr stop_target) 13 | : id(id) 14 | , timestamp(timestamp) 15 | , type(DataType::ORDER) 16 | , instrument(instrument) 17 | , exchange(exchange) 18 | , volume(volume) 19 | , price(price) 20 | , side(side) 21 | , order_type(order_type) 22 | , flag(flag) 23 | , stop_target(stop_target) 24 | , notional(notional) 25 | , filled(0.0) { 26 | // enforce that stop target match stop type 27 | if (order_type == OrderType::STOP) { 28 | // FIXME 29 | assert(stop_target); 30 | assert(stop_target->order_type != OrderType::STOP); 31 | } 32 | 33 | if (order_type != OrderType::MARKET) { 34 | // override notional 35 | notional = price * volume; 36 | } 37 | } 38 | 39 | bool 40 | Order::operator==(Order& other) const { 41 | return this->id == other.id; 42 | } 43 | 44 | bool 45 | Order::finished() const { 46 | return (volume == filled) || force_done; 47 | } 48 | 49 | void 50 | Order::finish() { 51 | force_done = true; 52 | } 53 | 54 | str_t 55 | Order::toString() const { 56 | sstream_t ss; 57 | ss << "Order+( instrument=" << instrument.toString() << ", " << volume << "@" << price 58 | << ", side=" << Side_to_string(side) << ", exchange=" << exchange.toString() << ")"; 59 | return ss.str(); 60 | } 61 | 62 | json 63 | Order::toJson() const { 64 | json ret; 65 | ret["id"] = id; 66 | ret["timestamp"] = format_timestamp(timestamp); 67 | ret["volume"] = volume; 68 | ret["price"] = price; 69 | ret["side"] = Side_to_string(side); 70 | ret["instrument"] = instrument.toString(); 71 | ret["exchange"] = exchange.toString(); 72 | return ret; 73 | } 74 | 75 | json 76 | Order::perspectiveSchema() const { 77 | json ret; 78 | ret["id"] = "str"; 79 | ret["timestamp"] = "int"; 80 | ret["volume"] = "float"; 81 | ret["price"] = "float"; 82 | ret["side"] = "str"; 83 | ret["instrument"] = "str"; 84 | ret["exchange"] = "str"; 85 | return ret; 86 | } 87 | 88 | } // namespace core 89 | } // namespace aat 90 | -------------------------------------------------------------------------------- /aat/cpp/src/core/data/trade.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace aat::common; 8 | 9 | namespace aat { 10 | namespace core { 11 | bool 12 | Trade::finished() const { 13 | return taker_order->finished(); 14 | } 15 | 16 | str_t 17 | Trade::toString() const { 18 | sstream_t ss; 19 | ss << "Trade+( id=" << id << ", timestamp=" << format_timestamp(timestamp) << volume << "@" << price 20 | << ", maker_orders=" << maker_orders.size() << ", taker_order=" << taker_order->toString() << ")"; 21 | return ss.str(); 22 | } 23 | 24 | json 25 | Trade::toJson() const { 26 | json ret; 27 | ret["id"] = id; 28 | ret["timestamp"] = format_timestamp(timestamp); 29 | ret["volume"] = volume; 30 | ret["price"] = price; 31 | 32 | ret["taker_order"] = taker_order->toJson(); 33 | 34 | std::vector orders; 35 | for (auto order : maker_orders) { 36 | orders.push_back(order->toJson()); 37 | } 38 | ret["maker_orders"] = orders; 39 | return ret; 40 | } 41 | 42 | json 43 | Trade::perspectiveSchema() const { 44 | json ret; 45 | ret["id"] = "str"; 46 | ret["timestamp"] = "int"; 47 | ret["volume"] = "float"; 48 | ret["price"] = "float"; 49 | 50 | // FIXME 51 | return ret; 52 | } 53 | 54 | } // namespace core 55 | } // namespace aat 56 | -------------------------------------------------------------------------------- /aat/cpp/src/core/exchange/exchange.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace aat { 6 | namespace core { 7 | str_t 8 | ExchangeType::toString() const { 9 | if (name != "") { 10 | return "Exchange+(" + name + ")"; 11 | } 12 | return "No Exchange"; 13 | } 14 | 15 | json 16 | ExchangeType::toJson() const { 17 | json ret; 18 | return ret; 19 | } 20 | 21 | } // namespace core 22 | } // namespace aat 23 | -------------------------------------------------------------------------------- /aat/cpp/src/core/instrument/instrument.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace aat { 4 | namespace core { 5 | 6 | bool 7 | Instrument::operator==(const Instrument& other) const { 8 | return this->name == other.name && this->type == other.type; 9 | } 10 | 11 | str_t 12 | Instrument::toString() const { 13 | sstream_t ss; 14 | ss << "Instrument+(" << name << "-" << InstrumentType_to_string(type) << ")"; 15 | return ss.str(); 16 | } 17 | 18 | json 19 | Instrument::toJson() const { 20 | json ret; 21 | return ret; 22 | } 23 | 24 | } // namespace core 25 | } // namespace aat 26 | -------------------------------------------------------------------------------- /aat/cpp/src/core/position/account.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace aat { 7 | namespace core { 8 | 9 | Account::Account(str_t id, ExchangeType& exchange, std::vector>& positions) 10 | : id(id) 11 | , exchange(exchange) 12 | , positions(positions) {} 13 | 14 | void 15 | addPosition(std::shared_ptr position) {} 16 | 17 | str_t 18 | Account::toString() const { 19 | sstream_t ss; 20 | ss << "Account+(id=" << id << ", exchange=" << exchange.toString() << ")"; 21 | return ss.str(); 22 | } 23 | 24 | json 25 | Account::toJson() const { 26 | json ret; 27 | ret["id"] = id; 28 | ret["exchange"] = exchange.toJson(); 29 | return ret; 30 | } 31 | 32 | json 33 | Account::perspectiveSchema() const { 34 | json ret; 35 | ret["id"] = "str"; 36 | ret["exchange"] = "str"; 37 | return ret; 38 | } 39 | 40 | } // namespace core 41 | } // namespace aat 42 | -------------------------------------------------------------------------------- /aat/cpp/src/core/position/cash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace aat { 7 | namespace core { 8 | 9 | CashPosition::CashPosition(double notional, timestamp_t timestamp, Instrument& instrument, ExchangeType& exchange) 10 | : timestamp(timestamp) 11 | , instrument(instrument) 12 | , exchange(exchange) 13 | , notional(notional) { 14 | notional_history.push_back(notional); 15 | notional_timestamps.push_back(timestamp); 16 | } 17 | 18 | str_t 19 | CashPosition::toString() const { 20 | sstream_t ss; 21 | ss << "Cash+(notional=" << notional << ", instrument=" << instrument.toString() 22 | << ", exchange=" << exchange.toString() << ")"; 23 | return ss.str(); 24 | } 25 | 26 | json 27 | CashPosition::toJson() const { 28 | json ret; 29 | ret["timestamp"] = format_timestamp(timestamp); 30 | ret["instrument"] = instrument.toJson(); 31 | ret["exchange"] = exchange.toJson(); 32 | 33 | ret["notional"] = notional; 34 | // ret["notional_history"] = notional_history; 35 | 36 | return ret; 37 | } 38 | 39 | json 40 | CashPosition::perspectiveSchema() const { 41 | json ret; 42 | ret["timestamp"] = "int"; 43 | ret["instrument"] = "str"; 44 | ret["exchange"] = "str"; 45 | ret["notional"] = "float"; 46 | return ret; 47 | } 48 | 49 | } // namespace core 50 | } // namespace aat 51 | -------------------------------------------------------------------------------- /aat/cpp/src/core/position/position.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace aat { 7 | namespace core { 8 | 9 | Position::Position(double size, double price, timestamp_t timestamp, Instrument& instrument, ExchangeType& exchange, 10 | std::vector>& trades) 11 | : timestamp(timestamp) 12 | , instrument(instrument) 13 | , exchange(exchange) 14 | , size(size) 15 | , price(price) 16 | , investment(size * price) 17 | , notional(size * price) 18 | , instrumentPrice(price) 19 | , pnl(0.0) 20 | , unrealizedPnl(0.0) 21 | , trades(trades) { 22 | size_history.push_back(size); 23 | size_timestamps.push_back(timestamp); 24 | 25 | price_history.push_back(price); 26 | price_timestamps.push_back(timestamp); 27 | 28 | investment_history.push_back(investment); 29 | investment_timestamps.push_back(timestamp); 30 | 31 | notional_history.push_back(notional); 32 | notional_timestamps.push_back(timestamp); 33 | 34 | instrumentPrice_history.push_back(price); 35 | instrumentPrice_timestamps.push_back(timestamp); 36 | 37 | pnl_history.push_back(pnl); 38 | pnl_timestamps.push_back(timestamp); 39 | 40 | unrealizedPnl_history.push_back(unrealizedPnl); 41 | unrealizedPnl_timestamps.push_back(timestamp); 42 | } 43 | 44 | str_t 45 | Position::toString() const { 46 | sstream_t ss; 47 | ss << "Position+(price=" << price << ", size=" << size << ", notional=" << notional << ", pnl=" << pnl 48 | << ", unrealizedPnl=" << unrealizedPnl << ", instrument=" << instrument.toString() 49 | << ", exchange=" << exchange.toString() << ")"; 50 | return ss.str(); 51 | } 52 | 53 | json 54 | Position::toJson() const { 55 | json ret; 56 | ret["timestamp"] = format_timestamp(timestamp); 57 | ret["instrument"] = instrument.toJson(); 58 | ret["exchange"] = exchange.toJson(); 59 | 60 | ret["size"] = size; 61 | // ret["size_history"] = size_history; 62 | 63 | ret["notional"] = notional; 64 | // ret["notional_history"] = notional_history; 65 | 66 | ret["price"] = price; 67 | // ret["price_history"] = price_history; 68 | 69 | ret["investment"] = investment; 70 | // ret["investment_history"] = investment_history; 71 | 72 | ret["instrumentPrice"] = instrumentPrice; 73 | // ret["instrumentPrice_history"] = instrumentPrice_history; 74 | 75 | ret["pnl"] = pnl; 76 | // ret["pnl_history"] = pnl_history; 77 | 78 | ret["unrealizedPnl"] = unrealizedPnl; 79 | // ret["unrealizedPnl_history"] = unrealizedPnl_history; 80 | 81 | // ret["trades"] = trades; 82 | 83 | return ret; 84 | } 85 | 86 | json 87 | Position::perspectiveSchema() const { 88 | json ret; 89 | ret["timestamp"] = "int"; 90 | ret["instrument"] = "str"; 91 | ret["exchange"] = "str"; 92 | ret["size"] = "float"; 93 | ret["notional"] = "float"; 94 | ret["price"] = "float"; 95 | ret["investment"] = "float"; 96 | ret["instrumentPrice"] = "float"; 97 | ret["pnl"] = "float"; 98 | ret["unrealizedPnl"] = "float"; 99 | return ret; 100 | } 101 | 102 | } // namespace core 103 | } // namespace aat 104 | -------------------------------------------------------------------------------- /aat/cpp/src/python/binding.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /aat/cpp/third/date/chrono_io.h: -------------------------------------------------------------------------------- 1 | #ifndef CHRONO_IO_H 2 | #define CHRONO_IO_H 3 | 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2016, 2017 Howard Hinnant 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | // Our apologies. When the previous paragraph was written, lowercase had not yet 27 | // been invented (that would involve another several millennia of evolution). 28 | // We did not mean to shout. 29 | 30 | // This functionality has moved to "date.h" 31 | 32 | #include "date.h" 33 | 34 | #endif // CHRONO_IO_H 35 | -------------------------------------------------------------------------------- /aat/cpp/third/date/ios.h: -------------------------------------------------------------------------------- 1 | // 2 | // ios.h 3 | // DateTimeLib 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2016 Alexander Kormanovsky 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | #ifndef ios_hpp 28 | #define ios_hpp 29 | 30 | #if __APPLE__ 31 | # include 32 | # if TARGET_OS_IPHONE 33 | # include 34 | 35 | namespace date 36 | { 37 | namespace iOSUtils 38 | { 39 | 40 | std::string get_tzdata_path(); 41 | std::string get_current_timezone(); 42 | 43 | } // namespace iOSUtils 44 | } // namespace date 45 | 46 | # endif // TARGET_OS_IPHONE 47 | #else // !__APPLE__ 48 | # define TARGET_OS_IPHONE 0 49 | #endif // !__APPLE__ 50 | #endif // ios_hpp 51 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/adl_serializer.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | NLOHMANN_JSON_NAMESPACE_BEGIN 19 | 20 | /// @sa https://json.nlohmann.me/api/adl_serializer/ 21 | template 22 | struct adl_serializer 23 | { 24 | /// @brief convert a JSON value to any value type 25 | /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ 26 | template 27 | static auto from_json(BasicJsonType && j, TargetType& val) noexcept( 28 | noexcept(::nlohmann::from_json(std::forward(j), val))) 29 | -> decltype(::nlohmann::from_json(std::forward(j), val), void()) 30 | { 31 | ::nlohmann::from_json(std::forward(j), val); 32 | } 33 | 34 | /// @brief convert a JSON value to any value type 35 | /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ 36 | template 37 | static auto from_json(BasicJsonType && j) noexcept( 38 | noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) 39 | -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) 40 | { 41 | return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); 42 | } 43 | 44 | /// @brief convert any value type to a JSON value 45 | /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/ 46 | template 47 | static auto to_json(BasicJsonType& j, TargetType && val) noexcept( 48 | noexcept(::nlohmann::to_json(j, std::forward(val)))) 49 | -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) 50 | { 51 | ::nlohmann::to_json(j, std::forward(val)); 52 | } 53 | }; 54 | 55 | NLOHMANN_JSON_NAMESPACE_END 56 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/input/position_t.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include // size_t 12 | 13 | #include 14 | 15 | NLOHMANN_JSON_NAMESPACE_BEGIN 16 | namespace detail 17 | { 18 | 19 | /// struct to capture the start position of the current token 20 | struct position_t 21 | { 22 | /// the total number of characters read 23 | std::size_t chars_read_total = 0; 24 | /// the number of characters read in the current line 25 | std::size_t chars_read_current_line = 0; 26 | /// the number of lines read 27 | std::size_t lines_read = 0; 28 | 29 | /// conversion to size_t to preserve SAX interface 30 | constexpr operator size_t() const 31 | { 32 | return chars_read_total; 33 | } 34 | }; 35 | 36 | } // namespace detail 37 | NLOHMANN_JSON_NAMESPACE_END 38 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/iterators/internal_iterator.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | NLOHMANN_JSON_NAMESPACE_BEGIN 15 | namespace detail 16 | { 17 | 18 | /*! 19 | @brief an iterator value 20 | 21 | @note This structure could easily be a union, but MSVC currently does not allow 22 | unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. 23 | */ 24 | template struct internal_iterator 25 | { 26 | /// iterator for JSON objects 27 | typename BasicJsonType::object_t::iterator object_iterator {}; 28 | /// iterator for JSON arrays 29 | typename BasicJsonType::array_t::iterator array_iterator {}; 30 | /// generic iterator for all other types 31 | primitive_iterator_t primitive_iterator {}; 32 | }; 33 | 34 | } // namespace detail 35 | NLOHMANN_JSON_NAMESPACE_END 36 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/iterators/iterator_traits.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include // random_access_iterator_tag 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | NLOHMANN_JSON_NAMESPACE_BEGIN 18 | namespace detail 19 | { 20 | 21 | template 22 | struct iterator_types {}; 23 | 24 | template 25 | struct iterator_types < 26 | It, 27 | void_t> 29 | { 30 | using difference_type = typename It::difference_type; 31 | using value_type = typename It::value_type; 32 | using pointer = typename It::pointer; 33 | using reference = typename It::reference; 34 | using iterator_category = typename It::iterator_category; 35 | }; 36 | 37 | // This is required as some compilers implement std::iterator_traits in a way that 38 | // doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. 39 | template 40 | struct iterator_traits 41 | { 42 | }; 43 | 44 | template 45 | struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> 46 | : iterator_types 47 | { 48 | }; 49 | 50 | template 51 | struct iterator_traits::value>> 52 | { 53 | using iterator_category = std::random_access_iterator_tag; 54 | using value_type = T; 55 | using difference_type = ptrdiff_t; 56 | using pointer = T*; 57 | using reference = T&; 58 | }; 59 | 60 | } // namespace detail 61 | NLOHMANN_JSON_NAMESPACE_END 62 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/iterators/primitive_iterator.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include // ptrdiff_t 12 | #include // numeric_limits 13 | 14 | #include 15 | 16 | NLOHMANN_JSON_NAMESPACE_BEGIN 17 | namespace detail 18 | { 19 | 20 | /* 21 | @brief an iterator for primitive JSON types 22 | 23 | This class models an iterator for primitive JSON types (boolean, number, 24 | string). It's only purpose is to allow the iterator/const_iterator classes 25 | to "iterate" over primitive values. Internally, the iterator is modeled by 26 | a `difference_type` variable. Value begin_value (`0`) models the begin, 27 | end_value (`1`) models past the end. 28 | */ 29 | class primitive_iterator_t 30 | { 31 | private: 32 | using difference_type = std::ptrdiff_t; 33 | static constexpr difference_type begin_value = 0; 34 | static constexpr difference_type end_value = begin_value + 1; 35 | 36 | JSON_PRIVATE_UNLESS_TESTED: 37 | /// iterator as signed integer type 38 | difference_type m_it = (std::numeric_limits::min)(); 39 | 40 | public: 41 | constexpr difference_type get_value() const noexcept 42 | { 43 | return m_it; 44 | } 45 | 46 | /// set iterator to a defined beginning 47 | void set_begin() noexcept 48 | { 49 | m_it = begin_value; 50 | } 51 | 52 | /// set iterator to a defined past the end 53 | void set_end() noexcept 54 | { 55 | m_it = end_value; 56 | } 57 | 58 | /// return whether the iterator can be dereferenced 59 | constexpr bool is_begin() const noexcept 60 | { 61 | return m_it == begin_value; 62 | } 63 | 64 | /// return whether the iterator is at end 65 | constexpr bool is_end() const noexcept 66 | { 67 | return m_it == end_value; 68 | } 69 | 70 | friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept 71 | { 72 | return lhs.m_it == rhs.m_it; 73 | } 74 | 75 | friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept 76 | { 77 | return lhs.m_it < rhs.m_it; 78 | } 79 | 80 | primitive_iterator_t operator+(difference_type n) noexcept 81 | { 82 | auto result = *this; 83 | result += n; 84 | return result; 85 | } 86 | 87 | friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept 88 | { 89 | return lhs.m_it - rhs.m_it; 90 | } 91 | 92 | primitive_iterator_t& operator++() noexcept 93 | { 94 | ++m_it; 95 | return *this; 96 | } 97 | 98 | primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp) 99 | { 100 | auto result = *this; 101 | ++m_it; 102 | return result; 103 | } 104 | 105 | primitive_iterator_t& operator--() noexcept 106 | { 107 | --m_it; 108 | return *this; 109 | } 110 | 111 | primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp) 112 | { 113 | auto result = *this; 114 | --m_it; 115 | return result; 116 | } 117 | 118 | primitive_iterator_t& operator+=(difference_type n) noexcept 119 | { 120 | m_it += n; 121 | return *this; 122 | } 123 | 124 | primitive_iterator_t& operator-=(difference_type n) noexcept 125 | { 126 | m_it -= n; 127 | return *this; 128 | } 129 | }; 130 | 131 | } // namespace detail 132 | NLOHMANN_JSON_NAMESPACE_END 133 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/json_ref.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | NLOHMANN_JSON_NAMESPACE_BEGIN 18 | namespace detail 19 | { 20 | 21 | template 22 | class json_ref 23 | { 24 | public: 25 | using value_type = BasicJsonType; 26 | 27 | json_ref(value_type&& value) 28 | : owned_value(std::move(value)) 29 | {} 30 | 31 | json_ref(const value_type& value) 32 | : value_ref(&value) 33 | {} 34 | 35 | json_ref(std::initializer_list init) 36 | : owned_value(init) 37 | {} 38 | 39 | template < 40 | class... Args, 41 | enable_if_t::value, int> = 0 > 42 | json_ref(Args && ... args) 43 | : owned_value(std::forward(args)...) 44 | {} 45 | 46 | // class should be movable only 47 | json_ref(json_ref&&) noexcept = default; 48 | json_ref(const json_ref&) = delete; 49 | json_ref& operator=(const json_ref&) = delete; 50 | json_ref& operator=(json_ref&&) = delete; 51 | ~json_ref() = default; 52 | 53 | value_type moved_or_copied() const 54 | { 55 | if (value_ref == nullptr) 56 | { 57 | return std::move(owned_value); 58 | } 59 | return *value_ref; 60 | } 61 | 62 | value_type const& operator*() const 63 | { 64 | return value_ref ? *value_ref : owned_value; 65 | } 66 | 67 | value_type const* operator->() const 68 | { 69 | return &** this; 70 | } 71 | 72 | private: 73 | mutable value_type owned_value = nullptr; 74 | value_type const* value_ref = nullptr; 75 | }; 76 | 77 | } // namespace detail 78 | NLOHMANN_JSON_NAMESPACE_END 79 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/macro_unscope.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | // restore clang diagnostic settings 12 | #if defined(__clang__) 13 | #pragma clang diagnostic pop 14 | #endif 15 | 16 | // clean up 17 | #undef JSON_ASSERT 18 | #undef JSON_INTERNAL_CATCH 19 | #undef JSON_THROW 20 | #undef JSON_PRIVATE_UNLESS_TESTED 21 | #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION 22 | #undef NLOHMANN_BASIC_JSON_TPL 23 | #undef JSON_EXPLICIT 24 | #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL 25 | #undef JSON_INLINE_VARIABLE 26 | #undef JSON_NO_UNIQUE_ADDRESS 27 | #undef JSON_DISABLE_ENUM_SERIALIZATION 28 | #undef JSON_USE_GLOBAL_UDLS 29 | 30 | #ifndef JSON_TEST_KEEP_MACROS 31 | #undef JSON_CATCH 32 | #undef JSON_TRY 33 | #undef JSON_HAS_CPP_11 34 | #undef JSON_HAS_CPP_14 35 | #undef JSON_HAS_CPP_17 36 | #undef JSON_HAS_CPP_20 37 | #undef JSON_HAS_FILESYSTEM 38 | #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM 39 | #undef JSON_HAS_THREE_WAY_COMPARISON 40 | #undef JSON_HAS_RANGES 41 | #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 42 | #endif 43 | 44 | #include 45 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/meta/call_std/begin.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | NLOHMANN_JSON_NAMESPACE_BEGIN 14 | 15 | NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); 16 | 17 | NLOHMANN_JSON_NAMESPACE_END 18 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/meta/call_std/end.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | NLOHMANN_JSON_NAMESPACE_BEGIN 14 | 15 | NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); 16 | 17 | NLOHMANN_JSON_NAMESPACE_END 18 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/meta/detected.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include 14 | 15 | NLOHMANN_JSON_NAMESPACE_BEGIN 16 | namespace detail 17 | { 18 | 19 | // https://en.cppreference.com/w/cpp/experimental/is_detected 20 | struct nonesuch 21 | { 22 | nonesuch() = delete; 23 | ~nonesuch() = delete; 24 | nonesuch(nonesuch const&) = delete; 25 | nonesuch(nonesuch const&&) = delete; 26 | void operator=(nonesuch const&) = delete; 27 | void operator=(nonesuch&&) = delete; 28 | }; 29 | 30 | template class Op, 33 | class... Args> 34 | struct detector 35 | { 36 | using value_t = std::false_type; 37 | using type = Default; 38 | }; 39 | 40 | template class Op, class... Args> 41 | struct detector>, Op, Args...> 42 | { 43 | using value_t = std::true_type; 44 | using type = Op; 45 | }; 46 | 47 | template class Op, class... Args> 48 | using is_detected = typename detector::value_t; 49 | 50 | template class Op, class... Args> 51 | struct is_detected_lazy : is_detected { }; 52 | 53 | template class Op, class... Args> 54 | using detected_t = typename detector::type; 55 | 56 | template class Op, class... Args> 57 | using detected_or = detector; 58 | 59 | template class Op, class... Args> 60 | using detected_or_t = typename detected_or::type; 61 | 62 | template class Op, class... Args> 63 | using is_detected_exact = std::is_same>; 64 | 65 | template class Op, class... Args> 66 | using is_detected_convertible = 67 | std::is_convertible, To>; 68 | 69 | } // namespace detail 70 | NLOHMANN_JSON_NAMESPACE_END 71 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/meta/identity_tag.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | NLOHMANN_JSON_NAMESPACE_BEGIN 14 | namespace detail 15 | { 16 | 17 | // dispatching helper struct 18 | template struct identity_tag {}; 19 | 20 | } // namespace detail 21 | NLOHMANN_JSON_NAMESPACE_END 22 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/meta/std_fs.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #if JSON_HAS_EXPERIMENTAL_FILESYSTEM 14 | #include 15 | NLOHMANN_JSON_NAMESPACE_BEGIN 16 | namespace detail 17 | { 18 | namespace std_fs = std::experimental::filesystem; 19 | } // namespace detail 20 | NLOHMANN_JSON_NAMESPACE_END 21 | #elif JSON_HAS_FILESYSTEM 22 | #include 23 | NLOHMANN_JSON_NAMESPACE_BEGIN 24 | namespace detail 25 | { 26 | namespace std_fs = std::filesystem; 27 | } // namespace detail 28 | NLOHMANN_JSON_NAMESPACE_END 29 | #endif 30 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/meta/void_t.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | NLOHMANN_JSON_NAMESPACE_BEGIN 14 | namespace detail 15 | { 16 | 17 | template struct make_void 18 | { 19 | using type = void; 20 | }; 21 | template using void_t = typename make_void::type; 22 | 23 | } // namespace detail 24 | NLOHMANN_JSON_NAMESPACE_END 25 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/detail/string_escape.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | NLOHMANN_JSON_NAMESPACE_BEGIN 14 | namespace detail 15 | { 16 | 17 | /*! 18 | @brief replace all occurrences of a substring by another string 19 | 20 | @param[in,out] s the string to manipulate; changed so that all 21 | occurrences of @a f are replaced with @a t 22 | @param[in] f the substring to replace with @a t 23 | @param[in] t the string to replace @a f 24 | 25 | @pre The search string @a f must not be empty. **This precondition is 26 | enforced with an assertion.** 27 | 28 | @since version 2.0.0 29 | */ 30 | template 31 | inline void replace_substring(StringType& s, const StringType& f, 32 | const StringType& t) 33 | { 34 | JSON_ASSERT(!f.empty()); 35 | for (auto pos = s.find(f); // find first occurrence of f 36 | pos != StringType::npos; // make sure f was found 37 | s.replace(pos, f.size(), t), // replace with t, and 38 | pos = s.find(f, pos + t.size())) // find next occurrence of f 39 | {} 40 | } 41 | 42 | /*! 43 | * @brief string escaping as described in RFC 6901 (Sect. 4) 44 | * @param[in] s string to escape 45 | * @return escaped string 46 | * 47 | * Note the order of escaping "~" to "~0" and "/" to "~1" is important. 48 | */ 49 | template 50 | inline StringType escape(StringType s) 51 | { 52 | replace_substring(s, StringType{"~"}, StringType{"~0"}); 53 | replace_substring(s, StringType{"/"}, StringType{"~1"}); 54 | return s; 55 | } 56 | 57 | /*! 58 | * @brief string unescaping as described in RFC 6901 (Sect. 4) 59 | * @param[in] s string to unescape 60 | * @return unescaped string 61 | * 62 | * Note the order of escaping "~1" to "/" and "~0" to "~" is important. 63 | */ 64 | template 65 | static void unescape(StringType& s) 66 | { 67 | replace_substring(s, StringType{"~1"}, StringType{"/"}); 68 | replace_substring(s, StringType{"~0"}, StringType{"~"}); 69 | } 70 | 71 | } // namespace detail 72 | NLOHMANN_JSON_NAMESPACE_END 73 | -------------------------------------------------------------------------------- /aat/cpp/third/nlohmann_json/nlohmann/json_fwd.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.2 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ 10 | #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ 11 | 12 | #include // int64_t, uint64_t 13 | #include // map 14 | #include // allocator 15 | #include // string 16 | #include // vector 17 | 18 | #include 19 | 20 | /*! 21 | @brief namespace for Niels Lohmann 22 | @see https://github.com/nlohmann 23 | @since version 1.0.0 24 | */ 25 | NLOHMANN_JSON_NAMESPACE_BEGIN 26 | 27 | /*! 28 | @brief default JSONSerializer template argument 29 | 30 | This serializer ignores the template arguments and uses ADL 31 | ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) 32 | for serialization. 33 | */ 34 | template 35 | struct adl_serializer; 36 | 37 | /// a class to store JSON values 38 | /// @sa https://json.nlohmann.me/api/basic_json/ 39 | template class ObjectType = 40 | std::map, 41 | template class ArrayType = std::vector, 42 | class StringType = std::string, class BooleanType = bool, 43 | class NumberIntegerType = std::int64_t, 44 | class NumberUnsignedType = std::uint64_t, 45 | class NumberFloatType = double, 46 | template class AllocatorType = std::allocator, 47 | template class JSONSerializer = 48 | adl_serializer, 49 | class BinaryType = std::vector> 50 | class basic_json; 51 | 52 | /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document 53 | /// @sa https://json.nlohmann.me/api/json_pointer/ 54 | template 55 | class json_pointer; 56 | 57 | /*! 58 | @brief default specialization 59 | @sa https://json.nlohmann.me/api/json/ 60 | */ 61 | using json = basic_json<>; 62 | 63 | /// @brief a minimal map-like container that preserves insertion order 64 | /// @sa https://json.nlohmann.me/api/ordered_map/ 65 | template 66 | struct ordered_map; 67 | 68 | /// @brief specialization that maintains the insertion order of object keys 69 | /// @sa https://json.nlohmann.me/api/ordered_json/ 70 | using ordered_json = basic_json; 71 | 72 | NLOHMANN_JSON_NAMESPACE_END 73 | 74 | #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ 75 | -------------------------------------------------------------------------------- /aat/cpp/third/pybind11/pybind11/common.h: -------------------------------------------------------------------------------- 1 | #include "detail/common.h" 2 | #warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." 3 | -------------------------------------------------------------------------------- /aat/cpp/third/pybind11/pybind11/complex.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/complex.h: Complex number support 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | 14 | #include 15 | 16 | /// glibc defines I as a macro which breaks things, e.g., boost template names 17 | #ifdef I 18 | # undef I 19 | #endif 20 | 21 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 22 | 23 | template 24 | struct format_descriptor, detail::enable_if_t::value>> { 25 | static constexpr const char c = format_descriptor::c; 26 | static constexpr const char value[3] = {'Z', c, '\0'}; 27 | static std::string format() { return std::string(value); } 28 | }; 29 | 30 | #ifndef PYBIND11_CPP17 31 | 32 | template 33 | constexpr const char 34 | format_descriptor, 35 | detail::enable_if_t::value>>::value[3]; 36 | 37 | #endif 38 | 39 | PYBIND11_NAMESPACE_BEGIN(detail) 40 | 41 | template 42 | struct is_fmt_numeric, detail::enable_if_t::value>> { 43 | static constexpr bool value = true; 44 | static constexpr int index = is_fmt_numeric::index + 3; 45 | }; 46 | 47 | template 48 | class type_caster> { 49 | public: 50 | bool load(handle src, bool convert) { 51 | if (!src) { 52 | return false; 53 | } 54 | if (!convert && !PyComplex_Check(src.ptr())) { 55 | return false; 56 | } 57 | Py_complex result = PyComplex_AsCComplex(src.ptr()); 58 | if (result.real == -1.0 && PyErr_Occurred()) { 59 | PyErr_Clear(); 60 | return false; 61 | } 62 | value = std::complex((T) result.real, (T) result.imag); 63 | return true; 64 | } 65 | 66 | static handle 67 | cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { 68 | return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); 69 | } 70 | 71 | PYBIND11_TYPE_CASTER(std::complex, const_name("complex")); 72 | }; 73 | PYBIND11_NAMESPACE_END(detail) 74 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 75 | -------------------------------------------------------------------------------- /aat/cpp/third/pybind11/pybind11/detail/typeid.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/detail/typeid.h: Compiler-independent access to type identifiers 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | #if defined(__GNUG__) 16 | # include 17 | #endif 18 | 19 | #include "common.h" 20 | 21 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 22 | PYBIND11_NAMESPACE_BEGIN(detail) 23 | 24 | /// Erase all occurrences of a substring 25 | inline void erase_all(std::string &string, const std::string &search) { 26 | for (size_t pos = 0;;) { 27 | pos = string.find(search, pos); 28 | if (pos == std::string::npos) { 29 | break; 30 | } 31 | string.erase(pos, search.length()); 32 | } 33 | } 34 | 35 | PYBIND11_NOINLINE void clean_type_id(std::string &name) { 36 | #if defined(__GNUG__) 37 | int status = 0; 38 | std::unique_ptr res{ 39 | abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free}; 40 | if (status == 0) { 41 | name = res.get(); 42 | } 43 | #else 44 | detail::erase_all(name, "class "); 45 | detail::erase_all(name, "struct "); 46 | detail::erase_all(name, "enum "); 47 | #endif 48 | detail::erase_all(name, "pybind11::"); 49 | } 50 | 51 | inline std::string clean_type_id(const char *typeid_name) { 52 | std::string name(typeid_name); 53 | detail::clean_type_id(name); 54 | return name; 55 | } 56 | 57 | PYBIND11_NAMESPACE_END(detail) 58 | 59 | /// Return a string representation of a C++ type 60 | template 61 | static std::string type_id() { 62 | return detail::clean_type_id(typeid(T).name()); 63 | } 64 | 65 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 66 | -------------------------------------------------------------------------------- /aat/cpp/third/pybind11/pybind11/eigen.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "eigen/matrix.h" 13 | -------------------------------------------------------------------------------- /aat/cpp/third/pybind11/pybind11/options.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/options.h: global settings that are configurable at runtime. 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "detail/common.h" 13 | 14 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 15 | 16 | class options { 17 | public: 18 | // Default RAII constructor, which leaves settings as they currently are. 19 | options() : previous_state(global_state()) {} 20 | 21 | // Class is non-copyable. 22 | options(const options &) = delete; 23 | options &operator=(const options &) = delete; 24 | 25 | // Destructor, which restores settings that were in effect before. 26 | ~options() { global_state() = previous_state; } 27 | 28 | // Setter methods (affect the global state): 29 | 30 | options &disable_user_defined_docstrings() & { 31 | global_state().show_user_defined_docstrings = false; 32 | return *this; 33 | } 34 | 35 | options &enable_user_defined_docstrings() & { 36 | global_state().show_user_defined_docstrings = true; 37 | return *this; 38 | } 39 | 40 | options &disable_function_signatures() & { 41 | global_state().show_function_signatures = false; 42 | return *this; 43 | } 44 | 45 | options &enable_function_signatures() & { 46 | global_state().show_function_signatures = true; 47 | return *this; 48 | } 49 | 50 | options &disable_enum_members_docstring() & { 51 | global_state().show_enum_members_docstring = false; 52 | return *this; 53 | } 54 | 55 | options &enable_enum_members_docstring() & { 56 | global_state().show_enum_members_docstring = true; 57 | return *this; 58 | } 59 | 60 | // Getter methods (return the global state): 61 | 62 | static bool show_user_defined_docstrings() { 63 | return global_state().show_user_defined_docstrings; 64 | } 65 | 66 | static bool show_function_signatures() { return global_state().show_function_signatures; } 67 | 68 | static bool show_enum_members_docstring() { 69 | return global_state().show_enum_members_docstring; 70 | } 71 | 72 | // This type is not meant to be allocated on the heap. 73 | void *operator new(size_t) = delete; 74 | 75 | private: 76 | struct state { 77 | bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. 78 | bool show_function_signatures = true; //< Include auto-generated function signatures 79 | // in docstrings. 80 | bool show_enum_members_docstring = true; //< Include auto-generated member list in enum 81 | // docstrings. 82 | }; 83 | 84 | static state &global_state() { 85 | static state instance; 86 | return instance; 87 | } 88 | 89 | state previous_state; 90 | }; 91 | 92 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 93 | -------------------------------------------------------------------------------- /aat/engine/__init__.py: -------------------------------------------------------------------------------- 1 | from .engine import TradingEngine # noqa: F401 2 | from .dispatch import StrategyManager # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/engine/dispatch/__init__.py: -------------------------------------------------------------------------------- 1 | from .manager import StrategyManager # noqa: F401 2 | from .execution import OrderManager # noqa: F401 3 | from .periodic import Periodic # noqa: F401 4 | from .portfolio import PortfolioManager, Portfolio # noqa: F401 5 | from .risk import RiskManager # noqa: F401 6 | -------------------------------------------------------------------------------- /aat/engine/dispatch/base.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod 2 | from typing import Optional, TYPE_CHECKING 3 | from aat.core import Event 4 | from aat.core.handler import EventHandler 5 | 6 | 7 | if TYPE_CHECKING: 8 | from .manager import StrategyManager 9 | 10 | 11 | class ManagerBase(EventHandler): 12 | @abstractmethod 13 | def _setManager(self, mgr: "StrategyManager") -> None: 14 | """set the root manager""" 15 | 16 | async def onBought( # type: ignore[override] 17 | self, event: Event, strategy: Optional[EventHandler] 18 | ) -> None: 19 | """Called on my order bought""" 20 | pass 21 | 22 | async def onSold( # type: ignore[override] 23 | self, event: Event, strategy: Optional[EventHandler] 24 | ) -> None: 25 | """Called on my order sold""" 26 | pass 27 | 28 | async def onTraded( # type: ignore[override] 29 | self, event: Event, strategy: Optional[EventHandler] 30 | ) -> None: 31 | """Called on my order bought or sold""" 32 | pass 33 | 34 | async def onReceived( # type: ignore[override] 35 | self, event: Event, strategy: Optional[EventHandler] 36 | ) -> None: 37 | """Called on my order received""" 38 | pass 39 | 40 | async def onRejected( # type: ignore[override] 41 | self, event: Event, strategy: Optional[EventHandler] 42 | ) -> None: 43 | """Called on my order rejected""" 44 | pass 45 | 46 | async def onCanceled( # type: ignore[override] 47 | self, event: Event, strategy: Optional[EventHandler] 48 | ) -> None: 49 | """Called on my order canceled""" 50 | pass 51 | -------------------------------------------------------------------------------- /aat/engine/dispatch/execution/__init__.py: -------------------------------------------------------------------------------- 1 | from .execution import OrderManager # noqa: F401 2 | -------------------------------------------------------------------------------- /aat/engine/dispatch/periodic.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from asyncio import Future 3 | from datetime import datetime 4 | from typing import Awaitable, Callable, List, Optional 5 | 6 | from temporalcache.utils import should_expire, calc # type: ignore 7 | 8 | 9 | class Periodic(object): 10 | def __init__( 11 | self, 12 | loop: asyncio.AbstractEventLoop, 13 | last_ts: datetime, 14 | function: Callable[..., Awaitable[None]], 15 | second: Optional[int], 16 | minute: Optional[int], 17 | hour: Optional[int], 18 | interval: bool = False, 19 | ) -> None: 20 | self._loop = loop 21 | self._function: Callable[..., Awaitable[None]] = function 22 | self.__second = second 23 | self.__minute = minute 24 | self.__hour = hour 25 | 26 | self._last = last_ts 27 | self._continue = True 28 | self._interval = interval 29 | 30 | @property 31 | def second(self) -> Optional[int]: 32 | return self.__second 33 | 34 | @property 35 | def minute(self) -> Optional[int]: 36 | return self.__minute 37 | 38 | @property 39 | def hour(self) -> Optional[int]: 40 | return self.__hour 41 | 42 | def stop(self) -> None: 43 | self._continue = False 44 | 45 | def expires(self, timestamp: datetime) -> bool: 46 | if (timestamp - self._last).total_seconds() < 1: 47 | return False 48 | if self._interval: 49 | total = (timestamp - self._last).total_seconds() 50 | if (timestamp - self._last).total_seconds() < 1: 51 | return False 52 | return total > calc( 53 | self.second or 1, self.minute or 0, self.hour or 0, 0, 0, 0, 0 54 | ) 55 | return should_expire(self._last, timestamp, self.second, self.minute, self.hour) 56 | 57 | async def execute(self, timestamp: datetime) -> Optional[Future]: 58 | if self.expires(timestamp): 59 | self._last = timestamp 60 | return asyncio.ensure_future(self._function(timestamp=timestamp)) 61 | else: 62 | return None 63 | 64 | 65 | class PeriodicManagerMixin(object): 66 | _periodics: List[Periodic] = [] 67 | 68 | def periodics(self) -> List[Periodic]: 69 | return self._periodics 70 | 71 | def periodicIntervals(self) -> int: 72 | """return the interval required for periodics, to optimize call times 73 | 1 - secondly 74 | 60 - minutely 75 | 3600 - hourly 76 | """ 77 | ret = 3600 78 | for p in self._periodics: 79 | if p._interval: 80 | # 1 for all interval 81 | return 1 82 | if p.second is None: 83 | # if any secondly, return 0 right away 84 | return 1 85 | elif p.minute is None: 86 | # if any require minutely, drop to 1 87 | ret = 60 88 | return ret 89 | -------------------------------------------------------------------------------- /aat/engine/dispatch/portfolio/__init__.py: -------------------------------------------------------------------------------- 1 | from .manager import PortfolioManager # noqa: F401 2 | from .mixin import StrategyManagerPortfolioMixin # noqa: F401 3 | from .portfolio import Portfolio # noqa: F401 4 | -------------------------------------------------------------------------------- /aat/engine/dispatch/portfolio/mixin.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from typing import Optional, List, Union, TYPE_CHECKING 3 | 4 | from aat.core import Instrument, ExchangeType, Position 5 | 6 | from .manager import PortfolioManager 7 | from .portfolio import Portfolio 8 | 9 | 10 | if TYPE_CHECKING: 11 | from aat.strategy import Strategy 12 | 13 | 14 | class StrategyManagerPortfolioMixin(object): 15 | _portfolio_mgr: PortfolioManager 16 | 17 | # ********************* 18 | # Risk Methods * 19 | # ********************* 20 | def portfolio(self) -> Portfolio: 21 | return self._portfolio_mgr.portfolio() 22 | 23 | def positions( 24 | self, 25 | strategy: "Strategy", 26 | instrument: Optional[Instrument] = None, 27 | exchange: Optional[ExchangeType] = None, 28 | ) -> List[Position]: 29 | return self._portfolio_mgr.positions( 30 | strategy=strategy, instrument=instrument, exchange=exchange 31 | ) 32 | 33 | def priceHistory( 34 | self, instrument: Optional[Instrument] = None 35 | ) -> Union[dict, pd.DataFrame]: 36 | return self._portfolio_mgr.priceHistory(instrument=instrument) 37 | -------------------------------------------------------------------------------- /aat/engine/dispatch/risk/__init__.py: -------------------------------------------------------------------------------- 1 | from .mixin import StrategyManagerRiskMixin # noqa: F401 2 | from .risk import RiskManager # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/engine/dispatch/risk/mixin.py: -------------------------------------------------------------------------------- 1 | from aat.core import Position 2 | from .risk import RiskManager 3 | from typing import Optional 4 | 5 | 6 | class StrategyManagerRiskMixin(object): 7 | _risk_mgr: RiskManager 8 | 9 | # ********************* 10 | # Risk Methods * 11 | # ********************* 12 | def risk(self, position: Optional[Position] = None) -> str: # TODO 13 | return self._risk_mgr.risk(position=position) 14 | -------------------------------------------------------------------------------- /aat/exchange/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't import external exchanges here as they might have deps 2 | from .exchange import Exchange # noqa: F401 3 | from .synthetic import SyntheticExchange # noqa: F401 4 | -------------------------------------------------------------------------------- /aat/exchange/base/__init__.py: -------------------------------------------------------------------------------- 1 | from .market_data import _MarketData # noqa: F401 2 | from .order_entry import _OrderEntry # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/exchange/base/market_data.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from typing import AsyncIterator, List, Optional 3 | 4 | from aat import Instrument, Event, OrderBook 5 | 6 | 7 | class _MarketData(metaclass=ABCMeta): 8 | """internal only class to represent the streaming-source 9 | side of a data source""" 10 | 11 | async def instruments(self) -> List[Instrument]: 12 | """get list of available instruments""" 13 | return [] 14 | 15 | async def subscribe(self, instrument: Instrument) -> None: 16 | """subscribe to market data for a given instrument""" 17 | 18 | async def tick(self) -> AsyncIterator[Event]: # type: ignore 19 | """return data from exchange""" 20 | 21 | async def book(self, instrument: Instrument) -> Optional[OrderBook]: 22 | """return order book""" 23 | -------------------------------------------------------------------------------- /aat/exchange/base/order_entry.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from typing import List 3 | from aat.core import Order, Position 4 | 5 | # from abc import ABCMeta, abstractmethod 6 | 7 | 8 | class _OrderEntry(metaclass=ABCMeta): 9 | """internal only class to represent the rest-sink 10 | side of a data source""" 11 | 12 | async def accounts(self) -> List[Position]: # TODO List[Account] ? 13 | """get accounts from source""" 14 | return [] 15 | 16 | async def balance(self) -> List[Position]: 17 | """get cash balance""" 18 | return [] 19 | 20 | async def newOrder(self, order: Order) -> bool: 21 | """submit a new order to the exchange. should set the given order's `id` field to exchange-assigned id 22 | 23 | Returns: 24 | True if order received 25 | False if order rejected 26 | 27 | For MarketData-only, can just return False/None 28 | """ 29 | raise NotImplementedError() 30 | 31 | async def cancelOrder(self, order: Order) -> bool: 32 | """cancel a previously submitted order to the exchange. 33 | 34 | Returns: 35 | True if order received 36 | False if order rejected 37 | 38 | For MarketData-only, can just return False/None 39 | """ 40 | raise NotImplementedError() 41 | -------------------------------------------------------------------------------- /aat/exchange/crypto/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/exchange/crypto/__init__.py -------------------------------------------------------------------------------- /aat/exchange/crypto/coinbase/__init__.py: -------------------------------------------------------------------------------- 1 | from .coinbase import CoinbaseProExchange # noqa: F401 2 | -------------------------------------------------------------------------------- /aat/exchange/exchange.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod 2 | from typing import List 3 | 4 | from aat.core import ExchangeType, Instrument 5 | 6 | from .base.market_data import _MarketData 7 | from .base.order_entry import _OrderEntry 8 | 9 | 10 | class Exchange(_MarketData, _OrderEntry): 11 | """Generic representation of an exchange. There are two primary functionalities of an exchange. 12 | 13 | Market Data Source: 14 | exchanges can stream data to the engine 15 | 16 | Order Entry Sink: 17 | exchanges can be queried for data, or send data 18 | """ 19 | 20 | def __init__(self, exchange: ExchangeType) -> None: 21 | self._exchange: ExchangeType = exchange 22 | 23 | def exchange(self) -> ExchangeType: 24 | return self._exchange 25 | 26 | @abstractmethod 27 | async def connect(self) -> None: 28 | """connect to exchange. should be asynchronous. 29 | 30 | For OrderEntry-only, can just return None 31 | """ 32 | 33 | async def lookup(self, instrument: Instrument) -> List[Instrument]: 34 | """lookup an instrument on the exchange""" 35 | return [] 36 | 37 | # ****************** # 38 | # Inherited methods # 39 | 40 | # From _MarketData 41 | # 42 | # async def tick(self): 43 | # def instruments(self): 44 | # def subscribe(self, instrument): 45 | 46 | # From _OrderEntry 47 | # 48 | # async def newOrder(self, order: Order): 49 | # def accounts(self) -> List: 50 | # ************************** # 51 | -------------------------------------------------------------------------------- /aat/exchange/generic/__init__.py: -------------------------------------------------------------------------------- 1 | from .csv import CSV # noqa: F401 2 | from .kafka import Kafka # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/exchange/generic/csv.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import csv 3 | from collections import deque 4 | from datetime import datetime 5 | from typing import List, Deque, AsyncGenerator, Any 6 | from aat.config import EventType, InstrumentType, Side, TradingType 7 | from aat.core import ExchangeType, Event, Instrument, Trade, Order 8 | from aat.exchange import Exchange 9 | 10 | 11 | class CSV(Exchange): 12 | """CSV File Exchange""" 13 | 14 | def __init__(self, trading_type: TradingType, verbose: bool, filename: str) -> None: 15 | super().__init__(ExchangeType("csv-{}".format(filename))) 16 | self._trading_type = trading_type 17 | self._verbose = verbose 18 | self._filename = filename 19 | self._data: List[Trade] = [] 20 | 21 | # "Order" management 22 | self._queued_orders: Deque[Order] = deque() 23 | self._order_id = 1 24 | 25 | async def instruments(self) -> List[Instrument]: 26 | """get list of available instruments""" 27 | return list(set(_.instrument for _ in self._data)) 28 | 29 | async def connect(self) -> None: 30 | with open(self._filename) as csvfile: 31 | self._reader = csv.DictReader(csvfile, delimiter=",") 32 | 33 | for row in self._reader: 34 | order = Order( 35 | volume=float(row["volume"]), 36 | price=float(row["close"]), 37 | side=Side.BUY, 38 | exchange=self.exchange(), 39 | instrument=Instrument( 40 | row["symbol"].split("-")[0], 41 | instrument=InstrumentType(row["symbol"].split("-")[1].upper()), 42 | exchange=self.exchange(), 43 | ), 44 | filled=float(row["volume"]), 45 | ) 46 | if "time" in row: 47 | order.timestamp = datetime.fromtimestamp(float(row["time"])) 48 | elif "date" in row: 49 | order.timestamp = datetime.fromisoformat(row["date"]) 50 | elif "datetime" in row: 51 | order.timestamp = datetime.fromisoformat(row["datetime"]) 52 | 53 | self._data.append( 54 | Trade( 55 | volume=float(row["volume"]), 56 | price=float(row["close"]), 57 | maker_orders=[], 58 | taker_order=order, 59 | ) 60 | ) 61 | 62 | async def tick(self) -> AsyncGenerator[Any, Event]: # type: ignore[override] 63 | for item in self._data: 64 | yield Event(EventType.TRADE, item) 65 | await asyncio.sleep(0) 66 | 67 | # save timestamp 68 | timestamp = item.timestamp 69 | 70 | while self._queued_orders: 71 | order = self._queued_orders.popleft() 72 | order.timestamp = timestamp 73 | order.filled = order.volume 74 | 75 | t = Trade( 76 | volume=order.volume, 77 | price=order.price, 78 | taker_order=order, 79 | maker_orders=[], 80 | my_order=order, # FIXME this isnt technically necessary as 81 | # the engine should do this automatically 82 | ) 83 | 84 | yield Event(type=EventType.TRADE, target=t) 85 | 86 | async def cancelOrder(self, order: Order) -> bool: 87 | # Can't cancel, orders execute immediately 88 | # TODO limit orders 89 | return False 90 | 91 | async def newOrder(self, order: Order) -> bool: 92 | if self._trading_type == TradingType.LIVE: 93 | raise NotImplementedError("Live OE not available for CSV") 94 | 95 | order.id = str(self._order_id) 96 | self._order_id += 1 97 | self._queued_orders.append(order) 98 | return True 99 | -------------------------------------------------------------------------------- /aat/exchange/generic/kafka.py: -------------------------------------------------------------------------------- 1 | from aat.exchange import Exchange 2 | from aat.config import TradingType 3 | from aat.core import ExchangeType 4 | 5 | 6 | class Kafka(Exchange): 7 | """Kafka Exchange""" 8 | 9 | def __init__(self, trading_type: TradingType, verbose: bool) -> None: 10 | super().__init__(ExchangeType("kafka")) 11 | self._trading_type = trading_type 12 | self._verbose = verbose 13 | -------------------------------------------------------------------------------- /aat/exchange/public/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't import submodules here as they might have uninstalled dependencies 2 | -------------------------------------------------------------------------------- /aat/exchange/public/ib/__init__.py: -------------------------------------------------------------------------------- 1 | from .ib import InteractiveBrokersExchange # noqa: F401 2 | -------------------------------------------------------------------------------- /aat/exchange/public/ib/spread.py: -------------------------------------------------------------------------------- 1 | from aat.config import Side 2 | from aat.core import Order 3 | from collections import deque 4 | from typing import Dict, Deque, Optional 5 | 6 | 7 | class SpreadReconstitute(object): 8 | def __init__(self) -> None: 9 | self._orders: Dict[str, Dict[Side, Deque[Order]]] = {} 10 | 11 | def push(self, order: Order) -> None: 12 | if order.id not in self._orders: 13 | self._orders[order.id] = {Side.BUY: deque(), Side.SELL: deque()} 14 | 15 | self._orders[order.id][order.side].append(order) 16 | 17 | def get(self, originalOrder: Order) -> Optional[Order]: 18 | if originalOrder.id not in self._orders: 19 | return None 20 | 21 | if ( 22 | self._orders[originalOrder.id][Side.BUY] 23 | and self._orders[originalOrder.id][Side.SELL] 24 | ): 25 | # if orders on both sides, pop out and assemble unified order 26 | buy = self._orders[originalOrder.id][Side.BUY].popleft() 27 | sell = self._orders[originalOrder.id][Side.SELL].popleft() 28 | 29 | # need to move in lock step for now 30 | assert buy.volume == sell.volume 31 | 32 | order = Order( 33 | volume=buy.volume, 34 | price=buy.price - sell.price, 35 | side=originalOrder.side, 36 | instrument=originalOrder.instrument, 37 | exchange=originalOrder.exchange, 38 | order_type=originalOrder.order_type, 39 | id=originalOrder.id, 40 | timestamp=originalOrder.timestamp, 41 | ) 42 | 43 | return order 44 | 45 | return None 46 | -------------------------------------------------------------------------------- /aat/exchange/public/tda.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/exchange/public/tda.py -------------------------------------------------------------------------------- /aat/exchange/synthetic/__main__.py: -------------------------------------------------------------------------------- 1 | from .server import main 2 | 3 | 4 | if __name__ == "__main__": 5 | main() 6 | -------------------------------------------------------------------------------- /aat/exchange/synthetic/server.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | import ujson # type: ignore 4 | import uvloop # type: ignore 5 | from websockets import serve # type: ignore 6 | from aat.exchange.synthetic import SyntheticExchange 7 | from aat.core import Order 8 | from aat.config import TradingType 9 | 10 | 11 | def _main(port: int = 5000): # type: ignore 12 | async def handle(websocket, *args, **kwargs): # type: ignore 13 | exchange = SyntheticExchange(trading_type=TradingType.SIMULATION) 14 | await exchange.connect() 15 | await exchange.instruments() 16 | 17 | while True: 18 | for item in exchange.snapshot(): 19 | print("sending snapshot: {}".format(item)) 20 | await websocket.send(ujson.dumps(item.json())) 21 | 22 | async for item in exchange.tick(snapshot=True): 23 | print("sending {}".format(item)) 24 | await websocket.send(ujson.dumps(item.json())) 25 | try: 26 | data = await asyncio.wait_for(websocket.recv(), timeout=0.1) 27 | order = Order.from_json(ujson.loads(data)) 28 | print("received order: {}".format(order)) 29 | ret = await exchange.newOrder(order) 30 | await websocket.send(ujson.dumps(ret.json())) 31 | except asyncio.TimeoutError: 32 | pass 33 | 34 | start_server = serve(handle, "0.0.0.0", port) 35 | print("listening on %d" % port) 36 | return start_server 37 | 38 | 39 | def main() -> None: 40 | try: 41 | asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 42 | asyncio.get_event_loop().run_until_complete( 43 | _main(int(os.environ.get("PORT", "5000"))) 44 | ) 45 | asyncio.get_event_loop().run_forever() 46 | except KeyboardInterrupt: 47 | print("terminating...") 48 | -------------------------------------------------------------------------------- /aat/exchange/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/exchange/test/__init__.py -------------------------------------------------------------------------------- /aat/strategy/__init__.py: -------------------------------------------------------------------------------- 1 | from .calculations import CalculationsMixin # noqa: F401 2 | from .strategy import Strategy # noqa: F401 3 | -------------------------------------------------------------------------------- /aat/strategy/portfolio.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from typing import Optional, List, Union, TYPE_CHECKING 3 | from aat.core import Instrument, ExchangeType, Position 4 | 5 | if TYPE_CHECKING: 6 | from aat.engine import StrategyManager 7 | from aat.engine.dispatch import Portfolio 8 | 9 | 10 | class StrategyPortfolioMixin(object): 11 | _manager: "StrategyManager" 12 | 13 | def positions( 14 | self, 15 | instrument: Optional[Instrument] = None, 16 | exchange: Optional[ExchangeType] = None, 17 | ) -> List[Position]: 18 | """select all positions 19 | 20 | Args: 21 | instrument (Instrument): filter positions by instrument 22 | exchange (ExchangeType): filter positions by exchange 23 | Returns: 24 | list (Position): list of positions 25 | """ 26 | return self._manager.positions( 27 | strategy=self, instrument=instrument, exchange=exchange # type: ignore # mixin 28 | ) 29 | 30 | def portfolio(self) -> "Portfolio": 31 | """Get portfolio object""" 32 | return self._manager.portfolio() 33 | 34 | def priceHistory( 35 | self, instrument: Optional[Instrument] = None 36 | ) -> Union[dict, pd.DataFrame]: 37 | """Get price history for asset 38 | 39 | Args: 40 | instrument (Instrument): get price history for instrument 41 | Returns: 42 | DataFrame: price history 43 | """ 44 | return self._manager.priceHistory(instrument=instrument) 45 | -------------------------------------------------------------------------------- /aat/strategy/risk.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, TYPE_CHECKING 2 | 3 | from aat.core import Position 4 | 5 | if TYPE_CHECKING: 6 | from aat.engine import StrategyManager 7 | 8 | 9 | class StrategyRiskMixin(object): 10 | _manager: "StrategyManager" 11 | 12 | ################ 13 | # Risk Methods # 14 | ################ 15 | def risk(self, position: Optional[Position] = None) -> str: # TODO 16 | """Get risk metrics 17 | 18 | Args: 19 | position (Position): only get metrics on this position 20 | Returns: 21 | dict: metrics 22 | """ 23 | return self._manager.risk(position=position) 24 | -------------------------------------------------------------------------------- /aat/strategy/sample/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/strategy/sample/__init__.py -------------------------------------------------------------------------------- /aat/strategy/sample/coinbase/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/strategy/sample/coinbase/__init__.py -------------------------------------------------------------------------------- /aat/strategy/sample/coinbase/buy_and_hold.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from pprint import pprint 3 | 4 | from aat import Strategy, Event, Order, Trade, Side, Instrument, InstrumentType 5 | 6 | 7 | class BuyAndHoldCBStrategy(Strategy): 8 | def __init__(self, notional: str, *args: Any, **kwargs: Any) -> None: 9 | super(BuyAndHoldCBStrategy, self).__init__(*args, **kwargs) 10 | self._notional = float(notional) 11 | 12 | async def onStart(self, event: Event) -> None: 13 | pprint(self.instruments()) 14 | pprint(self.positions()) 15 | 16 | await self.subscribe(Instrument("BTC-USD", InstrumentType.PAIR)) 17 | 18 | async def onTrade(self, event: Event) -> None: 19 | """Called whenever a `Trade` event is received""" 20 | # pprint(event) 21 | trade: Trade = event.target # type: ignore 22 | 23 | # no past trades, no current orders 24 | if not self.orders(trade.instrument) and not self.trades(trade.instrument): 25 | req = Order( 26 | side=Side.BUY, 27 | price=trade.price, 28 | volume=self._notional / trade.price, 29 | instrument=trade.instrument, 30 | order_type=Order.Types.MARKET, 31 | exchange=trade.exchange, 32 | ) 33 | 34 | print("requesting buy : {}".format(req)) 35 | 36 | await self.newOrder(req) 37 | 38 | async def onBought(self, event: Event) -> None: 39 | trade: Trade = event.target # type: ignore 40 | print("bought {:.2f} @ {:.2f}".format(trade.volume, trade.price)) 41 | 42 | async def onRejected(self, event: Event) -> None: 43 | print("order rejected") 44 | import sys 45 | 46 | sys.exit(0) 47 | 48 | async def onExit(self, event: Event) -> None: 49 | print("Finishing...") 50 | 51 | 52 | if __name__ == "__main__": 53 | from aat import TradingEngine, parseConfig 54 | 55 | cfg = parseConfig( 56 | [ 57 | "--trading_type", 58 | "sandbox", 59 | "--load_accounts", 60 | "--exchanges", 61 | "aat.exchange.crypto.coinbase:CoinbaseProExchange,,,,l2", 62 | "--strategies", 63 | "aat.strategy.sample.coinbase.buy_and_hold:BuyAndHoldCBStrategy", 64 | ] 65 | ) 66 | 67 | # OR via config file 68 | """ 69 | [general] 70 | verbose=0 71 | trading_type=sandbox 72 | load_accounts=1 73 | 74 | [exchange] 75 | exchanges= 76 | aat.exchange.crypto.coinbase:CoinbaseProExchange 77 | 78 | [strategy] 79 | strategies = 80 | aat.strategy.sample.coinbase.buy_and_hold:BuyAndHoldCBStrategy,1000 81 | """ 82 | 83 | print(cfg) 84 | t = TradingEngine(**cfg) 85 | t.start() 86 | -------------------------------------------------------------------------------- /aat/strategy/sample/coinbase/readonly.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from typing import Any 3 | from aat import Strategy, Event, Instrument, InstrumentType 4 | 5 | 6 | class ReadOnlyStrategy(Strategy): 7 | def __init__(self, *args: Any, **kwargs: Any) -> None: 8 | super(ReadOnlyStrategy, self).__init__(*args, **kwargs) 9 | 10 | async def onStart(self, event: Event) -> None: 11 | pprint(self.instruments()) 12 | pprint(self.positions()) 13 | 14 | await self.subscribe(Instrument("BTC-USD", InstrumentType.PAIR)) 15 | 16 | async def onTrade(self, event: Event) -> None: 17 | pprint(event) 18 | 19 | async def onOrder(self, event: Event) -> None: 20 | # pprint(event) 21 | pass 22 | 23 | async def onExit(self, event: Event) -> None: 24 | print("Finishing...") 25 | 26 | 27 | if __name__ == "__main__": 28 | from aat import TradingEngine, parseConfig 29 | 30 | cfg = parseConfig( 31 | [ 32 | "--trading_type", 33 | "sandbox", 34 | "--load_accounts", 35 | "--exchanges", 36 | "aat.exchange.crypto.coinbase:CoinbaseProExchange,,,,l2", 37 | "--strategies", 38 | "aat.strategy.sample.coinbase.readonly:ReadOnlyStrategy", 39 | ] 40 | ) 41 | 42 | # or via config file 43 | """ 44 | [general] 45 | verbose=0 46 | trading_type=sandbox 47 | load_accounts=1 48 | 49 | [exchange] 50 | exchanges= 51 | aat.exchange.crypto.coinbase:CoinbaseProExchange,,,,l2 52 | 53 | [strategy] 54 | strategies = 55 | aat.strategy.sample.coinbase.readonly:ReadOnlyStrategy 56 | """ 57 | print(cfg) 58 | t = TradingEngine(**cfg) 59 | t.start() 60 | -------------------------------------------------------------------------------- /aat/strategy/sample/csv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/strategy/sample/csv/__init__.py -------------------------------------------------------------------------------- /aat/strategy/sample/csv/readonly.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | from pprint import pprint 4 | from typing import Any 5 | from aat import Strategy, Event 6 | 7 | 8 | class ReadOnlyStrategy(Strategy): 9 | def __init__(self, *args: Any, **kwargs: Any) -> None: 10 | super(ReadOnlyStrategy, self).__init__(*args, **kwargs) 11 | 12 | async def onStart(self, event: Event) -> None: 13 | pprint(self.instruments()) 14 | pprint(self.positions()) 15 | 16 | for i in self.instruments(): 17 | print(i) 18 | await self.subscribe(i) 19 | 20 | async def onTrade(self, event: Event) -> None: 21 | pprint(event) 22 | 23 | async def onOrder(self, event: Event) -> None: 24 | # pprint(event) 25 | pass 26 | 27 | async def onExit(self, event: Event) -> None: 28 | print("Finishing...") 29 | 30 | 31 | if __name__ == "__main__": 32 | from aat import TradingEngine, parseConfig 33 | 34 | cfg = parseConfig( 35 | [ 36 | "--trading_type", 37 | "sandbox", 38 | "--load_accounts", 39 | "--timezone", 40 | "America/New_York", 41 | "--exchanges", 42 | "aat.exchange.generic:CSV,{}".format( 43 | os.path.join(os.path.dirname(__file__), "data", "aapl.csv") 44 | ), 45 | "--strategies", 46 | "aat.strategy.sample.csv.readonly:ReadOnlyStrategy", 47 | ] 48 | ) 49 | print(cfg) 50 | t = TradingEngine(**cfg) 51 | t.start() 52 | -------------------------------------------------------------------------------- /aat/strategy/sample/csv/readonly_periodic.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | from pprint import pprint 4 | from typing import Any, Dict, Tuple 5 | 6 | from aat import Strategy, Event 7 | 8 | 9 | class ReadOnlyStrategy(Strategy): 10 | def __init__(self, *args: Tuple, **kwargs: Dict) -> None: 11 | super(ReadOnlyStrategy, self).__init__(*args, **kwargs) 12 | self.count = 0 13 | 14 | async def onStart(self, event: Event) -> None: 15 | pprint(self.instruments()) 16 | pprint(self.positions()) 17 | 18 | for i in self.instruments(): 19 | await self.subscribe(i) 20 | self.at(self.onPeriodic, second=0, minute=0, hour=1) 21 | 22 | async def onTrade(self, event: Event) -> None: 23 | pprint(event) 24 | 25 | async def onOrder(self, event: Event) -> None: 26 | pprint(event) 27 | 28 | async def onExit(self, event: Event) -> None: 29 | print("Finishing...") 30 | 31 | async def onPeriodic(self, **kwargs: Any) -> None: 32 | print("here: {}".format(self.count)) 33 | self.count += 1 34 | 35 | 36 | if __name__ == "__main__": 37 | from aat import TradingEngine, parseConfig 38 | 39 | cfg = parseConfig( 40 | [ 41 | "--trading_type", 42 | "backtest", 43 | "--load_accounts", 44 | "--timezone", 45 | "America/New_York", 46 | "--exchanges", 47 | "aat.exchange.generic:CSV,{}".format( 48 | os.path.join(os.path.dirname(__file__), "data", "aapl.csv") 49 | ), 50 | "--strategies", 51 | "aat.strategy.sample.csv.readonly_periodic:ReadOnlyStrategy", 52 | ] 53 | ) 54 | print(cfg) 55 | t = TradingEngine(**cfg) 56 | t.start() 57 | assert t.strategies[0].count == 92 58 | -------------------------------------------------------------------------------- /aat/strategy/sample/csv/received.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | from pprint import pprint 4 | from typing import Any 5 | 6 | from aat import Strategy, Event, Order, Side, Trade 7 | 8 | 9 | class ReceivedStrategy(Strategy): 10 | def __init__(self, *args: Any, **kwargs: Any) -> None: 11 | super(ReceivedStrategy, self).__init__(*args, **kwargs) 12 | self._trade = True 13 | self._received_count = 0 14 | 15 | async def onStart(self, event: Event) -> None: 16 | pprint(self.instruments()) 17 | pprint(self.positions()) 18 | 19 | for i in self.instruments(): 20 | await self.subscribe(i) 21 | 22 | async def onTrade(self, event: Event) -> None: 23 | pprint(event) 24 | trade: Trade = event.target # type: ignore 25 | if self._trade and trade.my_order is None: 26 | await self.newOrder( 27 | Order( 28 | 1, 29 | trade.price, 30 | Side.BUY, 31 | trade.instrument, 32 | trade.exchange, 33 | ) 34 | ) 35 | self._trade = False 36 | 37 | async def onReceived(self, event: Event) -> None: 38 | pprint(event) 39 | self._trade = True 40 | self._received_count += 1 41 | 42 | async def onExit(self, event: Event) -> None: 43 | print("Finishing...") 44 | 45 | 46 | if __name__ == "__main__": 47 | from aat import TradingEngine, parseConfig 48 | 49 | cfg = parseConfig( 50 | [ 51 | "--trading_type", 52 | "backtest", 53 | "--load_accounts", 54 | "--exchanges", 55 | "aat.exchange.generic:CSV,{}".format( 56 | os.path.join(os.path.dirname(__file__), "data", "aapl.csv") 57 | ), 58 | "--strategies", 59 | "aat.strategy.sample.csv.received:ReceivedStrategy", 60 | ] 61 | ) 62 | print(cfg) 63 | t = TradingEngine(**cfg) 64 | t.start() 65 | print(t.strategies[0]._received_count) 66 | assert t.strategies[0]._received_count == 64 67 | -------------------------------------------------------------------------------- /aat/strategy/sample/ib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/strategy/sample/ib/__init__.py -------------------------------------------------------------------------------- /aat/strategy/sample/ib/buy_and_hold.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from aat import Strategy, Event, Order, Trade, Side, Instrument, InstrumentType 3 | 4 | 5 | class BuyAndHoldIBStrategy(Strategy): 6 | def __init__( 7 | self, instrument: str, notional: str, *args: Any, **kwargs: Any 8 | ) -> None: 9 | super(BuyAndHoldIBStrategy, self).__init__(*args, **kwargs) 10 | 11 | # symbol to trade 12 | self._symbol, self._symbol_type = instrument.split("-") 13 | 14 | # notional value to trade 15 | self._notional = float(notional) 16 | 17 | async def onStart(self, event: Event) -> None: 18 | # Create an instrument 19 | inst = Instrument(name=self._symbol, type=InstrumentType(self._symbol_type)) 20 | 21 | # Subscribe 22 | await self.subscribe(inst) 23 | print("Subscribing to {}".format(inst)) 24 | 25 | async def onTrade(self, event: Event) -> None: 26 | """Called whenever a `Trade` event is received""" 27 | trade: Trade = event.target # type: ignore 28 | 29 | # no past trades, no current orders 30 | if not self.orders(trade.instrument) and not self.trades(trade.instrument): 31 | req = Order( 32 | side=Side.BUY, 33 | price=trade.price, 34 | volume=self._notional // trade.price, 35 | instrument=trade.instrument, 36 | order_type=Order.Types.MARKET, 37 | exchange=trade.exchange, 38 | ) 39 | 40 | print("requesting buy : {}".format(req)) 41 | 42 | await self.newOrder(req) 43 | 44 | async def onBought(self, event: Event) -> None: 45 | trade: Trade = event.target # type: ignore 46 | print("bought {:.2f} @ {:.2f}".format(trade.volume, trade.price)) 47 | 48 | async def onRejected(self, event: Event) -> None: 49 | print("order rejected") 50 | import sys 51 | 52 | sys.exit(0) 53 | 54 | async def onExit(self, event: Event) -> None: 55 | print("Finishing...") 56 | -------------------------------------------------------------------------------- /aat/strategy/sample/ib/readonly.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from aat import TradingEngine, parseConfig 3 | 4 | cfg = parseConfig( 5 | [ 6 | "--trading_type", 7 | "live", 8 | "--load_accounts", 9 | "--exchanges", 10 | "aat.exchange.public.ib:InteractiveBrokersExchange", 11 | "--strategies", 12 | "aat.strategy.sample.readonly:ReadOnlyStrategy", 13 | ] 14 | ) 15 | print(cfg) 16 | t = TradingEngine(**cfg) 17 | t.start() 18 | -------------------------------------------------------------------------------- /aat/strategy/sample/iex/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/strategy/sample/iex/__init__.py -------------------------------------------------------------------------------- /aat/strategy/sample/iex/buy_and_hold.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Any 3 | from aat import ( 4 | Strategy, 5 | Event, 6 | Order, 7 | Trade, 8 | Side, 9 | Instrument, 10 | InstrumentType, 11 | ExchangeType, 12 | ) 13 | 14 | 15 | class BuyAndHoldIEXStrategy(Strategy): 16 | def __init__(self, symbol: str, *args: Any, **kwargs: Any) -> None: 17 | super(BuyAndHoldIEXStrategy, self).__init__(*args, **kwargs) 18 | self._symbol = symbol 19 | 20 | async def onStart(self, event: Event) -> None: 21 | # Create an instrument 22 | inst = Instrument( 23 | name=self._symbol, type=InstrumentType.EQUITY, exchange=ExchangeType("iex") 24 | ) 25 | 26 | # Subscribe 27 | await self.subscribe(inst) 28 | print("Subscribing to {}".format(inst)) 29 | 30 | async def onTrade(self, event: Event) -> None: 31 | """Called whenever a `Trade` event is received""" 32 | trade: Trade = event.target # type: ignore 33 | 34 | # no past trades, no current orders 35 | if not self.orders(trade.instrument) and not self.trades(trade.instrument): 36 | self._order = Order( 37 | side=Side.BUY, 38 | price=trade.price, 39 | volume=5000 // trade.price, 40 | instrument=trade.instrument, 41 | order_type=Order.Types.MARKET, 42 | exchange=trade.exchange, 43 | ) 44 | 45 | print("requesting buy : {}".format(self._order)) 46 | 47 | await self.newOrder(self._order) 48 | 49 | async def onBought(self, event: Event) -> None: 50 | trade: Trade = event.target # type: ignore 51 | print( 52 | "bought {} {:.2f} @ {:.2f}".format( 53 | trade.instrument, trade.volume, trade.price 54 | ) 55 | ) 56 | assert trade.my_order == self._order 57 | 58 | async def onRejected(self, event: Event) -> None: 59 | print("order rejected") 60 | import sys 61 | 62 | sys.exit(0) 63 | 64 | async def onExit(self, event: Event) -> None: 65 | print("Finishing...") 66 | if not os.environ.get("TESTING"): 67 | self.performanceCharts() 68 | 69 | 70 | if __name__ == "__main__": 71 | from aat import TradingEngine, parseConfig 72 | 73 | cfg = parseConfig( 74 | [ 75 | "--trading_type", 76 | "backtest", 77 | "--exchanges", 78 | "aat.exchange.public.iex:IEX,pk_75e8105f05d342a8abb1630c8894376c,True,1m,,,,", 79 | "--strategies", 80 | "aat.strategy.sample.iex.buy_and_hold:BuyAndHoldIEXStrategy,SPY", 81 | ] 82 | ) 83 | print(cfg) 84 | t = TradingEngine(**cfg) 85 | t.start() 86 | -------------------------------------------------------------------------------- /aat/strategy/sample/iex/readonly.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from typing import Any 3 | from aat import Strategy, Event, Instrument, InstrumentType, ExchangeType 4 | 5 | 6 | class ReadOnlyStrategy(Strategy): 7 | def __init__(self, symbol: str, *args: Any, **kwargs: Any) -> None: 8 | super(ReadOnlyStrategy, self).__init__(*args, **kwargs) 9 | self._inst = Instrument( 10 | name=symbol, type=InstrumentType.EQUITY, exchange=ExchangeType("iex") 11 | ) 12 | 13 | async def onStart(self, event: Event) -> None: 14 | await self.subscribe(self._inst) 15 | 16 | async def onTrade(self, event: Event) -> None: 17 | pprint(event) 18 | 19 | async def onOrder(self, event: Event) -> None: 20 | pprint(event) 21 | 22 | async def onExit(self, event: Event) -> None: 23 | print("Finishing...") 24 | 25 | 26 | if __name__ == "__main__": 27 | from aat import TradingEngine, parseConfig 28 | 29 | cfg = parseConfig( 30 | [ 31 | "--trading_type", 32 | "backtest", 33 | "--exchanges", 34 | "aat.exchange.public.iex:IEX,pk_75e8105f05d342a8abb1630c8894376c,True,1m,,,,", 35 | "--strategies", 36 | "aat.strategy.sample.iex.readonly:ReadOnlyStrategy,F", 37 | ] 38 | ) 39 | print(cfg) 40 | t = TradingEngine(**cfg) 41 | t.start() 42 | -------------------------------------------------------------------------------- /aat/strategy/sample/readonly.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from typing import Any 3 | from aat import Strategy, Event 4 | 5 | 6 | class ReadOnlyStrategy(Strategy): 7 | def __init__(self, *args: Any, **kwargs: Any) -> None: 8 | super(ReadOnlyStrategy, self).__init__(*args, **kwargs) 9 | 10 | async def onStart(self, event: Event) -> None: 11 | pprint(self.instruments()) 12 | pprint(self.positions()) 13 | 14 | for i in self.instruments(): 15 | print(i) 16 | await self.subscribe(i) 17 | 18 | async def onTrade(self, event: Event) -> None: 19 | pprint(event) 20 | 21 | async def onOrder(self, event: Event) -> None: 22 | pprint(event) 23 | 24 | async def onExit(self, event: Event) -> None: 25 | print("Finishing...") 26 | -------------------------------------------------------------------------------- /aat/strategy/sample/sell_plus_percent.py: -------------------------------------------------------------------------------- 1 | import os 2 | import math 3 | from typing import Any, Dict, Tuple 4 | 5 | from aat import Strategy, Event, Order, Trade, Side, Instrument 6 | 7 | 8 | class SellPlusPercentStrategy(Strategy): 9 | def __init__(self, percent: int = 10, *args: Any, **kwargs: Any) -> None: 10 | super(SellPlusPercentStrategy, self).__init__(*args, **kwargs) 11 | 12 | self._up_percent = 1.0 + float(percent) / 100 13 | self._down_percent = 1.0 - float(percent) / 100 14 | self._stop: Dict[Instrument, Tuple[float, float, float]] = {} 15 | 16 | async def onStart(self, event: Event) -> None: 17 | pos = self.positions() 18 | print("positions: {}".format(pos)) 19 | 20 | async def onTrade(self, event: Event) -> None: 21 | """Called whenever a `Trade` event is received""" 22 | trade: Trade = event.target # type: ignore 23 | 24 | # no current orders, no past trades 25 | if not self.orders(trade.instrument) and not self.trades(trade.instrument): 26 | req = Order( 27 | side=Side.BUY, 28 | price=trade.price, 29 | volume=math.ceil(1000 / trade.price), 30 | instrument=trade.instrument, 31 | exchange=trade.exchange, 32 | ) 33 | 34 | print("requesting buy : {}".format(req)) 35 | await self.newOrder(req) 36 | 37 | else: 38 | # no current orders, 1 past trades, and stop set 39 | if ( 40 | not self.orders(trade.instrument) 41 | and len(self.trades(trade.instrument)) == 1 42 | and trade.instrument in self._stop 43 | and ( 44 | trade.price >= self._stop[trade.instrument][0] 45 | or trade.price <= self._stop[trade.instrument][1] 46 | ) 47 | ): 48 | req = Order( 49 | side=Side.SELL, 50 | price=trade.price, 51 | volume=self._stop[trade.instrument][2], 52 | instrument=trade.instrument, 53 | exchange=trade.exchange, 54 | ) 55 | 56 | print("requesting sell : {}".format(req)) 57 | await self.newOrder(req) 58 | 59 | async def onBought(self, event: Event) -> None: 60 | trade: Trade = event.target # type: ignore 61 | 62 | print( 63 | "bought {} {:.2f} @ {:.2f}".format( 64 | trade.instrument, trade.volume, trade.price 65 | ) 66 | ) 67 | self._stop[trade.instrument] = ( 68 | trade.price * self._up_percent, 69 | trade.price * self._down_percent, 70 | trade.volume, 71 | ) 72 | 73 | async def onSold(self, event: Event) -> None: 74 | trade: Trade = event.target # type: ignore 75 | print("sold {:.2f} @ {:.2f}".format(trade.volume, trade.price)) 76 | del self._stop[trade.instrument] 77 | 78 | async def onRejected(self, event: Event) -> None: 79 | print("order rejected") 80 | import sys 81 | 82 | sys.exit(0) 83 | 84 | async def onExit(self, event: Event) -> None: 85 | print("Finishing...") 86 | # self.performanceCharts() 87 | 88 | 89 | if __name__ == "__main__": 90 | from aat import TradingEngine, parseConfig 91 | 92 | cfg = parseConfig( 93 | [ 94 | "--trading_type", 95 | "backtest", 96 | "--load_accounts", 97 | "--exchanges", 98 | "aat.exchange.generic:CSV,{}".format( 99 | os.path.join(os.path.dirname(__file__), "data", "aapl.csv") 100 | ), 101 | "--strategies", 102 | "aat.strategy.sample.readonly:ReadOnlyStrategy", 103 | ] 104 | ) 105 | print(cfg) 106 | t = TradingEngine(**cfg) 107 | t.start() 108 | -------------------------------------------------------------------------------- /aat/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/__init__.py -------------------------------------------------------------------------------- /aat/tests/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/config/__init__.py -------------------------------------------------------------------------------- /aat/tests/config/test_enums.py: -------------------------------------------------------------------------------- 1 | from aat.config import Side, EventType, DataType, InstrumentType, OrderType, OrderFlag 2 | 3 | 4 | class TestEnums: 5 | def test_side(self): 6 | assert Side.BUY.name == "BUY" 7 | assert Side.SELL.name == "SELL" 8 | 9 | def test_event_type(self): 10 | assert EventType.TRADE.name == "TRADE" 11 | assert EventType.OPEN.name == "OPEN" 12 | assert EventType.CANCEL.name == "CANCEL" 13 | assert EventType.CHANGE.name == "CHANGE" 14 | assert EventType.FILL.name == "FILL" 15 | assert EventType.DATA.name == "DATA" 16 | assert EventType.HALT.name == "HALT" 17 | assert EventType.CONTINUE.name == "CONTINUE" 18 | assert EventType.ERROR.name == "ERROR" 19 | assert EventType.START.name == "START" 20 | assert EventType.EXIT.name == "EXIT" 21 | 22 | def test_data_type(self): 23 | assert DataType.ORDER.name == "ORDER" 24 | assert DataType.TRADE.name == "TRADE" 25 | 26 | def test_instrument_type(self): 27 | assert InstrumentType.CURRENCY.name == "CURRENCY" 28 | assert InstrumentType.EQUITY.name == "EQUITY" 29 | 30 | def test_order_type(self): 31 | assert OrderType.LIMIT.name == "LIMIT" 32 | assert OrderType.MARKET.name == "MARKET" 33 | assert OrderType.STOP.name == "STOP" 34 | 35 | def test_order_flag(self): 36 | assert OrderFlag.NONE.name == "NONE" 37 | assert OrderFlag.FILL_OR_KILL.name == "FILL_OR_KILL" 38 | assert OrderFlag.ALL_OR_NONE.name == "ALL_OR_NONE" 39 | assert OrderFlag.IMMEDIATE_OR_CANCEL.name == "IMMEDIATE_OR_CANCEL" 40 | -------------------------------------------------------------------------------- /aat/tests/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/core/__init__.py -------------------------------------------------------------------------------- /aat/tests/core/instrument/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/core/instrument/__init__.py -------------------------------------------------------------------------------- /aat/tests/core/instrument/test_calendar.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from aat.core import ExchangeType, Instrument, TradingDay 3 | 4 | 5 | class TestInstrumentCalendar(object): 6 | def test_instrument_calendar(self): 7 | TradingDay() 8 | 9 | @pytest.mark.skipif("os.environ.get('AAT_USE_CPP', '')") 10 | def test_instrument_calendar_getter(self): 11 | t = TradingDay() 12 | e = ExchangeType("test-exchange") 13 | 14 | i = Instrument( 15 | "TestTradingDayInst", 16 | exchange=e, 17 | trading_day=t, 18 | ) 19 | 20 | assert i.tradingDay == t 21 | -------------------------------------------------------------------------------- /aat/tests/core/instrument/test_instrument.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from aat.core import ExchangeType, Instrument 3 | 4 | 5 | class TestInstrument(object): 6 | @pytest.mark.skipif("os.environ.get('AAT_USE_CPP', '')") 7 | def test_instrument(self): 8 | E1 = ExchangeType("E1") 9 | E2 = ExchangeType("E2") 10 | E3 = ExchangeType("E3") 11 | 12 | i1 = Instrument( 13 | "TestInst", 14 | exchange=E1, 15 | broker_id="1", 16 | ) 17 | 18 | i2 = Instrument( 19 | "TestInst", 20 | exchange=E2, 21 | broker_id="2", 22 | broker_exchange="test", 23 | ) 24 | 25 | i3 = Instrument( 26 | "TestInst", 27 | exchange=E3, 28 | broker_id="3", 29 | ) 30 | 31 | assert i1.tradingLines() == [i1, i2, i3] 32 | assert i2.tradingLines() == [i1, i2, i3] 33 | assert i3.tradingLines() == [i1, i2, i3] 34 | -------------------------------------------------------------------------------- /aat/tests/core/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/core/models/__init__.py -------------------------------------------------------------------------------- /aat/tests/core/models/test_order.py: -------------------------------------------------------------------------------- 1 | # type: ignore 2 | import pytest 3 | from aat.common import _in_cpp 4 | from aat.core import Order, Instrument, ExchangeType 5 | 6 | _INSTRUMENT = Instrument("TE.ST") 7 | 8 | 9 | class TestOrder: 10 | def test_stop_order_validation(self): 11 | if _in_cpp(): 12 | return 13 | 14 | with pytest.raises(AssertionError): 15 | Order( 16 | volume=0.0, 17 | price=5.0, 18 | side=Order.Sides.SELL, 19 | exchange=ExchangeType(""), 20 | order_type=Order.Types.STOP, 21 | stop_target=Order( 22 | volume=0.5, 23 | price=5.0, 24 | side=Order.Sides.SELL, 25 | exchange=ExchangeType(""), 26 | order_type=Order.Types.STOP, 27 | instrument=_INSTRUMENT, 28 | ), 29 | instrument=_INSTRUMENT, 30 | ) 31 | -------------------------------------------------------------------------------- /aat/tests/core/models/test_trade.py: -------------------------------------------------------------------------------- 1 | # type: ignore 2 | import os 3 | import pytest 4 | from collections import deque 5 | from aat.core import Trade, Order, Instrument, ExchangeType 6 | 7 | 8 | _INSTRUMENT = Instrument("TE.ST") 9 | 10 | 11 | class TestTrade: 12 | def test_maker_orders_validation(self): 13 | if not os.environ.get("AAT_USE_CPP"): 14 | with pytest.raises(Exception): 15 | o = Order( 16 | volume=0.0, 17 | price=5.0, 18 | side=Order.Sides.SELL, 19 | order_type=Order.Types.LIMIT, 20 | exchange=ExchangeType(""), 21 | instrument=_INSTRUMENT, 22 | ) 23 | Trade(maker_orders=deque(), taker_order=o) 24 | -------------------------------------------------------------------------------- /aat/tests/core/order_book/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/core/order_book/__init__.py -------------------------------------------------------------------------------- /aat/tests/core/order_book/helpers.py: -------------------------------------------------------------------------------- 1 | from aat.config import Side, OrderFlag, OrderType 2 | from aat.core import Order, ExchangeType 3 | 4 | 5 | def _seed(ob, instrument, flag=OrderFlag.NONE): 6 | x = 0.5 7 | while x < 10.0: 8 | side = Side.BUY if x <= 5 else Side.SELL 9 | order = Order( 10 | volume=1.0, 11 | price=x, 12 | side=side, 13 | instrument=instrument, 14 | exchange=ExchangeType(""), 15 | order_type=OrderType.LIMIT, 16 | flag=flag, 17 | id="1", 18 | ) 19 | ob.add(order) 20 | x += 0.5 21 | -------------------------------------------------------------------------------- /aat/tests/core/order_book/test_collector.py: -------------------------------------------------------------------------------- 1 | from aat.core.order_book.collector import _Collector 2 | 3 | 4 | class TestCollector: 5 | def test_collector(self): 6 | c = _Collector(lambda *args: print(args)) 7 | assert c 8 | -------------------------------------------------------------------------------- /aat/tests/core/order_book/test_order_book.py: -------------------------------------------------------------------------------- 1 | from aat.config import Side 2 | from aat.core import Instrument, OrderBook, Order 3 | from .helpers import _seed 4 | 5 | _INSTRUMENT = Instrument("TE.ST") 6 | 7 | 8 | class TestOrderBook: 9 | def test_order_book_init(self): 10 | ob = OrderBook(_INSTRUMENT) 11 | print(ob.topOfBook()) 12 | assert ob.topOfBook()[Side.BUY] == [0, 0] 13 | assert ob.topOfBook()[Side.SELL] == [float("inf"), 0] 14 | 15 | def test_order_book_run(self): 16 | ob = OrderBook(_INSTRUMENT) 17 | _seed(ob, _INSTRUMENT) 18 | 19 | assert ob.topOfBook()[Side.BUY] == [5.0, 1.0] 20 | assert ob.topOfBook()[Side.SELL] == [5.5, 1.0] 21 | 22 | data = Order( 23 | volume=5.0, 24 | price=4.5, 25 | side=Order.Sides.SELL, 26 | instrument=_INSTRUMENT, 27 | order_type=Order.Types.LIMIT, 28 | ) 29 | ob.add(data) 30 | 31 | print(ob) 32 | assert ob.topOfBook() == {Side.BUY: [4.0, 1.0], Side.SELL: [4.5, 3.0]} 33 | print(ob.levels(3)) 34 | assert ob.levels(3) == { 35 | Side.BUY: [[4.0, 1.0], [3.5, 1.0], [3.0, 1.0]], 36 | Side.SELL: [[4.5, 3.0], [5.5, 1.0], [6.0, 1.0]], 37 | } 38 | 39 | data = Order( 40 | volume=4.0, 41 | price=5.5, 42 | side=Order.Sides.BUY, 43 | instrument=_INSTRUMENT, 44 | order_type=Order.Types.LIMIT, 45 | ) 46 | ob.add(data) 47 | 48 | print(ob) 49 | assert ob.topOfBook() == {Side.BUY: [4.0, 1.0], Side.SELL: [6.0, 1.0]} 50 | print(ob.levels(3)) 51 | assert ob.levels(3) == { 52 | Side.BUY: [[4.0, 1.0], [3.5, 1.0], [3.0, 1.0]], 53 | Side.SELL: [[6.0, 1.0], [6.5, 1.0], [7.0, 1.0]], 54 | } 55 | 56 | def test_order_book_clearing_order(self): 57 | ob = OrderBook(_INSTRUMENT) 58 | 59 | _seed(ob, _INSTRUMENT) 60 | 61 | assert ob.topOfBook()[Side.BUY] == [5.0, 1.0] 62 | assert ob.topOfBook()[Side.SELL] == [5.5, 1.0] 63 | 64 | data = Order( 65 | volume=100.0, 66 | price=0.0, 67 | side=Order.Sides.SELL, 68 | instrument=_INSTRUMENT, 69 | order_type=Order.Types.LIMIT, 70 | ) 71 | ob.add(data) 72 | 73 | print(ob) 74 | print(ob.topOfBook()) 75 | assert ob.topOfBook() == {Side.BUY: [0.0, 0.0], Side.SELL: [0.0, 90.0]} 76 | print(ob.levels(3)) 77 | assert ob.levels(3) == { 78 | Side.BUY: [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], 79 | Side.SELL: [[0.0, 90.0], [5.5, 1.0], [6.0, 1.0]], 80 | } 81 | 82 | # def test_order_book_iter(self): 83 | # ob = OrderBook(Instrument('TEST'), 84 | # ExchangeType("")) 85 | 86 | # orders = [Order(10 + i, 87 | # 5, 88 | # Side.BUY, 89 | # Instrument('TEST'), 90 | # ExchangeType(""), 91 | # 0.0, 92 | # OrderType.LIMIT, 93 | # OrderFlag.NONE, 94 | # None) for i in range(100)] 95 | 96 | # for o in orders: # This causes a segfault 97 | # ob.add(o) 98 | 99 | # for o, op in zip(orders, ob): 100 | # assert o == op 101 | -------------------------------------------------------------------------------- /aat/tests/core/order_book/test_price_level.py: -------------------------------------------------------------------------------- 1 | from aat.core.order_book.price_level import _PriceLevel 2 | from aat.core.order_book.collector import _Collector 3 | 4 | 5 | class TestOrderBook: 6 | def test_price_level(self): 7 | # just instantiate, validate below 8 | pl = _PriceLevel(5.0, _Collector()) 9 | assert bool(pl) is False 10 | 11 | # def test_price_level_iter(self): 12 | # pl = _PriceLevel(5, _Collector()) 13 | # orders = [Order(10 + i, 5, Side.BUY, Instrument('TEST'), ExchangeType(""), 0.0, OrderType.LIMIT, OrderFlag.NONE, None) for i in range(2)] 14 | 15 | # for o in orders: # This causes a segfault 16 | # pl.add(o) 17 | 18 | # for o, op in zip(orders, pl): 19 | # assert o == op 20 | -------------------------------------------------------------------------------- /aat/tests/core/order_book/test_utils.py: -------------------------------------------------------------------------------- 1 | from aat.core.order_book.utils import _insort 2 | 3 | 4 | class TestUtils: 5 | def test_insort(self): 6 | a = [1, 2, 3] 7 | assert _insort(a, 1.5) 8 | assert a == [1, 1.5, 2, 3] 9 | assert _insort(a, 1.5) is False 10 | -------------------------------------------------------------------------------- /aat/tests/engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/engine/__init__.py -------------------------------------------------------------------------------- /aat/tests/engine/test_periodic.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from datetime import datetime 3 | from typing import Optional, Union, List 4 | 5 | from aat.engine.dispatch import Periodic 6 | from aat.engine.dispatch.periodic import PeriodicManagerMixin 7 | 8 | 9 | class TestMixin(PeriodicManagerMixin): 10 | def __init__(self, periodic: Union[List[Periodic], Periodic]): 11 | if isinstance(periodic, Periodic): 12 | periodic = [periodic] 13 | self._periodics = periodic 14 | 15 | 16 | class TestPeriodic: 17 | def create_periodic_mixin( 18 | self, second: Optional[int], minute: Optional[int], hour: Optional[int] 19 | ): 20 | async def noop(): 21 | pass 22 | 23 | return Periodic( 24 | asyncio.get_event_loop(), datetime.now(), noop, second, minute, hour 25 | ) 26 | 27 | def test_secondly_periodic(self): 28 | periodic = TestMixin(self.create_periodic_mixin(None, None, None)) 29 | assert periodic.periodicIntervals() == 1 30 | 31 | def test_minutely_periodic(self): 32 | periodic = TestMixin(self.create_periodic_mixin(5, None, None)) 33 | assert periodic.periodicIntervals() == 60 34 | 35 | def test_hourly_periodic(self): 36 | periodic = TestMixin(self.create_periodic_mixin(10, 2, None)) 37 | assert periodic.periodicIntervals() == 3600 38 | 39 | # no longer necessary 40 | # def test_removal_of_asterisk(self): 41 | # with pytest.raises(Exception): 42 | # periodic = TestMixin(self.create_periodic_mixin("*", "*", "*")) 43 | # periodic.periodicIntervals() 44 | -------------------------------------------------------------------------------- /aat/tests/exchange/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/exchange/__init__.py -------------------------------------------------------------------------------- /aat/tests/exchange/public/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/exchange/public/__init__.py -------------------------------------------------------------------------------- /aat/tests/exchange/public/test_ib_spread_reconstitute.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from unittest.mock import MagicMock 3 | from aat.core import Order, Instrument 4 | from aat.config import InstrumentType, Side 5 | 6 | leg1 = Instrument(name="LEG1", type=InstrumentType.FUTURE) 7 | leg2 = Instrument(name="LEG2", type=InstrumentType.FUTURE) 8 | spread = Instrument(name="LEG1_LEG2", type=InstrumentType.SPREAD, leg1=leg1, leg2=leg2) 9 | 10 | 11 | class Wrapper: 12 | ... 13 | 14 | 15 | sys.modules["ibapi"] = MagicMock() 16 | sys.modules["ibapi.client"] = MagicMock() 17 | sys.modules["ibapi.client"].EClient = object # type: ignore 18 | sys.modules["ibapi.commission_report"] = MagicMock() 19 | sys.modules["ibapi.contract"] = MagicMock() 20 | sys.modules["ibapi.execution"] = MagicMock() 21 | sys.modules["ibapi.order"] = MagicMock() 22 | sys.modules["ibapi.wrapper"] = MagicMock() 23 | sys.modules["ibapi.wrapper"].EWrapper = Wrapper # type: ignore 24 | 25 | from aat.exchange.public.ib.spread import SpreadReconstitute # noqa: E402 26 | 27 | 28 | class TestIBReconstituteSpread: 29 | def test_spread(self) -> None: 30 | sr = SpreadReconstitute() 31 | 32 | original_order = Order(10, 7, Side.BUY, spread) 33 | 34 | order1_l1 = Order(3, 10, Side.BUY, instrument=leg1) 35 | order1_l2 = Order(3, 2, Side.SELL, instrument=leg1) 36 | order1 = Order(3, 8, Side.BUY, instrument=spread) 37 | 38 | sr.push(order1_l1) 39 | 40 | assert sr.get(original_order) == None # noqa: E711 41 | 42 | sr.push(order1_l2) 43 | 44 | assert sr.get(original_order) == order1 45 | 46 | order2_l1 = Order(7, 11, Side.BUY, instrument=leg1) 47 | order2_l2 = Order(7, 3, Side.SELL, instrument=leg1) 48 | order2 = Order(7, 8, Side.SELL, instrument=spread) 49 | 50 | sr.push(order2_l1) 51 | 52 | assert sr.get(original_order) == None # noqa: E711 53 | 54 | sr.push(order2_l2) 55 | 56 | assert sr.get(original_order) == order2 57 | -------------------------------------------------------------------------------- /aat/tests/exchange/test_ib_race.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from typing import List, Any, AsyncGenerator, Optional 3 | from aat import Strategy 4 | from aat.config import EventType, InstrumentType, Side, TradingType 5 | from aat.core import ExchangeType, Event, Instrument, Trade, Order 6 | from aat.exchange import Exchange 7 | 8 | 9 | class Harness(Exchange): 10 | def __init__(self, trading_type: TradingType, verbose: bool) -> None: 11 | super().__init__(ExchangeType("testharness")) 12 | self._trading_type = trading_type 13 | self._verbose = verbose 14 | self._instrument = Instrument("Test.inst", InstrumentType.EQUITY) 15 | 16 | self._order: Optional[Order] = None 17 | self._done: bool = False 18 | 19 | async def instruments(self) -> List[Instrument]: 20 | """get list of available instruments""" 21 | return [self._instrument] 22 | 23 | async def connect(self) -> None: 24 | self._event = asyncio.Event() 25 | 26 | async def tick(self) -> AsyncGenerator[Any, Event]: # type: ignore[override] 27 | while True: 28 | if self._order: 29 | self._order.filled = self._order.volume 30 | t = Trade( 31 | self._order.volume, 32 | self._order.price, 33 | taker_order=self._order, 34 | maker_orders=[], 35 | ) 36 | t.my_order = self._order 37 | print("yielding trade") 38 | yield Event(type=EventType.TRADE, target=t) 39 | self._order = None 40 | self._event.set() 41 | yield Event( 42 | type=EventType.TRADE, 43 | target=Trade( 44 | 1, 45 | 1, 46 | taker_order=Order( 47 | 1, 48 | 1, 49 | Side.BUY, 50 | self._instrument, 51 | ExchangeType("testharness"), 52 | filled=1, 53 | ), 54 | maker_orders=[], 55 | ), 56 | ) 57 | if self._done: 58 | return 59 | 60 | async def newOrder(self, order: Order) -> bool: 61 | order.id = 1 62 | self._order = order 63 | print("waiting") 64 | await self._event.wait() 65 | self._done = True 66 | return True 67 | 68 | 69 | class TestStrategy(Strategy): 70 | def __init__(self, *args: Any, **kwargs: Any) -> None: 71 | super(TestStrategy, self).__init__(*args, **kwargs) 72 | self._receivedCount = 0 73 | self._order: Optional[Order] = None 74 | 75 | async def onTrade(self, event: Event) -> None: 76 | if not self._order: 77 | self._order = Order( 78 | 1, 1, Side.BUY, self.instruments()[0], ExchangeType("testharness") 79 | ) 80 | await self.newOrder(self._order) 81 | 82 | async def onTraded(self, order: Order) -> None: 83 | print("onTraded") 84 | 85 | async def onReceived(self, order: Order) -> None: 86 | print("onReceived") 87 | self._receivedCount += 1 88 | 89 | async def onExit(self, event: Event) -> None: 90 | assert self._receivedCount == 1 91 | print("all set!") 92 | 93 | 94 | if __name__ == "__main__": 95 | from aat import TradingEngine, parseConfig 96 | 97 | cfg = parseConfig( 98 | [ 99 | "--trading_type", 100 | "backtest", 101 | "--exchanges", 102 | "aat.tests.exchange.test_ib_race:Harness", 103 | "--strategies", 104 | "aat.tests.exchange.test_ib_race:TestStrategy", 105 | ] 106 | ) 107 | print(cfg) 108 | t = TradingEngine(**cfg) 109 | t.start() 110 | -------------------------------------------------------------------------------- /aat/tests/strategy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/strategy/__init__.py -------------------------------------------------------------------------------- /aat/tests/strategy/test_offline_calculations.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os.path 3 | from aat.common import _in_cpp 4 | 5 | 6 | class TestOfflineCalculations: 7 | def setup_class(self): 8 | self._argv = sys.argv 9 | 10 | def teardown_class(self): 11 | sys.argv = self._argv 12 | 13 | def test_calculations(self): 14 | from aat.strategy.calculations import main 15 | 16 | sys.argv = [ 17 | "aat.strategy.calculations", 18 | "--folder", 19 | os.path.join(os.path.dirname(__file__), "_aat_BACKTEST_test"), 20 | "--strategy", 21 | "MomentumStrategy-0", 22 | "--render", 23 | "False", 24 | ] 25 | 26 | if _in_cpp(): 27 | # TODO 28 | return 29 | main() 30 | -------------------------------------------------------------------------------- /aat/tests/strategy/test_strategies/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/tests/strategy/test_strategies/__init__.py -------------------------------------------------------------------------------- /aat/tests/strategy/test_strategies/test_cancel_all.py: -------------------------------------------------------------------------------- 1 | from aat import Strategy, Event, Order, OrderType, Side 2 | 3 | 4 | class TestCancelAll(Strategy): 5 | def __init__(self, *args, **kwargs) -> None: 6 | super(TestCancelAll, self).__init__(*args, **kwargs) 7 | self._count = 0 8 | 9 | async def onTrade(self, event: Event) -> None: 10 | if self._count < 5: 11 | await self.newOrder( 12 | Order( 13 | 1, 14 | 10000000, 15 | Side.SELL, 16 | self.instruments()[0], 17 | order_type=OrderType.LIMIT, 18 | ) 19 | ) 20 | self._count += 1 21 | assert len(self.orders()) == self._count 22 | else: 23 | await self.cancelAll() 24 | assert len(self.orders()) == 0 25 | 26 | 27 | if __name__ == "__main__": 28 | from aat import TradingEngine, parseConfig 29 | 30 | cfg = parseConfig( 31 | [ 32 | "--trading_type", 33 | "backtest", 34 | "--exchanges", 35 | "aat.exchange:SyntheticExchange,1,1000", 36 | "--strategies", 37 | "aat.tests.strategy.test_strategies.test_cancel_all::TestCancelAll", 38 | ] 39 | ) 40 | print(cfg) 41 | t = TradingEngine(**cfg) 42 | t.start() 43 | -------------------------------------------------------------------------------- /aat/tests/test_common.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | class TestCommon: 5 | def test_merge(self): 6 | from aat.common import _merge 7 | 8 | l1 = [ 9 | (1, datetime(2020, 1, 1)), 10 | (1, datetime(2020, 1, 2)), 11 | (2, datetime(2020, 1, 3)), 12 | (2, datetime(2020, 1, 4)), 13 | (3, datetime(2020, 1, 5)), 14 | (3, datetime(2020, 1, 6)), 15 | (4, datetime(2020, 1, 7)), 16 | (4, datetime(2020, 1, 8)), 17 | (3, datetime(2020, 1, 9)), 18 | (3, datetime(2020, 1, 20)), 19 | (2, datetime(2020, 1, 21)), 20 | (2, datetime(2020, 1, 22)), 21 | (1, datetime(2020, 1, 23)), 22 | (1, datetime(2020, 1, 24)), 23 | (0, datetime(2020, 1, 25)), 24 | (0, datetime(2020, 1, 26)), 25 | (-1, datetime(2020, 1, 27)), 26 | (-1, datetime(2020, 1, 28)), 27 | (0, datetime(2020, 1, 29)), 28 | (0, datetime(2020, 1, 30)), 29 | ] 30 | 31 | l2 = [ 32 | (1, datetime(2020, 1, 5)), 33 | (1, datetime(2020, 1, 6)), 34 | (1, datetime(2020, 1, 7)), 35 | (1, datetime(2020, 1, 8)), 36 | (-2, datetime(2020, 1, 9)), 37 | (-2, datetime(2020, 1, 10)), 38 | (-2, datetime(2020, 1, 11)), 39 | (-2, datetime(2020, 1, 12)), 40 | (-2, datetime(2020, 1, 13)), 41 | (-2, datetime(2020, 1, 14)), 42 | (2, datetime(2020, 1, 22)), 43 | (2, datetime(2020, 1, 23)), 44 | (2, datetime(2020, 1, 24)), 45 | (2, datetime(2020, 1, 25)), 46 | (2, datetime(2020, 1, 26)), 47 | (1, datetime(2020, 1, 29)), 48 | (1, datetime(2020, 1, 30)), 49 | ] 50 | 51 | combined = [ 52 | (1, datetime(2020, 1, 1)), 53 | (1, datetime(2020, 1, 2)), 54 | (2, datetime(2020, 1, 3)), 55 | (2, datetime(2020, 1, 4)), 56 | (4, datetime(2020, 1, 5)), 57 | (4, datetime(2020, 1, 6)), 58 | (5, datetime(2020, 1, 7)), 59 | (5, datetime(2020, 1, 8)), 60 | (1, datetime(2020, 1, 9)), 61 | (1, datetime(2020, 1, 10)), 62 | (1, datetime(2020, 1, 11)), 63 | (1, datetime(2020, 1, 12)), 64 | (1, datetime(2020, 1, 13)), 65 | (1, datetime(2020, 1, 14)), 66 | (1, datetime(2020, 1, 20)), 67 | (0, datetime(2020, 1, 21)), 68 | (4, datetime(2020, 1, 22)), 69 | (3, datetime(2020, 1, 23)), 70 | (3, datetime(2020, 1, 24)), 71 | (2, datetime(2020, 1, 25)), 72 | (2, datetime(2020, 1, 26)), 73 | (1, datetime(2020, 1, 27)), 74 | (1, datetime(2020, 1, 28)), 75 | (1, datetime(2020, 1, 29)), 76 | (1, datetime(2020, 1, 30)), 77 | ] 78 | 79 | ret = _merge(l1, l2) 80 | 81 | assert ret == combined 82 | -------------------------------------------------------------------------------- /aat/ui/__init__.py: -------------------------------------------------------------------------------- 1 | from .application import ServerApplication # noqa: F401 2 | -------------------------------------------------------------------------------- /aat/ui/application.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import tornado.web 3 | from typing import Any, List, Optional 4 | 5 | # from tornado_sqlalchemy_login.handlers import LoginHandler, LogoutHandler, RegisterHandler, APIKeyHandler 6 | # from tornado_sqlalchemy_login import SQLAlchemyLoginManagerOptions, SQLAlchemyLoginManager 7 | 8 | 9 | class ServerApplication(tornado.web.Application): 10 | def __init__( 11 | self, 12 | basepath: str = "/", 13 | wspath: str = "/api/v1/ws", 14 | handlers: Optional[List[Any]] = None, 15 | debug: bool = True, 16 | cookie_secret: Optional[str] = None, 17 | *args: Any, 18 | **kwargs: Any, 19 | ) -> None: 20 | handlers = handlers or [] 21 | 22 | logging.getLogger("tornado.access").disabled = False 23 | 24 | # SQLAlchemy Login Configuration 25 | # sqlalchemy_login_config = SQLAlchemyLoginManagerOptions( 26 | # port=port, 27 | # UserClass=User, 28 | # APIKeyClass=APIKey, 29 | # ) 30 | 31 | settings = { 32 | "cookie_secret": cookie_secret, 33 | "login_url": basepath + "login", 34 | "debug": debug, 35 | } 36 | 37 | settings.update(**kwargs) 38 | 39 | super(ServerApplication, self).__init__(handlers, *args, **kwargs) 40 | -------------------------------------------------------------------------------- /aat/ui/handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/aat/ui/handlers/__init__.py -------------------------------------------------------------------------------- /config/synthetic.cfg: -------------------------------------------------------------------------------- 1 | [general] 2 | verbose=0 3 | trading_type=simulation 4 | api=1 5 | load_accounts=1 6 | 7 | [exchange] 8 | exchanges= 9 | aat.exchange:SyntheticExchange,3,10000,True 10 | 11 | [strategy] 12 | strategies = 13 | aat.strategy.sample:SellPlusPercentStrategy,3 14 | -------------------------------------------------------------------------------- /cpplint.cfg: -------------------------------------------------------------------------------- 1 | filter=-legal/copyright,-build/include_order,-runtime/indentation_namespace,-readability/todo,-build/namespaces,-runtime/references -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = aat 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | 2 | ```eval_rst 3 | .. automodule:: aat 4 | :members: 5 | :undoc-members: 6 | :show-inheritance: 7 | 8 | .. automodule:: backtest 9 | :members: 10 | :undoc-members: 11 | :show-inheritance: 12 | 13 | .. automodule:: callback 14 | :members: 15 | :undoc-members: 16 | :show-inheritance: 17 | 18 | .. automodule:: config 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | .. automodule:: data_source 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | .. automodule:: define 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | 33 | .. automodule:: enums 34 | :members: 35 | :undoc-members: 36 | :show-inheritance: 37 | 38 | .. automodule:: exchange 39 | :members: 40 | :undoc-members: 41 | :show-inheritance: 42 | 43 | .. automodule:: exchanges 44 | :members: 45 | :undoc-members: 46 | :show-inheritance: 47 | 48 | .. automodule:: execution 49 | :members: 50 | :undoc-members: 51 | :show-inheritance: 52 | 53 | .. automodule:: logging 54 | :members: 55 | :undoc-members: 56 | :show-inheritance: 57 | 58 | .. automodule:: market_data 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | .. automodule:: order_book 64 | :members: 65 | :undoc-members: 66 | :show-inheritance: 67 | 68 | .. automodule:: order_entry 69 | :members: 70 | :undoc-members: 71 | :show-inheritance: 72 | 73 | .. automodule:: parser 74 | :members: 75 | :undoc-members: 76 | :show-inheritance: 77 | 78 | .. automodule:: persistence 79 | :members: 80 | :undoc-members: 81 | :show-inheritance: 82 | 83 | .. automodule:: query 84 | :members: 85 | :undoc-members: 86 | :show-inheritance: 87 | 88 | .. automodule:: risk 89 | :members: 90 | :undoc-members: 91 | :show-inheritance: 92 | 93 | .. automodule:: strategies 94 | :members: 95 | :undoc-members: 96 | :show-inheritance: 97 | 98 | .. automodule:: strategy 99 | :members: 100 | :undoc-members: 101 | :show-inheritance: 102 | 103 | .. automodule:: structs 104 | :members: 105 | :undoc-members: 106 | :show-inheritance: 107 | 108 | .. automodule:: trading 109 | :members: 110 | :undoc-members: 111 | :show-inheritance: 112 | 113 | .. automodule:: ui 114 | :members: 115 | :undoc-members: 116 | :show-inheritance: 117 | 118 | .. automodule:: utils 119 | :members: 120 | :undoc-members: 121 | :show-inheritance: 122 | ``` 123 | -------------------------------------------------------------------------------- /docs/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/docs/img/icon.png -------------------------------------------------------------------------------- /docs/img/nem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/docs/img/nem.png -------------------------------------------------------------------------------- /docs/img/orderbook.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/docs/img/orderbook.gif -------------------------------------------------------------------------------- /docs/img/orderbook2.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a6314e0b2abd2488ad2ecc3806c4b439c9be6bd9284ff6711e38e658c3435fa9 3 | size 4130571 4 | -------------------------------------------------------------------------------- /docs/img/rethist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/docs/img/rethist.png -------------------------------------------------------------------------------- /docs/img/tearsheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AsyncAlgoTrading/aat/1a94b89902eb06ad6313d0cd96fd21c11378c21b/docs/img/tearsheet.png -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=aat 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | # Minimum requirements for the build system to execute. 3 | requires = ["setuptools<=60", "wheel", "numpy"] 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore=E203, W503 3 | max-line-length=250 4 | per-file-ignores= 5 | aat/core/__init__.py:F401, F403 6 | 7 | [mypy] 8 | python_version = 3.7 9 | disallow_untyped_calls = True 10 | disallow_untyped_defs = True 11 | show_column_numbers = True 12 | follow_imports = silent 13 | ignore_missing_imports = True 14 | warn_unused_configs = True 15 | 16 | [mypy-aat.tests.*] 17 | ignore_errors = True 18 | 19 | [bumpversion] 20 | current_version = 0.1.0 21 | commit = True 22 | tag = False 23 | 24 | [bumpversion:file:aat/__init__.py] 25 | search = __version__ = "{current_version}" 26 | replace = __version__ = "{new_version}" 27 | 28 | [bumpversion:file:setup.py] 29 | search = version="{current_version}" 30 | replace = version="{new_version}" 31 | 32 | [bumpversion:file:docs/conf.py] 33 | search = version = "{current_version}" 34 | replace = version = "{new_version}" 35 | --------------------------------------------------------------------------------