├── .gitattributes ├── .github └── workflows │ └── shellcheck.yml ├── .gitignore ├── CMakeLists.txt ├── InputInquiryConnector.cpp ├── InputMarketConnector.cpp ├── InputPriceConnector.cpp ├── InputTradeConnector.cpp ├── LICENSE ├── README.md ├── data ├── inquiries.txt ├── marketdata.txt ├── prices.txt └── trades.txt ├── headers ├── algoexecutionservice.hpp ├── algostreamingservice.hpp ├── executionservice.hpp ├── fileconnector.hpp ├── guiservice.hpp ├── historicaldataservice.hpp ├── inquiryservice.hpp ├── marketdataservice.hpp ├── positionservice.hpp ├── pricingservice.hpp ├── products.hpp ├── riskservice.hpp ├── soa.hpp ├── streamingservice.hpp ├── tradebookingservice.hpp └── utils.hpp ├── imgs ├── clientserver.png └── serviceflow.png ├── main.cpp ├── res ├── allinquiries.txt ├── executions.txt ├── gui.txt ├── positions.txt ├── risk.txt └── streaming.txt └── run.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "multithread" ] 6 | pull_request: 7 | branches: [ "multithread" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | # run shellcheck 17 | - name: ShellCheck 18 | uses: ludeeus/action-shellcheck@2.0.0 19 | - name: lintmarkdown 20 | if: success() 21 | uses: tomwhross/write-good-action@v1.6 22 | with: 23 | message: | 24 | Finish shell checking. 25 | repo-token: ${{ secrets.GITHUB_TOKEN }} 26 | repo-token-user-login: 'github-actions[bot]' 27 | allow-repeats: false 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | 3 | # ignore all files in the build directory 4 | /build 5 | 6 | # Prerequisites 7 | *.d 8 | 9 | # Compiled Object files 10 | *.slo 11 | *.lo 12 | *.o 13 | *.obj 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.2) 2 | project(tradingsystem) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED True) 6 | set(CMAKE_CXX_EXTENSIONS OFF) 7 | 8 | # If on windows, uncomment and set the path to the boost library 9 | # set(CMAKE_PREFIX_PATH "D:/boost_1_82_0") 10 | 11 | # Find the Boost library 12 | find_package(Boost REQUIRED) 13 | include_directories(${Boost_INCLUDE_DIRS}) 14 | 15 | set(CPACK_PROJECT_NAME ${PROJECT_NAME}) 16 | set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) 17 | 18 | # six servers/processes: input (price, orderbook, trade, inquiry) and output (streaming, execution) 19 | add_executable(server main.cpp) 20 | target_link_libraries(server ${Boost_LIBRARIES}) 21 | 22 | # four main clients: price, orderbook, trade, and inquiry 23 | add_executable(price InputPriceConnector.cpp) 24 | add_executable(market InputMarketConnector.cpp) 25 | add_executable(trade InputTradeConnector.cpp) 26 | add_executable(inquiry InputInquiryConnector.cpp) 27 | -------------------------------------------------------------------------------- /InputInquiryConnector.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/fileconnector.hpp" 2 | 3 | int main(){ 4 | const string inquiryPath = "../data/inquiries.txt"; 5 | const string host = "localhost"; 6 | const string port = "3003"; 7 | FileConnector inquiryFileConnector(inquiryPath, host, port); 8 | inquiryFileConnector.Subscribe(); 9 | } -------------------------------------------------------------------------------- /InputMarketConnector.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/fileconnector.hpp" 2 | 3 | int main(){ 4 | const string marketDataPath = "../data/marketdata.txt"; 5 | const string host = "localhost"; 6 | const string port = "3001"; 7 | FileConnector marketFileConnector(marketDataPath, host, port); 8 | marketFileConnector.Subscribe(); 9 | } -------------------------------------------------------------------------------- /InputPriceConnector.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/fileconnector.hpp" 2 | 3 | int main(){ 4 | const string pricePath = "../data/prices.txt"; 5 | const string host = "localhost"; 6 | const string port = "3000"; 7 | FileConnector priceFileConnector(pricePath, host, port); 8 | priceFileConnector.Subscribe(); 9 | } -------------------------------------------------------------------------------- /InputTradeConnector.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/fileconnector.hpp" 2 | 3 | int main(){ 4 | const string tradePath = "../data/trades.txt"; 5 | const string host = "localhost"; 6 | const string port = "3002"; 7 | FileConnector tradeFileConnector(tradePath, host, port); 8 | tradeFileConnector.Subscribe(); 9 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trading-System 2 | [![C/C++ CI](https://github.com/bradleyboyuyang/Trading-System/actions/workflows/shellcheck.yml/badge.svg)](https://github.com/bradleyboyuyang/Trading-System/actions/workflows/shellcheck.yml) 3 | 4 | An asynchronous and low-latency trading system designed under service-oriented architecture (SOA). 5 | 6 | The project gives an example of a trading system for seven US Treasury securities, with real-time price and orderbook flows, data streaming, user inquiries, algorithm order execution, risk monitor, logging modules, etc. 7 | 8 | ## Service Architecture 9 | 10 | 11 | 12 | Data flow into the trading system through `Subscribe()` that calls `OnMessage()`, transmits among different service components through `ProcessAdd()`, and flows out of the system through `Publish()`. 13 | 14 | ### Connector 15 | 16 | Connectors can be subscribe-only or publish-only, or both. An input file connector can both `Subscribe` data from outside data sources and `Publish` data to a socket with a specified host and port. An inbound connector can then subscribe data from the socket and flow data into the trading system by calling `Service.OnMessage()`, or publish data to outside source using `Service.Publish()`. 17 | 18 | ## Client-Server Pattern 19 | 20 | 21 | The whole system follows a client-server pattern through asynchronous I/O. The program consists of four client programs that subscribe to external data and publish to TCP sockets, and a main server program running six servers simultaneously using multi-threading. Data flows into the trading system through connectors from connectivity source (e.g. a socket, database, etc). 22 | 23 | ### Socket-based Communication 24 | Communication between different components is based on sockets to ensure real-time, low-latency and high throughput. Six servers are running simultaneously, with four (price, market, trade, inquiry server) listening to TCP sockets from `localhost:3000` to `localhost:3004` and flow data into the system, and two (steaming and execution server) publishing data to TCP sockets `localhost:3004` and `localhost:3005`. 25 | 26 | 27 | 28 | ## Deployment 29 | The instruction to run the system on a Linux machine is as follows (tested on Ubuntu 22.04). Before running the system, make sure you have installed `boost` and `cmake` tools, and check firewall settings to open local ports `3000-3005` for TCP sockets (important!) 30 | ```bash 31 | # install boost and cmake tools 32 | sudo apt-get update 33 | sudo apt-get install libboost-all-dev 34 | sudo apt install cmake 35 | ``` 36 | 37 | The direct way is simply run the bash script `run.sh` in the root directory. It starts the system and the servers in the foreground, then start the four client programs in the background. 38 | ```bash 39 | chmod +x run.sh 40 | ./run.sh 41 | ``` 42 | 43 | Or one can manually build and run the system. 44 | ```bash 45 | # compile and start servers 46 | mkdir build 47 | cd build 48 | cmake .. 49 | make 50 | ./server 51 | 52 | # start clients in separate terminals 53 | ./price 54 | ./market 55 | ./trade 56 | ./inquiry 57 | ``` 58 | 59 | 60 | ## Scripts 61 | - Main program 62 | - `InputPriceConnector`: an input connector that subscribes external price data and publishes to TCP socket `localhost:3000` 63 | - `InputMarketDataConnector`: an input connector that subscribes external orderbook data and publishes to TCP socket `localhost:3001` 64 | - `InputTradeConnector`: an input connector that subscribes external trade data and publishes to TCP socket `localhost:3002` 65 | - `InputInquiryConnector`: an input connector that subscribes external user inquiry data and publishes to TCP socket `localhost:3003` 66 | - `StreamOutputConnector`: an outbound connector that subscribes streaming flow data from TCP socket `localhost:3000` and publishes to `localhost:3004` 67 | - `ExecutionOutputConnector`: an outbound connector that subscribes execution data from TCP socket `localhost:3001` and publishes to `localhost:3005` 68 | - `main`: connect different services, attach six threads to six servers, and start listening to TCP sockets `localhost:3000-3005` 69 | 70 | 71 | - Service components 72 | 73 | - `pricingservice`: read in price data from the socket to the system through an inbound connector 74 | - `algostreamingservice`: listen to pricing service, flow in data of `Price` and generate data of `AlgoStream` 75 | - `streamingservice`: listen to algo streaming service, flow in data of `AlgoStream` and record bid/ask prices into `priceStream`, publish streams via socket in a separate process 76 | - `guiservice`: a GUI component that listens to streaming prices that should be throttled with a 300 millisecond throttle., register a service listener on the pricing service and output the updates with a timestamp with millisecond precision to a file `gui.txt`. 77 | - `marketdataservice`: read in orderbook data from the socket to the system through an inbound connector 78 | - `algoexecutionservice`: listen to market data service, flow in data of `Orderbook` and turn into execution data `AlgoExecution` 79 | - `executionservice`: listen to algo execution service, flow in data of `AlgoExecution` and record order information into `ExecutionOrder`, publish order executions via socket in a separate process 80 | - `tradebookingservice`: read in trade data, listen to execution service at the same time, flow in `ExecutionOrder` and turn in trade data of type `Trade` 81 | - `positionservice`: listen to trade booking service, flow in `Trade` data and turn into `Position` 82 | - `riskservice`: listen to position service, flow in `Position` data and calculate corresponding position risks, such as `PV01`. 83 | - `inquiryservice`: read in user inquiry data, interact with connectors and deal with inquiries 84 | 85 | - Other components 86 | - `products`: define the class for the trading products, which can be treasury bonds, interest rate swaps, future, commodity, or any user-defined product object 87 | - `historicaldataservice`: a last-step service that listens to position service, risk service, execution service, streaming service, and inquiry service; persist objects it receives and saves the data into a database (usually data centers, KDB database, etc) 88 | - `utils`: time displayer, data generator, and risk calculator 89 | 90 | - Data and results 91 | 92 | - `data`: data source for price data, orderbook updates, user inquiries, and trade data, can be replaced by other connectivity sources (a database, socket, etc) 93 | 94 | - `res`: results published by the system, including processed queries, executed orders, positions, risk monitor, data streaming, and GUI output. 95 | 96 | ## Note 97 | The trading system is designed to be scalable, extensible, and maintainable. Multi-threading and asynchronous programming ensure low-latency, high throughput, and high-performance. The system is also designed to be modularized, with each service component being independent and loosely coupled with others. New trading products can be added into `products.hpp`, new services, listeners, and connectors can all be easily added and integrated into the whole system. 98 | -------------------------------------------------------------------------------- /data/inquiries.txt: -------------------------------------------------------------------------------- 1 | OM595MUHI7N7,9128283H1,BUY,1000000,99-285,RECEIVED 2 | JZWQP3UVQYU3,9128283H1,SELL,2000000,100-031,RECEIVED 3 | XXQTD3WHQHRB,9128283H1,BUY,3000000,99-241,RECEIVED 4 | 4LSMSF9SULYJ,9128283H1,SELL,4000000,100-161,RECEIVED 5 | P8FV6PF4M5DF,9128283H1,BUY,5000000,99-237,RECEIVED 6 | OQXU746RPYTY,9128283H1,SELL,1000000,100-310,RECEIVED 7 | U36444O9CJ5I,9128283H1,BUY,2000000,99-283,RECEIVED 8 | 802B5G696J4T,9128283H1,SELL,3000000,100-147,RECEIVED 9 | NALTOERYXX3I,9128283H1,BUY,4000000,99-067,RECEIVED 10 | H7R9QCRECA5X,9128283H1,SELL,5000000,100-265,RECEIVED 11 | 6C7SVBLZLMSP,9128283L2,BUY,1000000,99-185,RECEIVED 12 | HJOEWRCTY424,9128283L2,SELL,2000000,100-236,RECEIVED 13 | WTY9J46PWTH7,9128283L2,BUY,3000000,99-312,RECEIVED 14 | 4J655YVMXZH9,9128283L2,SELL,4000000,100-060,RECEIVED 15 | QT24D4OAE2ZX,9128283L2,BUY,5000000,99-031,RECEIVED 16 | 653IF0QJJW55,9128283L2,SELL,1000000,100-247,RECEIVED 17 | BGR8F8Y5I0PV,9128283L2,BUY,2000000,99-163,RECEIVED 18 | 5D5ZVKW26F0L,9128283L2,SELL,3000000,100-301,RECEIVED 19 | GQKF3PKE5S2K,9128283L2,BUY,4000000,99-246,RECEIVED 20 | G05YHUU2NF1J,9128283L2,SELL,5000000,100-287,RECEIVED 21 | 0D16TH7P8R4R,912828M80,BUY,1000000,99-300,RECEIVED 22 | H552XNND48SL,912828M80,SELL,2000000,100-256,RECEIVED 23 | JM3MH4LXH2JA,912828M80,BUY,3000000,99-243,RECEIVED 24 | KQFSYKJF54X2,912828M80,SELL,4000000,100-10+,RECEIVED 25 | 70VB9NCSPFUN,912828M80,BUY,5000000,99-13+,RECEIVED 26 | ZGKHIK72QNAO,912828M80,SELL,1000000,100-14+,RECEIVED 27 | NTJSDHALHLC6,912828M80,BUY,2000000,99-013,RECEIVED 28 | 95EE0910P2XN,912828M80,SELL,3000000,100-120,RECEIVED 29 | M4QS70HUAG2N,912828M80,BUY,4000000,99-217,RECEIVED 30 | DS8VE1H36WH6,912828M80,SELL,5000000,100-14+,RECEIVED 31 | 5J6AL3XNO3FV,9128283J7,BUY,1000000,99-260,RECEIVED 32 | 4C6UTOHMH6HB,9128283J7,SELL,2000000,100-241,RECEIVED 33 | 7FETBBZWUMMV,9128283J7,BUY,3000000,99-052,RECEIVED 34 | 5JI93ELNQRHZ,9128283J7,SELL,4000000,100-247,RECEIVED 35 | FYMCKJ37YH0P,9128283J7,BUY,5000000,99-076,RECEIVED 36 | TGL327ZN7XDQ,9128283J7,SELL,1000000,100-097,RECEIVED 37 | BYDIPUHK8JDT,9128283J7,BUY,2000000,99-026,RECEIVED 38 | JG0HE1NNXOQF,9128283J7,SELL,3000000,100-176,RECEIVED 39 | C52J3FPED2WI,9128283J7,BUY,4000000,99-293,RECEIVED 40 | WUJKDWDWCUU6,9128283J7,SELL,5000000,100-111,RECEIVED 41 | BHT8LKNX55W8,9128283F5,BUY,1000000,99-303,RECEIVED 42 | 1LNENZDZTWKN,9128283F5,SELL,2000000,100-273,RECEIVED 43 | SDZK79RY606S,9128283F5,BUY,3000000,99-083,RECEIVED 44 | K9P6FMEG7HAB,9128283F5,SELL,4000000,100-17+,RECEIVED 45 | H3AQZAT7OTSB,9128283F5,BUY,5000000,99-051,RECEIVED 46 | 2ZA9ZW10MQ61,9128283F5,SELL,1000000,100-271,RECEIVED 47 | SKX0INBZR15Q,9128283F5,BUY,2000000,99-313,RECEIVED 48 | SZYGS67A5XJK,9128283F5,SELL,3000000,100-216,RECEIVED 49 | A0KW7QDZRAZ9,9128283F5,BUY,4000000,99-01+,RECEIVED 50 | XQOO8TF08TWG,9128283F5,SELL,5000000,100-173,RECEIVED 51 | Z3RKHQ4R753E,912810TW8,BUY,1000000,99-001,RECEIVED 52 | VGD2QTR3ZV87,912810TW8,SELL,2000000,100-065,RECEIVED 53 | 537DW3UBN1VK,912810TW8,BUY,3000000,99-156,RECEIVED 54 | RFRYKUSWA6EG,912810TW8,SELL,4000000,100-233,RECEIVED 55 | F6ZE17M6A90M,912810TW8,BUY,5000000,99-227,RECEIVED 56 | DUXGV802ORH8,912810TW8,SELL,1000000,100-147,RECEIVED 57 | L9KBVZ7A5M5M,912810TW8,BUY,2000000,99-07+,RECEIVED 58 | UR9K097TJ49U,912810TW8,SELL,3000000,100-002,RECEIVED 59 | T9WHGT5HJ6SE,912810TW8,BUY,4000000,99-292,RECEIVED 60 | 5F5Q2ACC11CH,912810TW8,SELL,5000000,100-297,RECEIVED 61 | AJR9403XQGUM,912810RZ3,BUY,1000000,99-305,RECEIVED 62 | 9F4SLWNQSSWA,912810RZ3,SELL,2000000,100-035,RECEIVED 63 | IP2ZQEGHY7QI,912810RZ3,BUY,3000000,99-027,RECEIVED 64 | OUVUAPGZKKS6,912810RZ3,SELL,4000000,100-217,RECEIVED 65 | XVC539FLYHK4,912810RZ3,BUY,5000000,99-207,RECEIVED 66 | BGLPOC7S622G,912810RZ3,SELL,1000000,100-287,RECEIVED 67 | 7IF8JNUWY611,912810RZ3,BUY,2000000,99-097,RECEIVED 68 | VGM9D6E4NZUR,912810RZ3,SELL,3000000,100-31+,RECEIVED 69 | B1ZXK1T7ZPVI,912810RZ3,BUY,4000000,99-032,RECEIVED 70 | CPERCVS7RVX4,912810RZ3,SELL,5000000,100-133,RECEIVED 71 | -------------------------------------------------------------------------------- /data/trades.txt: -------------------------------------------------------------------------------- 1 | 9128283H1,JY9757ACX1EV,99-285,TRSY1,1000000,BUY 2 | 9128283H1,E7NMOIGSZ8FX,100-031,TRSY2,2000000,SELL 3 | 9128283H1,QQQVJJ5IIU53,99-241,TRSY3,3000000,BUY 4 | 9128283H1,1GVYX99BWDEK,100-161,TRSY1,4000000,SELL 5 | 9128283H1,BUTAJ889EE4D,99-237,TRSY2,5000000,BUY 6 | 9128283H1,X9WFKHYLDT0R,100-310,TRSY3,1000000,SELL 7 | 9128283H1,I92F2WFE7OOQ,99-283,TRSY1,2000000,BUY 8 | 9128283H1,WCFBQJOO80JS,100-147,TRSY2,3000000,SELL 9 | 9128283H1,YIUBRA2AZL52,99-067,TRSY3,4000000,BUY 10 | 9128283H1,X0GKPKQ1X5SN,100-265,TRSY1,5000000,SELL 11 | 9128283L2,4XRDXBLB9V3G,99-185,TRSY1,1000000,BUY 12 | 9128283L2,5L65MBNKS3KH,100-236,TRSY2,2000000,SELL 13 | 9128283L2,3BI0GB4LOBE1,99-312,TRSY3,3000000,BUY 14 | 9128283L2,2FDBBG8W1E14,100-060,TRSY1,4000000,SELL 15 | 9128283L2,Q4OYN8FRZDRG,99-031,TRSY2,5000000,BUY 16 | 9128283L2,4BHSNBUPQ7H1,100-247,TRSY3,1000000,SELL 17 | 9128283L2,3PEK3FO9KS77,99-163,TRSY1,2000000,BUY 18 | 9128283L2,H2EGGMC0X9T0,100-301,TRSY2,3000000,SELL 19 | 9128283L2,K36BQNSTS6TB,99-246,TRSY3,4000000,BUY 20 | 9128283L2,2YL268P3R4KN,100-287,TRSY1,5000000,SELL 21 | 912828M80,QCN3MW4MZAXP,99-300,TRSY1,1000000,BUY 22 | 912828M80,D6Y5C8GU6HWS,100-256,TRSY2,2000000,SELL 23 | 912828M80,62WX6W0C8NFA,99-243,TRSY3,3000000,BUY 24 | 912828M80,KZXZ9AP2G37T,100-10+,TRSY1,4000000,SELL 25 | 912828M80,B43X10Q722KO,99-13+,TRSY2,5000000,BUY 26 | 912828M80,YK06OVXOVUNK,100-14+,TRSY3,1000000,SELL 27 | 912828M80,4S3LWQUNUX1C,99-013,TRSY1,2000000,BUY 28 | 912828M80,D7ZV9KJ7KZTO,100-120,TRSY2,3000000,SELL 29 | 912828M80,BQCM0GM58P6K,99-217,TRSY3,4000000,BUY 30 | 912828M80,G08QD927G225,100-14+,TRSY1,5000000,SELL 31 | 9128283J7,22C2HLRSCJES,99-260,TRSY1,1000000,BUY 32 | 9128283J7,ZHX86JSMZGDD,100-241,TRSY2,2000000,SELL 33 | 9128283J7,5VK1D26F4YY2,99-052,TRSY3,3000000,BUY 34 | 9128283J7,KPUCOP4O6ICC,100-247,TRSY1,4000000,SELL 35 | 9128283J7,1KF118U6JU8X,99-076,TRSY2,5000000,BUY 36 | 9128283J7,WUCHTAJDFT54,100-097,TRSY3,1000000,SELL 37 | 9128283J7,I9847KX94CQL,99-026,TRSY1,2000000,BUY 38 | 9128283J7,0K8JEWGQQ97J,100-176,TRSY2,3000000,SELL 39 | 9128283J7,ZQCFKHZI7NNE,99-293,TRSY3,4000000,BUY 40 | 9128283J7,7K3RCTDCT1B7,100-111,TRSY1,5000000,SELL 41 | 9128283F5,XSYNHLNGCZBC,99-303,TRSY1,1000000,BUY 42 | 9128283F5,XAUKXXEKHIC9,100-273,TRSY2,2000000,SELL 43 | 9128283F5,R5LL6X83566M,99-083,TRSY3,3000000,BUY 44 | 9128283F5,ST2K9EWM466I,100-17+,TRSY1,4000000,SELL 45 | 9128283F5,4KI1IABAFCB1,99-051,TRSY2,5000000,BUY 46 | 9128283F5,PJKA67WE0FEP,100-271,TRSY3,1000000,SELL 47 | 9128283F5,9QBTWXRGYAYG,99-313,TRSY1,2000000,BUY 48 | 9128283F5,K96G1HX7HIH3,100-216,TRSY2,3000000,SELL 49 | 9128283F5,PEH59VVILMRX,99-01+,TRSY3,4000000,BUY 50 | 9128283F5,0JEYTCUT119I,100-173,TRSY1,5000000,SELL 51 | 912810TW8,Y7PV57YA1VFA,99-001,TRSY1,1000000,BUY 52 | 912810TW8,6Q87TF5TYZ77,100-065,TRSY2,2000000,SELL 53 | 912810TW8,R1H8IQQHDGSY,99-156,TRSY3,3000000,BUY 54 | 912810TW8,3R8424U9UIG3,100-233,TRSY1,4000000,SELL 55 | 912810TW8,X1WC03Z7LGF3,99-227,TRSY2,5000000,BUY 56 | 912810TW8,NMKGITFLK352,100-147,TRSY3,1000000,SELL 57 | 912810TW8,7FBIDSLQ9Y2A,99-07+,TRSY1,2000000,BUY 58 | 912810TW8,H2X2YD6LZ62X,100-002,TRSY2,3000000,SELL 59 | 912810TW8,ZHIZK328ITQB,99-292,TRSY3,4000000,BUY 60 | 912810TW8,1R1BPKL72IP0,100-297,TRSY1,5000000,SELL 61 | 912810RZ3,BBMQIONXLLDL,99-305,TRSY1,1000000,BUY 62 | 912810RZ3,OVTMOZXQRZ1W,100-035,TRSY2,2000000,SELL 63 | 912810RZ3,J2JL091CKNII,99-027,TRSY3,3000000,BUY 64 | 912810RZ3,R6WS79EW4NIS,100-217,TRSY1,4000000,SELL 65 | 912810RZ3,3WYABZ6U1QVI,99-207,TRSY2,5000000,BUY 66 | 912810RZ3,FWUF0SYREUKM,100-287,TRSY3,1000000,SELL 67 | 912810RZ3,JEI3HGVKSUU3,99-097,TRSY1,2000000,BUY 68 | 912810RZ3,9HXR789M5J15,100-31+,TRSY2,3000000,SELL 69 | 912810RZ3,BFCQPCCO6ARO,99-032,TRSY3,4000000,BUY 70 | 912810RZ3,6NOZXZ26GGD3,100-133,TRSY1,5000000,SELL 71 | -------------------------------------------------------------------------------- /headers/algoexecutionservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * algoexecution.hpp 3 | * Defines the data types and Service for algo execution. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | #ifndef ALGOEXECUTION_SERVICE_HPP 8 | #define ALGOEXECUTION_SERVICE_HPP 9 | 10 | #include 11 | #include "soa.hpp" 12 | #include "marketdataservice.hpp" 13 | #include "utils.hpp" 14 | 15 | enum OrderType { FOK, IOC, MARKET, LIMIT, STOP }; 16 | 17 | enum Market { BROKERTEC, ESPEED, CME }; 18 | 19 | /** 20 | * An execution order that can be placed on an exchange. 21 | * Type T is the product type. 22 | */ 23 | template 24 | class ExecutionOrder 25 | { 26 | 27 | public: 28 | 29 | // ctor for an order 30 | ExecutionOrder() = default; // needed for map data structure later 31 | ExecutionOrder(const T &_product, PricingSide _side, string _orderId, OrderType _orderType, double _price, double _visibleQuantity, double _hiddenQuantity, string _parentOrderId, bool _isChildOrder); 32 | 33 | // Get the product 34 | const T& GetProduct() const; 35 | 36 | // Get the pricing side 37 | PricingSide GetSide() const; 38 | 39 | // Get the order ID 40 | const string& GetOrderId() const; 41 | 42 | // Get the order type on this order 43 | OrderType GetOrderType() const; 44 | 45 | // Get the price on this order 46 | double GetPrice() const; 47 | 48 | // Get the visible quantity on this order 49 | long GetVisibleQuantity() const; 50 | 51 | // Get the hidden quantity 52 | long GetHiddenQuantity() const; 53 | 54 | // Get the parent order ID 55 | const string& GetParentOrderId() const; 56 | 57 | // Is child order? 58 | bool IsChildOrder() const; 59 | 60 | // object printer 61 | template 62 | friend ostream& operator<<(ostream& os, const ExecutionOrder& order); 63 | 64 | private: 65 | T product; 66 | PricingSide side; 67 | string orderId; 68 | OrderType orderType; 69 | double price; 70 | long visibleQuantity; 71 | long hiddenQuantity; 72 | string parentOrderId; 73 | bool isChildOrder; 74 | 75 | }; 76 | 77 | template 78 | ExecutionOrder::ExecutionOrder(const T &_product, PricingSide _side, string _orderId, OrderType _orderType, double _price, double _visibleQuantity, double _hiddenQuantity, string _parentOrderId, bool _isChildOrder) : 79 | product(_product) 80 | { 81 | side = _side; 82 | orderId = _orderId; 83 | orderType = _orderType; 84 | price = _price; 85 | visibleQuantity = _visibleQuantity; 86 | hiddenQuantity = _hiddenQuantity; 87 | parentOrderId = _parentOrderId; 88 | isChildOrder = _isChildOrder; 89 | } 90 | 91 | template 92 | const T& ExecutionOrder::GetProduct() const 93 | { 94 | return product; 95 | } 96 | 97 | template 98 | PricingSide ExecutionOrder::GetSide() const 99 | { 100 | return side; 101 | } 102 | 103 | 104 | template 105 | const string& ExecutionOrder::GetOrderId() const 106 | { 107 | return orderId; 108 | } 109 | 110 | template 111 | OrderType ExecutionOrder::GetOrderType() const 112 | { 113 | return orderType; 114 | } 115 | 116 | template 117 | double ExecutionOrder::GetPrice() const 118 | { 119 | return price; 120 | } 121 | 122 | template 123 | long ExecutionOrder::GetVisibleQuantity() const 124 | { 125 | return visibleQuantity; 126 | } 127 | 128 | template 129 | long ExecutionOrder::GetHiddenQuantity() const 130 | { 131 | return hiddenQuantity; 132 | } 133 | 134 | template 135 | const string& ExecutionOrder::GetParentOrderId() const 136 | { 137 | return parentOrderId; 138 | } 139 | 140 | template 141 | bool ExecutionOrder::IsChildOrder() const 142 | { 143 | return isChildOrder; 144 | } 145 | 146 | template 147 | ostream& operator<<(ostream& os, const ExecutionOrder& order) 148 | { 149 | T product = order.GetProduct(); 150 | string _product = product.GetProductId(); 151 | string _orderId = order.GetOrderId(); 152 | string _side = (order.GetSide()==BID? "Bid":"Ask"); 153 | string _orderType; 154 | switch(order.GetOrderType()) { 155 | case FOK: _orderType = "FOK"; break; 156 | case MARKET: _orderType = "MARKET"; break; 157 | case LIMIT: _orderType = "LIMIT"; break; 158 | case STOP: _orderType = "STOP"; break; 159 | case IOC: _orderType = "IOC"; break; 160 | } 161 | string _price = convertPrice(order.GetPrice()); 162 | string _visibleQuantity = to_string(order.GetVisibleQuantity()); 163 | string _hiddenQuantity = to_string(order.GetHiddenQuantity()); 164 | string _parentOrderId = order.GetParentOrderId(); 165 | string _isChildOrder = (order.IsChildOrder()?"True":"False"); 166 | 167 | vector _strings; 168 | _strings.push_back(_product); 169 | _strings.push_back(_orderId); 170 | _strings.push_back(_side); 171 | _strings.push_back(_orderType); 172 | _strings.push_back(_price); 173 | _strings.push_back(_visibleQuantity); 174 | _strings.push_back(_hiddenQuantity); 175 | _strings.push_back(_parentOrderId); 176 | _strings.push_back(_isChildOrder); 177 | string _str = join(_strings, ","); 178 | os << _str; 179 | return os; 180 | } 181 | 182 | /** 183 | * Algo Execution Service to execute orders on market. 184 | * Keyed on product identifier. 185 | * Type T is the product type. 186 | */ 187 | template 188 | class AlgoExecution 189 | { 190 | private: 191 | ExecutionOrder executionOrder; 192 | Market market; 193 | 194 | public: 195 | // ctor for an order 196 | AlgoExecution() = default; // needed for map data structure later 197 | AlgoExecution(const ExecutionOrder &_executionOrder, Market _market); 198 | 199 | // Get the execution order 200 | const ExecutionOrder& GetExecutionOrder() const; 201 | 202 | // Get the market 203 | Market GetMarket() const; 204 | 205 | }; 206 | 207 | template 208 | AlgoExecution::AlgoExecution(const ExecutionOrder &_executionOrder, Market _market) : 209 | executionOrder(_executionOrder), market(_market) 210 | { 211 | } 212 | 213 | template 214 | const ExecutionOrder& AlgoExecution::GetExecutionOrder() const 215 | { 216 | return executionOrder; 217 | } 218 | 219 | template 220 | Market AlgoExecution::GetMarket() const 221 | { 222 | return market; 223 | } 224 | 225 | // forward declaration of AlgoExecutionServiceListener 226 | template 227 | class AlgoExecutionServiceListener; 228 | 229 | 230 | /** 231 | * Algo Execution Service to execute orders on market. 232 | * Keyed on product identifier. 233 | * Type T is the product type. 234 | */ 235 | template 236 | class AlgoExecutionService : public Service> 237 | { 238 | private: 239 | map> algoExecutionMap; // store algo execution data keyed by product identifier 240 | vector>*> listeners; // list of listeners to this service 241 | AlgoExecutionServiceListener* algoexecservicelistener; 242 | double spread; 243 | long count; 244 | 245 | public: 246 | // ctor 247 | AlgoExecutionService(); 248 | // dtor 249 | ~AlgoExecutionService() = default; 250 | 251 | // Get data on our service given a key 252 | AlgoExecution& GetData(string key); 253 | 254 | // The callback that a Connector should invoke for any new or updated data 255 | void OnMessage(AlgoExecution& data) override; 256 | 257 | // Add a listener to the Service for callbacks on add, remove, and update events for data to the Service 258 | void AddListener(ServiceListener> *listener) override; 259 | 260 | // Get all listeners on the Service. 261 | const vector< ServiceListener>* >& GetListeners() const override; 262 | 263 | // Get the special listener for algo execution service 264 | AlgoExecutionServiceListener* GetAlgoExecutionServiceListener(); 265 | 266 | // Execute an algo order on a market, called by AlgoExecutionServiceListener to subscribe data from Algo Market Data Service to Algo Execution Service 267 | void AlgoExecuteOrder(OrderBook& _orderBook); 268 | 269 | }; 270 | 271 | template 272 | AlgoExecutionService::AlgoExecutionService() 273 | { 274 | count = 0; 275 | algoexecservicelistener = new AlgoExecutionServiceListener(this); // listener related to this server 276 | } 277 | 278 | template 279 | AlgoExecution& AlgoExecutionService::GetData(string key) 280 | { 281 | return algoExecutionMap[key]; 282 | } 283 | 284 | /** 285 | * OnMessage() used to be called by input connector to subscribe data 286 | * no need to implement here. 287 | */ 288 | template 289 | void AlgoExecutionService::OnMessage(AlgoExecution& data) 290 | { 291 | } 292 | 293 | template 294 | void AlgoExecutionService::AddListener(ServiceListener> *listener) 295 | { 296 | listeners.push_back(listener); 297 | } 298 | 299 | template 300 | const vector< ServiceListener>* >& AlgoExecutionService::GetListeners() const 301 | { 302 | return listeners; 303 | } 304 | 305 | template 306 | AlgoExecutionServiceListener* AlgoExecutionService::GetAlgoExecutionServiceListener() 307 | { 308 | return algoexecservicelistener; 309 | } 310 | 311 | /** 312 | * Similar to AddExecutionOrder in executionservice.hpp 313 | * Execute an algo order on a market, called by AlgoExecutionServiceListener to subscribe data from Algo Market Data Service to Algo Execution Service 314 | * 1. Store the listened market orderbook data into algo execution map 315 | * 2. Flow the data to listeners 316 | */ 317 | template 318 | void AlgoExecutionService::AlgoExecuteOrder(OrderBook& _orderBook) 319 | { 320 | // get the order book data 321 | T product = _orderBook.GetProduct(); 322 | string key = product.GetProductId(); 323 | string orderId = "Algo" + GenerateRandomId(11); 324 | string parentOrderId = "AlgoParent" + GenerateRandomId(5); 325 | 326 | // get the best bid and offer order and their corresponding price and quantity 327 | BidOffer bidOffer = _orderBook.GetBestBidOffer(); 328 | Order bid = bidOffer.GetBidOrder(); 329 | Order offer = bidOffer.GetOfferOrder(); 330 | double bidPrice = bid.GetPrice(); 331 | double offerPrice = offer.GetPrice(); 332 | long bidQuantity = bid.GetQuantity(); 333 | long offerQuantity = offer.GetQuantity(); 334 | 335 | PricingSide side; 336 | double price; 337 | long quantity; 338 | // only agressing when the spread is at its tightest (1/128) 339 | if (offerPrice-bidPrice <= 1.0/128.0){ 340 | // alternating between bid and offer 341 | // taking the opposite side of the book to cross the spread, i.e., market order 342 | if (count % 2 == 0) { 343 | side = BID; 344 | price = offerPrice; // BUY order takes best ask price 345 | quantity = bidQuantity; 346 | } else { 347 | side = OFFER; 348 | price = bidPrice; // SELL order takes best bid price 349 | quantity = offerQuantity; 350 | } 351 | } 352 | 353 | // update the count 354 | count++; 355 | 356 | // Create the execution order 357 | long visibleQuantity = quantity; 358 | long hiddenQuantity = 0; 359 | bool isChildOrder = false; 360 | OrderType orderType = MARKET; // market order 361 | ExecutionOrder executionOrder(product, side, orderId, orderType, price, visibleQuantity, hiddenQuantity, parentOrderId, isChildOrder); 362 | 363 | // Create the algo execution 364 | Market market = BROKERTEC; 365 | AlgoExecution algoExecution(executionOrder, market); 366 | 367 | // update the algo execution map 368 | if (algoExecutionMap.find(key) != algoExecutionMap.end()) {algoExecutionMap.erase(key);} 369 | algoExecutionMap.insert(pair> (key, algoExecution)); 370 | 371 | // flow the data to listeners 372 | for (auto& l : listeners) { 373 | l -> ProcessAdd(algoExecution); 374 | } 375 | } 376 | 377 | 378 | /** 379 | * Algo Execution Service Listener subscribing data from Market Data Service to Algo Execution Service. 380 | * Type T is the product type. 381 | */ 382 | template 383 | class AlgoExecutionServiceListener : public ServiceListener> 384 | { 385 | private: 386 | AlgoExecutionService* service; 387 | 388 | public: 389 | // ctor 390 | AlgoExecutionServiceListener(AlgoExecutionService* _service); 391 | // dtor 392 | ~AlgoExecutionServiceListener()=default; 393 | 394 | // Listener callback to process an add event to the Service 395 | void ProcessAdd(OrderBook &data) override; 396 | 397 | // Listener callback to process a remove event to the Service 398 | void ProcessRemove(OrderBook &data) override; 399 | 400 | // Listener callback to process an update event to the Service 401 | void ProcessUpdate(OrderBook &data) override; 402 | 403 | }; 404 | 405 | template 406 | AlgoExecutionServiceListener::AlgoExecutionServiceListener(AlgoExecutionService* _service) 407 | { 408 | service = _service; 409 | } 410 | 411 | /** 412 | * ProcessAdd() method is used by listener to subscribe data from Market Data Service to Algo Execution Service. 413 | * It calls AlgoExecuteOrder() method, change the data type from OrderBook to AlgoExecution and notify the listeners. 414 | */ 415 | template 416 | void AlgoExecutionServiceListener::ProcessAdd(OrderBook &data) 417 | { 418 | service->AlgoExecuteOrder(data); 419 | } 420 | 421 | template 422 | void AlgoExecutionServiceListener::ProcessRemove(OrderBook &data) 423 | { 424 | } 425 | 426 | template 427 | void AlgoExecutionServiceListener::ProcessUpdate(OrderBook &data) 428 | { 429 | } 430 | 431 | 432 | #endif -------------------------------------------------------------------------------- /headers/algostreamingservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * algostreamingservice.hpp 3 | * Defines the data types and Service for algo streams. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | 8 | #ifndef ALGOSTREAMING_SERVICE_HPP 9 | #define ALGOSTREAMING_SERVICE_HPP 10 | 11 | #include "soa.hpp" 12 | #include "utils.hpp" 13 | #include "pricingservice.hpp" 14 | #include "marketdataservice.hpp" // for PricingSide definition 15 | 16 | /** 17 | * A price stream order with price and quantity (visible and hidden) 18 | */ 19 | class PriceStreamOrder 20 | { 21 | 22 | public: 23 | 24 | // ctor for an order 25 | PriceStreamOrder() = default; // needed for map data structure later 26 | PriceStreamOrder(double _price, long _visibleQuantity, long _hiddenQuantity, PricingSide _side); 27 | 28 | // The side on this order 29 | PricingSide GetSide() const; 30 | 31 | // Get the price on this order 32 | double GetPrice() const; 33 | 34 | // Get the visible quantity on this order 35 | long GetVisibleQuantity() const; 36 | 37 | // Get the hidden quantity on this order 38 | long GetHiddenQuantity() const; 39 | 40 | // object printer 41 | friend ostream& operator<<(ostream& os, const PriceStreamOrder& order); 42 | 43 | private: 44 | double price; 45 | long visibleQuantity; 46 | long hiddenQuantity; 47 | PricingSide side; 48 | 49 | }; 50 | 51 | PriceStreamOrder::PriceStreamOrder(double _price, long _visibleQuantity, long _hiddenQuantity, PricingSide _side) 52 | { 53 | price = _price; 54 | visibleQuantity = _visibleQuantity; 55 | hiddenQuantity = _hiddenQuantity; 56 | side = _side; 57 | } 58 | 59 | double PriceStreamOrder::GetPrice() const 60 | { 61 | return price; 62 | } 63 | 64 | long PriceStreamOrder::GetVisibleQuantity() const 65 | { 66 | return visibleQuantity; 67 | } 68 | 69 | long PriceStreamOrder::GetHiddenQuantity() const 70 | { 71 | return hiddenQuantity; 72 | } 73 | 74 | PricingSide PriceStreamOrder::GetSide() const 75 | { 76 | return side; 77 | } 78 | 79 | ostream& operator<<(ostream& os, const PriceStreamOrder& order) 80 | { 81 | string _price = convertPrice(order.GetPrice()); 82 | string _visibleQuantity = to_string(order.GetVisibleQuantity()); 83 | string _hiddenQuantity = to_string(order.GetHiddenQuantity()); 84 | PricingSide side = order.GetSide(); 85 | string _side; 86 | switch (side) 87 | { 88 | case BID: 89 | _side = "BID"; 90 | break; 91 | case OFFER: 92 | _side = "OFFER"; 93 | break; 94 | } 95 | 96 | vector _strings; 97 | _strings.push_back(_price); 98 | _strings.push_back(_visibleQuantity); 99 | _strings.push_back(_hiddenQuantity); 100 | _strings.push_back(_side); 101 | string _str = join(_strings, ","); 102 | os << _str; 103 | return os; 104 | } 105 | 106 | /** 107 | * Price Stream with a two-way market. 108 | * Type T is the product type. 109 | */ 110 | template 111 | class PriceStream 112 | { 113 | 114 | public: 115 | 116 | // ctor 117 | PriceStream() = default; // needed for map data structure later 118 | PriceStream(const T &_product, const PriceStreamOrder &_bidOrder, const PriceStreamOrder &_offerOrder); 119 | 120 | // Get the product 121 | const T& GetProduct() const; 122 | 123 | // Get the bid order 124 | const PriceStreamOrder& GetBidOrder() const; 125 | 126 | // Get the offer order 127 | const PriceStreamOrder& GetOfferOrder() const; 128 | 129 | // object printer 130 | template 131 | friend ostream& operator<<(ostream& os, const PriceStream& priceStream); 132 | 133 | private: 134 | T product; 135 | PriceStreamOrder bidOrder; 136 | PriceStreamOrder offerOrder; 137 | 138 | }; 139 | 140 | template 141 | PriceStream::PriceStream(const T &_product, const PriceStreamOrder &_bidOrder, const PriceStreamOrder &_offerOrder) : 142 | product(_product), bidOrder(_bidOrder), offerOrder(_offerOrder) 143 | { 144 | } 145 | 146 | template 147 | const T& PriceStream::GetProduct() const 148 | { 149 | return product; 150 | } 151 | 152 | template 153 | const PriceStreamOrder& PriceStream::GetBidOrder() const 154 | { 155 | return bidOrder; 156 | } 157 | 158 | template 159 | const PriceStreamOrder& PriceStream::GetOfferOrder() const 160 | { 161 | return offerOrder; 162 | } 163 | 164 | template 165 | ostream& operator<<(ostream& os, const PriceStream& priceStream) 166 | { 167 | T product = priceStream.GetProduct(); 168 | string productId = product.GetProductId(); 169 | PriceStreamOrder bidOrder = priceStream.GetBidOrder(); 170 | PriceStreamOrder offerOrder = priceStream.GetOfferOrder(); 171 | os << productId << "," << bidOrder << "," << offerOrder; 172 | return os; 173 | } 174 | 175 | /** 176 | * An algo streaming that process algo streaming. 177 | * Type T is the product type. 178 | */ 179 | template 180 | class AlgoStream 181 | { 182 | private: 183 | PriceStream priceStream; 184 | 185 | public: 186 | // ctor for an order 187 | AlgoStream() = default; // needed for map data structure later 188 | AlgoStream(const PriceStream &_priceStream); 189 | 190 | // Get the price stream 191 | const PriceStream& GetPriceStream() const; 192 | 193 | }; 194 | 195 | template 196 | AlgoStream::AlgoStream(const PriceStream &_priceStream) : 197 | priceStream(_priceStream) 198 | { 199 | } 200 | 201 | template 202 | const PriceStream& AlgoStream::GetPriceStream() const 203 | { 204 | return priceStream; 205 | } 206 | 207 | // forward declaration 208 | template 209 | class AlgoStreamingServiceListener; 210 | 211 | 212 | /** 213 | * Algo Streaming Service to publish algo streams. 214 | * Keyed on product identifier. 215 | * Type T is the product type. 216 | */ 217 | template 218 | class AlgoStreamingService : public Service > 219 | { 220 | private: 221 | map> algoStreamMap; // store algo stream data keyed by product identifier 222 | vector>*> listeners; // list of listeners to this service 223 | AlgoStreamingServiceListener* algostreamlistener; 224 | long count; 225 | 226 | public: 227 | // ctor and dtor 228 | AlgoStreamingService(); 229 | ~AlgoStreamingService()=default; 230 | 231 | // Get data on our service given a key 232 | AlgoStream& GetData(string key) override; 233 | 234 | // The callback that a Connector should invoke for any new or updated data 235 | void OnMessage(AlgoStream& data) override; 236 | 237 | // Add a listener to the Service for callbacks on add, remove, and update events for data to the Service 238 | void AddListener(ServiceListener> *listener) override; 239 | 240 | // Get all listeners on the Service. 241 | const vector< ServiceListener>* >& GetListeners() const override; 242 | 243 | // Get the special listener for algo streaming service 244 | AlgoStreamingServiceListener* GetAlgoStreamingListener(); 245 | 246 | // Publish algo streams (called by algo streaming service listener to subscribe data from pricing service) 247 | void PublishAlgoStream(const Price& price); 248 | 249 | }; 250 | 251 | template 252 | AlgoStreamingService::AlgoStreamingService() 253 | { 254 | algostreamlistener = new AlgoStreamingServiceListener(this); 255 | } 256 | 257 | template 258 | AlgoStream& AlgoStreamingService::GetData(string key) 259 | { 260 | return algoStreamMap[key]; 261 | } 262 | 263 | /** 264 | * OnMessage() used to be called by input connector to subscribe data 265 | * no need to implement here. 266 | */ 267 | template 268 | void AlgoStreamingService::OnMessage(AlgoStream& data) 269 | { 270 | } 271 | 272 | template 273 | void AlgoStreamingService::AddListener(ServiceListener> *listener) 274 | { 275 | listeners.push_back(listener); 276 | } 277 | 278 | template 279 | const vector< ServiceListener>* >& AlgoStreamingService::GetListeners() const 280 | { 281 | return listeners; 282 | } 283 | 284 | template 285 | AlgoStreamingServiceListener* AlgoStreamingService::GetAlgoStreamingListener() 286 | { 287 | return algostreamlistener; 288 | } 289 | 290 | /** 291 | * PublishAlgoStream() method is used by listener to subscribe data from pricing service. 292 | * It transites the data structure from Price to AlgoStream, save in the service, and notify the listeners. 293 | */ 294 | template 295 | void AlgoStreamingService::PublishAlgoStream(const Price& price) 296 | { 297 | T product = price.GetProduct(); 298 | string key = product.GetProductId(); 299 | double mid = price.GetMid(); 300 | double spread = price.GetBidOfferSpread(); 301 | double bidPrice = mid - spread/2; 302 | double offerPrice = mid + spread/2; 303 | // alternate visible size between 1000000 and 2000000 304 | long visibleQuantity = (count % 2 == 0) ? 1000000 : 2000000; 305 | // hidden size is twice the visible size 306 | long hiddenQuantity = visibleQuantity * 2; 307 | 308 | count++; 309 | 310 | // create bid order and offer order 311 | PriceStreamOrder bidOrder(bidPrice, visibleQuantity, hiddenQuantity, BID); 312 | PriceStreamOrder offerOrder(offerPrice, visibleQuantity, hiddenQuantity, OFFER); 313 | // create price stream 314 | PriceStream priceStream(product, bidOrder, offerOrder); 315 | // create algo stream 316 | AlgoStream algoStream(priceStream); 317 | 318 | // update the algo stream map 319 | if (algoStreamMap.find(key) != algoStreamMap.end()) {algoStreamMap.erase(key);} 320 | algoStreamMap.insert(pair> (key, algoStream)); 321 | 322 | // notify the listeners 323 | for (auto& listener : listeners) 324 | { 325 | listener->ProcessAdd(algoStream); 326 | } 327 | } 328 | 329 | /** 330 | * Algo Streaming Service Listener to subscribe data from pricing service. 331 | * Type T is the product type. 332 | */ 333 | template 334 | class AlgoStreamingServiceListener : public ServiceListener> 335 | { 336 | private: 337 | AlgoStreamingService* algoStreamingService; 338 | 339 | public: 340 | // ctor 341 | AlgoStreamingServiceListener(AlgoStreamingService* _algoStreamingService); 342 | 343 | // Listener callback to process an add event to the Service 344 | void ProcessAdd(Price& price) override; 345 | 346 | // Listener callback to process a remove event to the Service 347 | void ProcessRemove(Price& price) override; 348 | 349 | // Listener callback to process an update event to the Service 350 | void ProcessUpdate(Price& price) override; 351 | 352 | }; 353 | 354 | template 355 | AlgoStreamingServiceListener::AlgoStreamingServiceListener(AlgoStreamingService* _algoStreamingService) 356 | { 357 | algoStreamingService = _algoStreamingService; 358 | } 359 | 360 | /** 361 | * ProcessAdd() method is used by listener to subscribe data from pricing service. 362 | * It calls GetPriceStream() method, change the data type from Price to AlgoStream and notify the listeners. 363 | */ 364 | 365 | template 366 | void AlgoStreamingServiceListener::ProcessAdd(Price& price) 367 | { 368 | algoStreamingService->PublishAlgoStream(price); 369 | } 370 | 371 | template 372 | void AlgoStreamingServiceListener::ProcessRemove(Price& price) 373 | { 374 | } 375 | 376 | template 377 | void AlgoStreamingServiceListener::ProcessUpdate(Price& price) 378 | { 379 | } 380 | 381 | 382 | #endif -------------------------------------------------------------------------------- /headers/executionservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * executionservice.hpp 3 | * Defines the data types and Service for executions. 4 | * Execution service does not need an input connector, since it flows in data from a listener from algo execution service. 5 | * But it needs an inner connector (publish only) to publish executions. 6 | * @author Boyu Yang 7 | */ 8 | #ifndef EXECUTION_SERVICE_HPP 9 | #define EXECUTION_SERVICE_HPP 10 | 11 | #include 12 | #include "soa.hpp" 13 | #include "algoexecutionservice.hpp" 14 | 15 | /** 16 | * Forward declaration of ExecutionOutputConnector and ExecutionServiceListener. 17 | * As described, execution service needs a publish-only connector to publish executions 18 | * Type T is the product type. 19 | */ 20 | template 21 | class ExecutionOutputConnector; 22 | template 23 | class ExecutionServiceListener; 24 | 25 | 26 | /** 27 | * Service for executing orders on an exchange. 28 | * Keyed on product identifier. 29 | * Type T is the product type. 30 | */ 31 | template 32 | class ExecutionService : public Service > 33 | { 34 | private: 35 | map> executionOrderMap; // store execution order data keyed by product identifier 36 | vector>*> listeners; // list of listeners to this service 37 | string host; // host name for inbound connector 38 | string port; // port number for inbound connector 39 | ExecutionOutputConnector* connector; // connector related to this server 40 | ExecutionServiceListener* executionservicelistener; // listener related to this server 41 | 42 | public: 43 | // ctor and dtor 44 | ExecutionService(const string& _host, const string& _port); 45 | ~ExecutionService()=default; 46 | 47 | // Get data on our service given a key 48 | ExecutionOrder& GetData(string key); 49 | 50 | // The callback that a Connector should invoke for any new or updated data 51 | void OnMessage(ExecutionOrder& data) override; 52 | 53 | // Add a listener to the Service for callbacks on add, remove, and update events for data to the Service 54 | void AddListener(ServiceListener> *listener) override; 55 | 56 | // Get all listeners on the Service. 57 | const vector< ServiceListener>* >& GetListeners() const override; 58 | 59 | // Get the special listener for execution service 60 | ExecutionServiceListener* GetExecutionServiceListener(); 61 | 62 | // Get the connector 63 | ExecutionOutputConnector* GetConnector(); 64 | 65 | // Execute an order on a market, call the publish-only connector to publish executions 66 | void ExecuteOrder(const ExecutionOrder& order, Market market); 67 | 68 | // called by ExecutionServiceListener to subscribe data from Algo Execution Service to Execution Service 69 | void AddExecutionOrder(const AlgoExecution& algoExecution); 70 | 71 | }; 72 | 73 | template 74 | ExecutionService::ExecutionService(const string& _host, const string& _port) 75 | : host(_host), port(_port) 76 | { 77 | connector = new ExecutionOutputConnector(this, host, port); // connector related to this server 78 | executionservicelistener = new ExecutionServiceListener(this); // listener related to this server 79 | } 80 | 81 | template 82 | ExecutionOrder& ExecutionService::GetData(string key) 83 | { 84 | return executionOrderMap[key]; 85 | } 86 | 87 | /** 88 | * OnMessage() used to be called by input connector to subscribe data 89 | * no need to implement here. 90 | */ 91 | template 92 | void ExecutionService::OnMessage(ExecutionOrder& data) 93 | { 94 | } 95 | 96 | template 97 | void ExecutionService::AddListener(ServiceListener> *listener) 98 | { 99 | listeners.push_back(listener); 100 | } 101 | 102 | template 103 | const vector< ServiceListener>* >& ExecutionService::GetListeners() const 104 | { 105 | return listeners; 106 | } 107 | 108 | template 109 | ExecutionServiceListener* ExecutionService::GetExecutionServiceListener() 110 | { 111 | return executionservicelistener; 112 | } 113 | 114 | template 115 | ExecutionOutputConnector* ExecutionService::GetConnector() 116 | { 117 | return connector; 118 | } 119 | 120 | /** 121 | * Execute an order on a market, call the publish-only connector to publish executions via connector 122 | */ 123 | template 124 | void ExecutionService::ExecuteOrder(const ExecutionOrder& order, Market market) 125 | { 126 | connector->Publish(order, market); 127 | } 128 | 129 | /** 130 | * Called by ExecutionServiceListener to subscribe data from Algo Execution Service to Execution Service 131 | * Store the listened algo execution order data into execution order map 132 | * Key should be the order id (unique) 133 | */ 134 | template 135 | void ExecutionService::AddExecutionOrder(const AlgoExecution& algoExecution) 136 | { 137 | ExecutionOrder executionOrder = algoExecution.GetExecutionOrder(); 138 | string orderId = executionOrder.GetOrderId(); 139 | if (executionOrderMap.find(orderId) != executionOrderMap.end()) {executionOrderMap.erase(orderId);} 140 | executionOrderMap.insert(pair> (orderId, executionOrder)); 141 | 142 | // notify the listener 143 | for (auto& l : listeners) { 144 | l -> ProcessAdd(executionOrder); 145 | } 146 | } 147 | 148 | /** 149 | * ExecutionOutputConnector: publish data to execution service. 150 | * Type T is the product type. 151 | */ 152 | template 153 | class ExecutionOutputConnector 154 | { 155 | private: 156 | ExecutionService* service; // execution service related to this connector 157 | string host; // host name 158 | string port; // port number 159 | boost::asio::io_service io_service; // io service 160 | boost::asio::ip::tcp::socket socket; // socket 161 | 162 | void handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request); 163 | void start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service); 164 | 165 | public: 166 | // ctor 167 | ExecutionOutputConnector(ExecutionService* _service, const string& _host, const string& _port); 168 | // dtor: close the socket 169 | ~ExecutionOutputConnector(); 170 | 171 | // Publish data to the Connector 172 | void Publish(const ExecutionOrder& order, Market& market); 173 | 174 | // Here the subscribe is used to open a process and listen to the socket 175 | void Subscribe(); 176 | }; 177 | 178 | template 179 | ExecutionOutputConnector::ExecutionOutputConnector(ExecutionService* _service, const string& _host, const string& _port) 180 | : service(_service), host(_host), port(_port), socket(io_service) 181 | { 182 | } 183 | 184 | template 185 | ExecutionOutputConnector::~ExecutionOutputConnector() 186 | { 187 | socket.close(); 188 | } 189 | 190 | template 191 | void ExecutionOutputConnector::start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service) { 192 | boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*io_service); 193 | acceptor->async_accept(*socket, [this, socket, acceptor, io_service](const boost::system::error_code& ec) { 194 | if (!ec) { 195 | boost::asio::streambuf* request = new boost::asio::streambuf; 196 | boost::asio::async_read_until(*socket, *request, "\r", std::bind(&ExecutionOutputConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 197 | } 198 | start_accept(acceptor, io_service); // accept the next connection 199 | }); 200 | } 201 | 202 | template 203 | void ExecutionOutputConnector::handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request) { 204 | if (!ec) { 205 | // get the entire data 206 | std::string data = std::string(boost::asio::buffers_begin(request->data()), boost::asio::buffers_end(request->data())); 207 | // find the last newline 208 | std::size_t last_newline = data.rfind('\r'); 209 | if (last_newline != std::string::npos) { 210 | // consume only up to the last newline 211 | request->consume(last_newline + 1); 212 | // only process the data up to the last newline 213 | data = data.substr(0, last_newline); 214 | } else { 215 | // if there's no newline, don't process any data 216 | data.clear(); 217 | } 218 | // server receives and prints data 219 | cout << data << endl; 220 | 221 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&ExecutionOutputConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 222 | } else { 223 | delete request; // delete the streambuf when we're done with it 224 | delete socket; // delete the socket when we're done with it 225 | } 226 | } 227 | 228 | /** 229 | * Publish() method is used by publish-only connector to publish executions. 230 | */ 231 | template 232 | void ExecutionOutputConnector::Publish(const ExecutionOrder& order, Market& market) 233 | { 234 | // connect to the socket 235 | boost::asio::ip::tcp::resolver resolver(io_service); 236 | boost::asio::ip::tcp::resolver::query query(host, port); 237 | boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); 238 | boost::asio::connect(socket, endpoint_iterator); 239 | 240 | // publish the execution order data to socket 241 | auto product = order.GetProduct(); 242 | string order_type; 243 | switch(order.GetOrderType()) { 244 | case FOK: order_type = "FOK"; break; 245 | case MARKET: order_type = "MARKET"; break; 246 | case LIMIT: order_type = "LIMIT"; break; 247 | case STOP: order_type = "STOP"; break; 248 | case IOC: order_type = "IOC"; break; 249 | } 250 | string tradeMarket; 251 | switch(market) { 252 | case BROKERTEC: tradeMarket = "BROKERTEC"; break; 253 | case ESPEED: tradeMarket = "ESPEED"; break; 254 | case CME: tradeMarket = "CME"; break; 255 | } 256 | std::string dataLine = string("ExecutionOrder: \n") 257 | + "\tProduct: " + product.GetProductId() + "\tOrderId: " + order.GetOrderId() + "\tTrade Market: " + tradeMarket + "\n" 258 | + "\tPricingSide: " + (order.GetSide()==BID? "Bid":"Offer") 259 | + "\tOrderType: " + order_type + "\t\tIsChildOrder: " + (order.IsChildOrder()?"True":"False") + "\n" 260 | + "\tPrice: " + std::to_string(order.GetPrice()) + "\tVisibleQuantity: " + std::to_string(order.GetVisibleQuantity()) 261 | + "\tHiddenQuantity: " + std::to_string(order.GetHiddenQuantity()) + "\n"; 262 | 263 | // publish the data string to socket 264 | // asynchronous operation ensures server gets all data 265 | boost::asio::async_write(socket, boost::asio::buffer(dataLine + "\r"), [](boost::system::error_code /*ec*/, std::size_t /*length*/) {}); 266 | } 267 | 268 | template 269 | void ExecutionOutputConnector::Subscribe() 270 | { 271 | log(LogLevel::NOTE, "Streaming output server listening on " + host + ":" + port); 272 | try { 273 | // connect to the socket 274 | boost::asio::ip::tcp::resolver resolver(io_service); 275 | boost::asio::ip::tcp::resolver::query query(host, port); 276 | boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); 277 | boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint); 278 | start_accept(&acceptor, &io_service); 279 | io_service.run(); 280 | } 281 | catch (std::exception& e){ 282 | // throw error log 283 | log(LogLevel::ERROR, e.what()); 284 | return; 285 | } 286 | } 287 | 288 | /** 289 | * Execution Service Listener subscribes data from Algo Execution Service to Execution Service. 290 | * Type T is the product type. Inner data type is AlgoExecution. 291 | */ 292 | template 293 | class ExecutionServiceListener : public ServiceListener> 294 | { 295 | private: 296 | ExecutionService* executionService; 297 | 298 | public: 299 | // ctor 300 | ExecutionServiceListener(ExecutionService* _executionService); 301 | // dtor 302 | ~ExecutionServiceListener()=default; 303 | 304 | // Listener callback to process an add event to the Service 305 | void ProcessAdd(AlgoExecution &data) override; 306 | 307 | // Listener callback to process a remove event to the Service 308 | void ProcessRemove(AlgoExecution &data) override; 309 | 310 | // Listener callback to process an update event to the Service 311 | void ProcessUpdate(AlgoExecution &data) override; 312 | 313 | }; 314 | 315 | template 316 | ExecutionServiceListener::ExecutionServiceListener(ExecutionService* _executionService) 317 | { 318 | executionService = _executionService; 319 | } 320 | 321 | /** 322 | * ProcessAdd() method is used by listener to subscribe data from Algo Execution Service to Execution Service. 323 | * It calls ExecuteOrder() method (for publishing through connector) and the AddExecutionOrder() method (for saving data into execution order map) 324 | */ 325 | template 326 | void ExecutionServiceListener::ProcessAdd(AlgoExecution &data) 327 | { 328 | // save algo execution info into execution service 329 | // directly pass in AlgoExecution type and transit to ExecutionOrder type inside the function 330 | executionService->AddExecutionOrder(data); 331 | 332 | // call the connector to publish executions 333 | ExecutionOrder executionOrder = data.GetExecutionOrder(); 334 | Market market = data.GetMarket(); 335 | executionService->ExecuteOrder(executionOrder, market); 336 | 337 | } 338 | 339 | template 340 | void ExecutionServiceListener::ProcessRemove(AlgoExecution &data) 341 | { 342 | } 343 | 344 | template 345 | void ExecutionServiceListener::ProcessUpdate(AlgoExecution &data) 346 | { 347 | } 348 | 349 | #endif 350 | -------------------------------------------------------------------------------- /headers/fileconnector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILECONNECTOR_HPP 2 | #define FILECONNECTOR_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "soa.hpp" 9 | #include "utils.hpp" 10 | 11 | using namespace std; 12 | 13 | /** 14 | * FileConnector: generic file connector that subscribes data from file and publishes to socket. 15 | * Type T is the product type. 16 | */ 17 | template 18 | class FileConnector 19 | { 20 | private: 21 | string dataFile; // data file name 22 | string host; // host name 23 | string port; // port number 24 | boost::asio::io_service io_service; // io service 25 | boost::asio::ip::tcp::socket socket; // socket 26 | 27 | public: 28 | // ctor 29 | FileConnector(const string& _dataFile, const string& _host, const string& _port); 30 | // dtor 31 | ~FileConnector()=default; 32 | 33 | // Publish data to the socket 34 | void Publish(const string& data); 35 | 36 | // Subscribe external data 37 | void Subscribe(); 38 | 39 | // function call operator for threading purpose 40 | void operator() (); 41 | 42 | }; 43 | 44 | 45 | template 46 | FileConnector::FileConnector(const string& _dataFile, const string& _host, const string& _port) 47 | : dataFile(_dataFile), socket(io_service), host(_host), port(_port) 48 | { 49 | // connect to the socket 50 | boost::asio::ip::tcp::resolver resolver(io_service); 51 | boost::asio::ip::tcp::resolver::query query(host, port); 52 | boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); 53 | boost::asio::connect(socket, endpoint_iterator); 54 | } 55 | 56 | template 57 | void FileConnector::Publish(const string& dataLine) 58 | { 59 | // write the data string to socket 60 | // asynchronous operation ensures server gets all data 61 | boost::asio::async_write(socket, boost::asio::buffer(dataLine + "\n"), [](boost::system::error_code /*ec*/, std::size_t /*length*/) {}); 62 | } 63 | 64 | template 65 | void FileConnector::Subscribe() 66 | { 67 | try { 68 | // read data from file 69 | ifstream data(dataFile.c_str()); 70 | if (!data.is_open()){ 71 | // throw error log 72 | log(LogLevel::ERROR, "No such file or directory: " + dataFile); 73 | return; 74 | } 75 | string line; 76 | while (getline(data, line)) 77 | { 78 | // publish data to socket 79 | this->Publish(line); 80 | } 81 | data.close(); 82 | } 83 | catch (std::exception& e){ 84 | // throw error log 85 | log(LogLevel::ERROR, e.what()); 86 | return; 87 | } 88 | } 89 | 90 | template 91 | void FileConnector::operator() () 92 | { 93 | this->Subscribe(); 94 | } 95 | 96 | 97 | #endif -------------------------------------------------------------------------------- /headers/guiservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * guiservice.hpp 3 | * Defines the data types and Service for GUI output. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | 8 | #ifndef GUI_SERVICE_HPP 9 | #define GUI_SERVICE_HPP 10 | 11 | #include "soa.hpp" 12 | #include "utils.hpp" 13 | #include "pricingservice.hpp" 14 | 15 | // forward declaration of GUIConnector and GUIServiceListener 16 | template 17 | class GUIConnector; 18 | template 19 | class GUIServiceListener; 20 | 21 | 22 | /** 23 | * Service for outputing GUI with a certain throttle. 24 | * Keyed on product identifier. 25 | * Type T is the product type. 26 | */ 27 | template 28 | class GUIService : public Service > 29 | { 30 | private: 31 | map> priceMap; // store price data keyed by product identifier 32 | vector>*> listeners; // list of listeners to this service 33 | GUIConnector* connector; // connector related to this server 34 | GUIServiceListener* guiservicelistener; // listener related to this server 35 | int throttle; // throttle of the service 36 | std::chrono::system_clock::time_point startTime; // start time 37 | 38 | public: 39 | // ctor 40 | GUIService(); 41 | // dtor 42 | ~GUIService()=default; 43 | 44 | // Get data on our service given a key 45 | Price& GetData(string key) override; 46 | 47 | // The callback that a Connector should invoke for any new or updated data 48 | void OnMessage(Price& data) override; 49 | 50 | // Add a listener to the Service for callbacks on add, remove, and update events for data to the Service 51 | void AddListener(ServiceListener> *listener) override; 52 | 53 | // Get all listeners on the Service. 54 | const vector< ServiceListener>* >& GetListeners() const override; 55 | 56 | // Get the special listener for GUI service 57 | GUIServiceListener* GetGUIServiceListener(); 58 | 59 | // Get the connector 60 | GUIConnector* GetConnector(); 61 | 62 | // Get the throttle 63 | int GetThrottle() const; 64 | 65 | // Publish the throttled price through connector 66 | void PublishThrottledPrice(Price& price); 67 | 68 | }; 69 | 70 | template 71 | GUIService::GUIService() 72 | { 73 | connector = new GUIConnector(this); // connector related to this server 74 | guiservicelistener = new GUIServiceListener(this); // listener related to this server 75 | throttle = 300; // default throttle 76 | startTime = std::chrono::system_clock::now(); // start time 77 | } 78 | 79 | template 80 | Price& GUIService::GetData(string key) 81 | { 82 | return priceMap[key]; 83 | } 84 | 85 | // no need to implement OnMessage 86 | template 87 | void GUIService::OnMessage(Price& data) 88 | { 89 | } 90 | 91 | template 92 | void GUIService::AddListener(ServiceListener> *listener) 93 | { 94 | listeners.push_back(listener); 95 | } 96 | 97 | template 98 | const vector< ServiceListener>* >& GUIService::GetListeners() const 99 | { 100 | return listeners; 101 | } 102 | 103 | template 104 | GUIServiceListener* GUIService::GetGUIServiceListener() 105 | { 106 | return guiservicelistener; 107 | } 108 | 109 | template 110 | GUIConnector* GUIService::GetConnector() 111 | { 112 | return connector; 113 | } 114 | 115 | template 116 | int GUIService::GetThrottle() const 117 | { 118 | return throttle; 119 | } 120 | 121 | template 122 | void GUIService::PublishThrottledPrice(Price& price) 123 | { 124 | // only publish price to GUI if the time interval is larger than throttle 125 | auto now = std::chrono::system_clock::now(); 126 | auto diff = std::chrono::duration_cast(now - startTime); 127 | if (diff.count() > throttle) { 128 | // update the time 129 | startTime = now; 130 | // publish the price 131 | connector->Publish(price); 132 | } 133 | } 134 | 135 | /** 136 | * GUI Connector publishing data from GUI Service. 137 | * Type T is the product type. 138 | */ 139 | template 140 | class GUIConnector : public Connector> 141 | { 142 | private: 143 | GUIService* service; 144 | 145 | public: 146 | // ctor 147 | GUIConnector(GUIService* _service); 148 | // Publish data to the Connector 149 | // If subscribe-only, then this does nothing 150 | void Publish(Price &data) override; 151 | }; 152 | 153 | template 154 | GUIConnector::GUIConnector(GUIService* _service) 155 | { 156 | service = _service; 157 | } 158 | 159 | // publish to external source gui.txt 160 | template 161 | void GUIConnector::Publish(Price &data) 162 | { 163 | ofstream outFile; 164 | outFile.open("../res/gui.txt", ios::app); 165 | // need overloading operator<< for Price 166 | outFile << getTime() << "," << data << endl; 167 | outFile.close(); 168 | } 169 | 170 | /** 171 | * GUI Service Listener subscribing data from Pricing Service to GUI Service. 172 | * Type T is the product type. 173 | */ 174 | template 175 | class GUIServiceListener : public ServiceListener> 176 | { 177 | private: 178 | GUIService* guiService; 179 | 180 | public: 181 | // ctor 182 | GUIServiceListener(GUIService* _guiService); 183 | // Listener callback to process an add event to the Service 184 | void ProcessAdd(Price& price) override; 185 | // Listener callback to process a remove event to the Service 186 | void ProcessRemove(Price& price) override; 187 | // Listener callback to process an update event to the Service 188 | void ProcessUpdate(Price& price) override; 189 | }; 190 | 191 | template 192 | GUIServiceListener::GUIServiceListener(GUIService* _guiService) 193 | { 194 | guiService = _guiService; 195 | } 196 | 197 | template 198 | void GUIServiceListener::ProcessAdd(Price& price) 199 | { 200 | guiService->PublishThrottledPrice(price); 201 | } 202 | 203 | template 204 | void GUIServiceListener::ProcessRemove(Price& price) 205 | { 206 | } 207 | 208 | template 209 | void GUIServiceListener::ProcessUpdate(Price& price) 210 | { 211 | } 212 | 213 | #endif -------------------------------------------------------------------------------- /headers/historicaldataservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * historicaldataservice.hpp 3 | * historicaldataservice.hpp 4 | * 5 | * @author Boyu Yang 6 | * Defines the data types and Service for historical data. 7 | */ 8 | #ifndef HISTORICAL_DATA_SERVICE_HPP 9 | #define HISTORICAL_DATA_SERVICE_HPP 10 | 11 | #include "soa.hpp" 12 | #include "streamingservice.hpp" 13 | #include "riskservice.hpp" 14 | #include "executionservice.hpp" 15 | #include "inquiryservice.hpp" 16 | #include "positionservice.hpp" 17 | #include "utils.hpp" 18 | 19 | enum ServiceType {POSITION, RISK, EXECUTION, STREAMING, INQUIRY}; 20 | 21 | 22 | // pre declaration 23 | template 24 | class HistoricalDataConnector; 25 | template 26 | class HistoricalDataServiceListener; 27 | 28 | 29 | /** 30 | * Service for processing and persisting historical data to a persistent store. 31 | * Keyed on some persistent key. 32 | * Type T is the data type to persist. 33 | */ 34 | template 35 | class HistoricalDataService : Service 36 | { 37 | private: 38 | map dataMap; // store data keyed by some persistent key 39 | vector*> listeners; // list of listeners to this service 40 | HistoricalDataConnector* connector; // connector related to this server 41 | ServiceType type; // type of the service 42 | HistoricalDataServiceListener* historicalservicelistener; // listener to this service 43 | 44 | public: 45 | // ctor and dtor 46 | HistoricalDataService(ServiceType _type); 47 | ~HistoricalDataService()=default; 48 | 49 | // Get data on our service given a key 50 | T& GetData(string key) override; 51 | 52 | // The callback that a Connector should invoke for any new or updated data 53 | void OnMessage(T& data) override; 54 | 55 | // Add a listener to the Service for callbacks on add, remove, and update events for data to the Service 56 | void AddListener(ServiceListener *listener) override; 57 | 58 | // Get all listeners on the Service. 59 | const vector< ServiceListener* >& GetListeners() const override; 60 | 61 | // Get the special listener for historical data service 62 | HistoricalDataServiceListener* GetHistoricalDataServiceListener(); 63 | 64 | // Get the connector 65 | HistoricalDataConnector* GetConnector(); 66 | 67 | // Get the type of the service 68 | ServiceType GetServiceType() const; 69 | 70 | // Persist data to a store 71 | // call the connector to persist/publish data to an external store (such as KDB database) 72 | void PersistData(string persistKey, T& data); 73 | 74 | }; 75 | 76 | template 77 | HistoricalDataService::HistoricalDataService(ServiceType _type) 78 | { 79 | type = _type; 80 | historicalservicelistener = new HistoricalDataServiceListener(this); // listener related to this server 81 | connector = new HistoricalDataConnector(this); // connector related to this server 82 | } 83 | 84 | template 85 | T& HistoricalDataService::GetData(string key) 86 | { 87 | return dataMap[key]; 88 | } 89 | 90 | /** 91 | * OnMessage() used to be called by input connector to subscribe data 92 | * The service only has a publish-only connector and hence no need to implement here. 93 | */ 94 | template 95 | void HistoricalDataService::OnMessage(T& data) 96 | { 97 | } 98 | 99 | template 100 | void HistoricalDataService::AddListener(ServiceListener *listener) 101 | { 102 | listeners.push_back(listener); 103 | } 104 | 105 | template 106 | const vector< ServiceListener* >& HistoricalDataService::GetListeners() const 107 | { 108 | return listeners; 109 | } 110 | 111 | template 112 | HistoricalDataServiceListener* HistoricalDataService::GetHistoricalDataServiceListener() 113 | { 114 | return historicalservicelistener; 115 | } 116 | 117 | template 118 | HistoricalDataConnector* HistoricalDataService::GetConnector() 119 | { 120 | return connector; 121 | } 122 | 123 | template 124 | ServiceType HistoricalDataService::GetServiceType() const 125 | { 126 | return type; 127 | } 128 | 129 | // Historical data service listener subscribes data from position, risk, execution, streaming and inquiry services. 130 | // call the connector to persist/publish data to an external store (such as KDB database) 131 | // NOTE: since data from different services are keyed by different keys, we need to pass in the key as function parameter as well 132 | template 133 | void HistoricalDataService::PersistData(string persistKey, T& data) 134 | { 135 | // save position/risk/execution/inquiry/streaming data to the service 136 | if (dataMap.find(persistKey) == dataMap.end()) 137 | dataMap.insert(pair(persistKey, data)); 138 | else 139 | dataMap[persistKey] = data; 140 | 141 | // persist/publish data to an external data store 142 | connector->Publish(data); 143 | } 144 | 145 | /** 146 | * Historical Data Connector publishing data from Historical Data Service. 147 | * Type T is the data type to persist. 148 | */ 149 | template 150 | class HistoricalDataConnector : public Connector 151 | { 152 | private: 153 | HistoricalDataService* service; 154 | 155 | public: 156 | // ctor 157 | HistoricalDataConnector(HistoricalDataService* _service); 158 | // Publish-only connector, publish to external source 159 | void Publish(T& data); 160 | }; 161 | 162 | template 163 | HistoricalDataConnector::HistoricalDataConnector(HistoricalDataService* _service) 164 | : service(_service) 165 | { 166 | } 167 | 168 | /** 169 | * Publish data to the Connector 170 | * call the connector to persist/publish data to an external store (such as KDB database) 171 | * for data from different services, obtain the string representation of these objects and vend out 172 | * into positions.txt, risk.txt, executions.txt, allinquiries.txt, streaming.txt 173 | */ 174 | template 175 | void HistoricalDataConnector::Publish(T& data) 176 | { 177 | ServiceType type = service->GetServiceType(); 178 | ofstream outFile; 179 | string fileName; 180 | switch (type) 181 | { 182 | case POSITION: 183 | fileName = "../res/positions.txt"; 184 | break; 185 | case RISK: 186 | fileName = "../res/risk.txt"; 187 | break; 188 | case EXECUTION: 189 | fileName = "../res/executions.txt"; 190 | break; 191 | case STREAMING: 192 | fileName = "../res/streaming.txt"; 193 | break; 194 | case INQUIRY: 195 | fileName = "../res/allinquiries.txt"; 196 | break; 197 | default: 198 | break; 199 | } 200 | outFile.open(fileName, ios::app); 201 | if (outFile.is_open()) 202 | { 203 | // need overloading operator<< for different data types 204 | outFile << getTime() << "," << data << endl; 205 | } 206 | outFile.close(); 207 | } 208 | 209 | /** 210 | * Historical Data Service Listener subscribing data to Historical Data. 211 | * Type T is the data type to persist. 212 | */ 213 | template 214 | class HistoricalDataServiceListener : public ServiceListener 215 | { 216 | private: 217 | HistoricalDataService* service; 218 | 219 | public: 220 | // ctor 221 | HistoricalDataServiceListener(HistoricalDataService* _service); 222 | // Listener callback to process an add event to the Service 223 | void ProcessAdd(Position& data); 224 | void ProcessAdd(PV01& data); 225 | void ProcessAdd(PriceStream& data); 226 | void ProcessAdd(ExecutionOrder& data); 227 | void ProcessAdd(Inquiry& data); 228 | 229 | // Listener callback to process a remove event to the Service 230 | void ProcessRemove(T& data) override; 231 | // Listener callback to process an update event to the Service 232 | void ProcessUpdate(T& data) override; 233 | }; 234 | 235 | template 236 | HistoricalDataServiceListener::HistoricalDataServiceListener(HistoricalDataService* _service) 237 | { 238 | service = _service; 239 | } 240 | 241 | /** 242 | * Historical data service listener subscribes data from position, risk, execution, streaming and inquiry services. 243 | * ProcessAdd() thus calls PersistData() method to let connector persist/publish data to external data store (such as a KDB database) 244 | * different services have different keys 245 | * if the service type is POISITION, RISK, STREAMING, the key is the product identifier 246 | */ 247 | template 248 | void HistoricalDataServiceListener::ProcessAdd(Position& data) 249 | { 250 | string persistKey = data.GetProduct().GetProductId(); 251 | service->PersistData(persistKey, data); 252 | } 253 | 254 | template 255 | void HistoricalDataServiceListener::ProcessAdd(PV01& data) 256 | { 257 | string persistKey = data.GetProduct().GetProductId(); 258 | service->PersistData(persistKey, data); 259 | } 260 | 261 | template 262 | void HistoricalDataServiceListener::ProcessAdd(PriceStream& data) 263 | { 264 | string persistKey = data.GetProduct().GetProductId(); 265 | service->PersistData(persistKey, data); 266 | } 267 | 268 | template 269 | void HistoricalDataServiceListener::ProcessAdd(ExecutionOrder& data) 270 | { 271 | string persistKey = data.GetOrderId(); 272 | service->PersistData(persistKey, data); 273 | } 274 | 275 | template 276 | void HistoricalDataServiceListener::ProcessAdd(Inquiry& data) 277 | { 278 | string persistKey = data.GetInquiryId(); 279 | service->PersistData(persistKey, data); 280 | } 281 | 282 | 283 | template 284 | void HistoricalDataServiceListener::ProcessRemove(T& data) 285 | { 286 | } 287 | 288 | template 289 | void HistoricalDataServiceListener::ProcessUpdate(T& data) 290 | { 291 | } 292 | 293 | #endif 294 | -------------------------------------------------------------------------------- /headers/inquiryservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * inquiryservice.hpp 3 | * Defines the data types and Service for customer inquiries. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | #ifndef INQUIRY_SERVICE_HPP 8 | #define INQUIRY_SERVICE_HPP 9 | 10 | #include "soa.hpp" 11 | #include "utils.hpp" 12 | #include "tradebookingservice.hpp" 13 | 14 | // Various inqyury states 15 | enum InquiryState { RECEIVED, QUOTED, DONE, REJECTED, CUSTOMER_REJECTED }; 16 | 17 | /** 18 | * Inquiry object modeling a customer inquiry from a client. 19 | * Type T is the product type. 20 | */ 21 | template 22 | class Inquiry 23 | { 24 | 25 | public: 26 | 27 | // ctor for an inquiry 28 | Inquiry() = default; 29 | Inquiry(string _inquiryId, const T &_product, Side _side, long _quantity, double _price, InquiryState _state); 30 | 31 | // Get the inquiry ID 32 | const string& GetInquiryId() const; 33 | 34 | // Get the product 35 | const T& GetProduct() const; 36 | 37 | // Get the side on the inquiry 38 | Side GetSide() const; 39 | 40 | // Get the quantity that the client is inquiring for 41 | long GetQuantity() const; 42 | 43 | // Get the price that we have responded back with 44 | double GetPrice() const; 45 | 46 | // Set the price 47 | void SetPrice(double _price); 48 | 49 | // Get the current state on the inquiry 50 | InquiryState GetState() const; 51 | 52 | // Set the current state on the inquiry 53 | void SetState(InquiryState state); 54 | 55 | // object printer 56 | template 57 | friend ostream& operator<<(ostream& os, const Inquiry& inquiry); 58 | 59 | private: 60 | string inquiryId; 61 | T product; 62 | Side side; 63 | long quantity; 64 | double price; 65 | InquiryState state; 66 | 67 | }; 68 | 69 | 70 | template 71 | Inquiry::Inquiry(string _inquiryId, const T &_product, Side _side, long _quantity, double _price, InquiryState _state) : 72 | product(_product) 73 | { 74 | inquiryId = _inquiryId; 75 | side = _side; 76 | quantity = _quantity; 77 | price = _price; 78 | state = _state; 79 | } 80 | 81 | template 82 | const string& Inquiry::GetInquiryId() const 83 | { 84 | return inquiryId; 85 | } 86 | 87 | template 88 | const T& Inquiry::GetProduct() const 89 | { 90 | return product; 91 | } 92 | 93 | template 94 | Side Inquiry::GetSide() const 95 | { 96 | return side; 97 | } 98 | 99 | template 100 | long Inquiry::GetQuantity() const 101 | { 102 | return quantity; 103 | } 104 | 105 | template 106 | double Inquiry::GetPrice() const 107 | { 108 | return price; 109 | } 110 | 111 | template 112 | void Inquiry::SetPrice(double _price) 113 | { 114 | price = _price; 115 | } 116 | 117 | template 118 | InquiryState Inquiry::GetState() const 119 | { 120 | return state; 121 | } 122 | 123 | template 124 | void Inquiry::SetState(InquiryState _state) 125 | { 126 | state = _state; 127 | } 128 | 129 | template 130 | ostream& operator<<(ostream& os, const Inquiry& inquiry) 131 | { 132 | string _inquiryId = inquiry.GetInquiryId(); 133 | T product = inquiry.GetProduct(); 134 | string _product = product.GetProductId(); 135 | Side side = inquiry.GetSide(); 136 | string _side; 137 | switch (side) 138 | { 139 | case BID: 140 | _side = "BID"; 141 | break; 142 | case OFFER: 143 | _side = "OFFER"; 144 | break; 145 | } 146 | long _quantity = inquiry.GetQuantity(); 147 | double _price = inquiry.GetPrice(); 148 | string _state; 149 | switch (inquiry.GetState()) 150 | { 151 | case RECEIVED: 152 | _state = "RECEIVED"; 153 | break; 154 | case QUOTED: 155 | _state = "QUOTED"; 156 | break; 157 | case DONE: 158 | _state = "DONE"; 159 | break; 160 | case REJECTED: 161 | _state = "REJECTED"; 162 | break; 163 | case CUSTOMER_REJECTED: 164 | _state = "CUSTOMER_REJECTED"; 165 | break; 166 | } 167 | 168 | vector _strings; 169 | _strings.push_back(_inquiryId); 170 | _strings.push_back(_product); 171 | _strings.push_back(_side); 172 | _strings.push_back(to_string(_quantity)); 173 | _strings.push_back(convertPrice(_price)); 174 | _strings.push_back(_state); 175 | string _str = join(_strings, ","); 176 | os << _str; 177 | return os; 178 | } 179 | 180 | // forward declaration of InquiryDataConnector 181 | template 182 | class InquiryDataConnector; 183 | 184 | /** 185 | * Service for customer inquiry objects. 186 | * Keyed on inquiry identifier (NOTE: this is NOT a product identifier since each inquiry must be unique). 187 | * Type T is the product type. 188 | */ 189 | template 190 | class InquiryService : public Service > 191 | { 192 | private: 193 | map> inquiryMap; 194 | vector>*> listeners; 195 | InquiryDataConnector* connector; 196 | string host; // host name for inbound connector 197 | string port; // port number for inbound connector 198 | 199 | public: 200 | // ctor and dtor 201 | InquiryService(const string& _host, const string& _port); 202 | ~InquiryService()=default; 203 | 204 | // Get data on our service given a key 205 | Inquiry& GetData(string key); 206 | 207 | // The callback that a Connector should invoke for any new or updated data 208 | void OnMessage(Inquiry &data); 209 | 210 | // Add a listener to the Service for callbacks on add, remove, and update events for data to the Service. 211 | void AddListener(ServiceListener> *listener); 212 | 213 | // Get all listeners on the Service. 214 | const vector< ServiceListener>* >& GetListeners() const; 215 | 216 | // Get the connector 217 | InquiryDataConnector* GetConnector(); 218 | 219 | // Send a quote back to the client 220 | void SendQuote(const string &inquiryId, double price); 221 | 222 | // Reject an inquiry from the client 223 | void RejectInquiry(const string &inquiryId); 224 | 225 | }; 226 | 227 | template 228 | InquiryService::InquiryService(const string& _host, const string& _port) 229 | : host(_host), port(_port) 230 | { 231 | connector = new InquiryDataConnector(this, host, port); 232 | } 233 | 234 | template 235 | Inquiry& InquiryService::GetData(string key) 236 | { 237 | return inquiryMap[key]; 238 | } 239 | 240 | template 241 | void InquiryService::OnMessage(Inquiry &data) 242 | { 243 | InquiryState state = data.GetState(); 244 | string inquiryId = data.GetInquiryId(); 245 | switch (state){ 246 | case RECEIVED: 247 | // if inquiry is received, send back a quote to the connector via publish() 248 | connector->Publish(data); 249 | break; 250 | case QUOTED: 251 | // finish the inquiry with DONE status and send an update of the object 252 | data.SetState(DONE); 253 | // store the inquiry 254 | if (inquiryMap.find(inquiryId) != inquiryMap.end()) {inquiryMap.erase(inquiryId);} 255 | inquiryMap.insert(pair > (inquiryId, data)); 256 | // notify listeners 257 | for (auto& listener : listeners) 258 | { 259 | listener->ProcessAdd(data); 260 | } 261 | break; 262 | default: 263 | break; 264 | } 265 | 266 | 267 | // if inquiry is done, remove it from the map 268 | if (data.GetState() == DONE) 269 | { 270 | inquiryMap.erase(data.GetInquiryId()); 271 | } 272 | // otherwise, update the inquiry 273 | else 274 | { 275 | inquiryMap[data.GetInquiryId()] = data; 276 | } 277 | 278 | // notify listeners 279 | for (auto& listener : listeners) 280 | { 281 | listener->ProcessAdd(data); 282 | } 283 | } 284 | 285 | template 286 | void InquiryService::AddListener(ServiceListener> *listener) 287 | { 288 | listeners.push_back(listener); 289 | } 290 | 291 | template 292 | const vector< ServiceListener>* >& InquiryService::GetListeners() const 293 | { 294 | return listeners; 295 | } 296 | 297 | template 298 | InquiryDataConnector* InquiryService::GetConnector() 299 | { 300 | return connector; 301 | } 302 | 303 | template 304 | void InquiryService::SendQuote(const string &inquiryId, double price) 305 | { 306 | // get the inquiry 307 | Inquiry& inquiry = inquiryMap[inquiryId]; 308 | // update the inquiry 309 | inquiry.SetPrice(price); 310 | // notify listeners 311 | for (auto& listener : listeners) 312 | { 313 | listener->ProcessAdd(inquiry); 314 | } 315 | } 316 | 317 | template 318 | void InquiryService::RejectInquiry(const string &inquiryId) 319 | { 320 | // get the inquiry 321 | Inquiry& inquiry = inquiryMap[inquiryId]; 322 | // update the inquiry 323 | inquiry.SetState(REJECTED); 324 | } 325 | 326 | /** 327 | * Inquiry data connector subscribing data from socket to Inquiry Service 328 | * Type T is the product type. 329 | */ 330 | template 331 | class InquiryDataConnector : public Connector> 332 | { 333 | private: 334 | InquiryService* service; 335 | string host; // host name 336 | string port; // port number 337 | boost::asio::io_service io_service; // io service 338 | boost::asio::ip::tcp::socket socket; // socket 339 | 340 | void handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request); 341 | void start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service); 342 | 343 | public: 344 | // ctor 345 | InquiryDataConnector(InquiryService* _service, const string& _host, const string& _port); 346 | // dtor: close the socket 347 | ~InquiryDataConnector(); 348 | 349 | // Publish data to the Connector 350 | // If subscribe-only, then this does nothing 351 | void Publish(Inquiry &data) override; 352 | 353 | // Subscribe data from socket 354 | void Subscribe(); 355 | 356 | // Subcribe updated inquiry record from the connector 357 | void SubscribeUpdate(Inquiry& data); 358 | }; 359 | 360 | template 361 | InquiryDataConnector::InquiryDataConnector(InquiryService* _service, const string& _host, const string& _port) 362 | : service(_service), host(_host), port(_port), socket(io_service) 363 | { 364 | } 365 | 366 | template 367 | InquiryDataConnector::~InquiryDataConnector() 368 | { 369 | socket.close(); 370 | } 371 | 372 | template 373 | void InquiryDataConnector::start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service) { 374 | boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*io_service); 375 | acceptor->async_accept(*socket, [this, socket, acceptor, io_service](const boost::system::error_code& ec) { 376 | if (!ec) { 377 | boost::asio::streambuf* request = new boost::asio::streambuf; 378 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&InquiryDataConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 379 | } 380 | start_accept(acceptor, io_service); // accept the next connection 381 | }); 382 | } 383 | 384 | template 385 | void InquiryDataConnector::handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request) { 386 | if (!ec) { 387 | std::string data = std::string(boost::asio::buffers_begin(request->data()), boost::asio::buffers_end(request->data())); 388 | // find the last newline 389 | std::size_t last_newline = data.rfind('\n'); 390 | if (last_newline != std::string::npos) { 391 | // consume only up to the last newline 392 | request->consume(last_newline + 1); 393 | // only process the data up to the last newline 394 | data = data.substr(0, last_newline); 395 | } else { 396 | // if there's no newline, don't process any data 397 | data.clear(); 398 | } 399 | 400 | // split the data into lines 401 | std::stringstream ss(data); 402 | std::string line; 403 | while (std::getline(ss, line)) { 404 | // parse the line 405 | vector tokens; 406 | stringstream lineStream(line); 407 | string token; 408 | while(getline(lineStream, token, ',')) 409 | tokens.push_back(token); 410 | 411 | // create inquiry 412 | string inquiryId = tokens[0]; 413 | string productId = tokens[1]; 414 | T product = getProductObject(productId); 415 | Side side = tokens[2] == "BUY" ? BUY : SELL; 416 | long quantity = stol(tokens[3]); 417 | double price = convertPrice(tokens[4]); 418 | InquiryState state = tokens[5] == "RECEIVED" ? RECEIVED : tokens[5] == "QUOTED" ? QUOTED : tokens[5] == "DONE" ? DONE : tokens[5] == "REJECTED" ? REJECTED : CUSTOMER_REJECTED; 419 | Inquiry inquiry(inquiryId, product, side, quantity, price, state); 420 | service->OnMessage(inquiry); 421 | } 422 | 423 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&InquiryDataConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 424 | } else { 425 | delete request; // delete the streambuf when we're done with it 426 | delete socket; // delete the socket when we're done with it 427 | } 428 | } 429 | 430 | // Transite the inquiry from RECEIVED to QUOTED and send back to the service 431 | template 432 | void InquiryDataConnector::Publish(Inquiry &data) 433 | { 434 | if (data.GetState() == RECEIVED){ 435 | data.SetState(QUOTED); 436 | this->SubscribeUpdate(data); 437 | } 438 | } 439 | 440 | template 441 | void InquiryDataConnector::Subscribe() 442 | { 443 | log(LogLevel::NOTE, "Inquiry data server listening on " + host + ":" + port); 444 | try { 445 | // connect to the socket 446 | boost::asio::ip::tcp::resolver resolver(io_service); 447 | boost::asio::ip::tcp::resolver::query query(host, port); 448 | boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); 449 | boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint); 450 | start_accept(&acceptor, &io_service); 451 | io_service.run(); 452 | } 453 | catch (std::exception& e){ 454 | // throw error log 455 | log(LogLevel::ERROR, e.what()); 456 | return; 457 | } 458 | } 459 | 460 | template 461 | void InquiryDataConnector::SubscribeUpdate(Inquiry& data) 462 | { 463 | // send updated inquiry back to the service 464 | service->OnMessage(data); 465 | } 466 | 467 | #endif 468 | -------------------------------------------------------------------------------- /headers/marketdataservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * marketdataservice.hpp 3 | * Defines the data types and Service for order book market data. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | #ifndef MARKET_DATA_SERVICE_HPP 8 | #define MARKET_DATA_SERVICE_HPP 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "soa.hpp" 20 | #include "utils.hpp" 21 | 22 | using namespace std; 23 | 24 | // Side for market data 25 | enum PricingSide { BID, OFFER }; 26 | 27 | /** 28 | * A market data order with price, quantity, and side. 29 | */ 30 | class Order 31 | { 32 | 33 | public: 34 | // default ctor, need by the map data structure 35 | Order() = default; 36 | 37 | // ctor for an order 38 | Order(double _price, long _quantity, PricingSide _side); 39 | 40 | // Get the price on the order 41 | double GetPrice() const; 42 | 43 | // Get the quantity on the order 44 | long GetQuantity() const; 45 | 46 | // Get the side on the order 47 | PricingSide GetSide() const; 48 | 49 | private: 50 | double price; 51 | long quantity; 52 | PricingSide side; 53 | 54 | }; 55 | 56 | Order::Order(double _price, long _quantity, PricingSide _side) : 57 | price(_price), quantity(_quantity), side(_side) 58 | { 59 | } 60 | 61 | double Order::GetPrice() const 62 | { 63 | return price; 64 | } 65 | 66 | long Order::GetQuantity() const 67 | { 68 | return quantity; 69 | } 70 | 71 | PricingSide Order::GetSide() const 72 | { 73 | return side; 74 | } 75 | 76 | 77 | /** 78 | * Class representing a bid and offer order 79 | */ 80 | class BidOffer 81 | { 82 | 83 | public: 84 | 85 | // ctor for bid/offer 86 | BidOffer(const Order &_bidOrder, const Order &_offerOrder); 87 | 88 | // Get the bid order 89 | const Order& GetBidOrder() const; 90 | 91 | // Get the offer order 92 | const Order& GetOfferOrder() const; 93 | 94 | private: 95 | Order bidOrder; 96 | Order offerOrder; 97 | 98 | }; 99 | 100 | BidOffer::BidOffer(const Order &_bidOrder, const Order &_offerOrder) : 101 | bidOrder(_bidOrder), offerOrder(_offerOrder) 102 | { 103 | } 104 | 105 | const Order& BidOffer::GetBidOrder() const 106 | { 107 | return bidOrder; 108 | } 109 | 110 | const Order& BidOffer::GetOfferOrder() const 111 | { 112 | return offerOrder; 113 | } 114 | 115 | 116 | /** 117 | * Order book with a bid and offer stack. 118 | * Type T is the product type. 119 | */ 120 | template 121 | class OrderBook 122 | { 123 | 124 | public: 125 | 126 | // default ctor, needed for the map data structure 127 | OrderBook() = default; 128 | 129 | OrderBook(const string& productId); 130 | 131 | // ctor for the order book 132 | OrderBook(const T &_product, const vector &_bidStack, const vector &_offerStack); 133 | 134 | // Get the product 135 | const T& GetProduct() const; 136 | 137 | // Get the bid stack 138 | vector& GetBidStack(); 139 | 140 | // Get the offer stack 141 | vector& GetOfferStack(); 142 | 143 | // Get the best bid/offer order 144 | BidOffer GetBestBidOffer() const; 145 | 146 | 147 | private: 148 | T product; 149 | vector bidStack; 150 | vector offerStack; 151 | 152 | }; 153 | 154 | template 155 | OrderBook::OrderBook(const string& productId) 156 | : product(getProductObject(productId)), bidStack(vector()), offerStack(vector()) 157 | { 158 | } 159 | 160 | 161 | template 162 | OrderBook::OrderBook(const T &_product, const vector &_bidStack, const vector &_offerStack) : 163 | product(_product), bidStack(_bidStack), offerStack(_offerStack) 164 | { 165 | } 166 | 167 | template 168 | const T& OrderBook::GetProduct() const 169 | { 170 | return product; 171 | } 172 | 173 | template 174 | vector& OrderBook::GetBidStack() 175 | { 176 | return bidStack; 177 | } 178 | 179 | template 180 | vector& OrderBook::GetOfferStack() 181 | { 182 | return offerStack; 183 | } 184 | 185 | template 186 | BidOffer OrderBook::GetBestBidOffer() const 187 | { 188 | // iterate bid stack and offer stack to find the best bid/offer order 189 | auto bestBid = std::max_element(bidStack.begin(), bidStack.end(), [](const Order& a, const Order& b) {return a.GetPrice() < b.GetPrice(); }); 190 | auto bestOffer = std::min_element(offerStack.begin(), offerStack.end(), [](const Order& a, const Order& b) {return a.GetPrice() < b.GetPrice(); }); 191 | return BidOffer(*bestBid, *bestOffer); 192 | 193 | } 194 | 195 | // forward declaration of MarketDataConnector 196 | template 197 | class MarketDataConnector; 198 | 199 | /** 200 | * Market Data Service which distributes market data 201 | * Keyed on product identifier. 202 | * Type T is the product type. 203 | */ 204 | template 205 | class MarketDataService : public Service > 206 | { 207 | private: 208 | map> orderBookMap; 209 | vector>*> listeners; 210 | int bookDepth; 211 | string host; // host name for inbound connector 212 | string port; // port number for inbound connector 213 | MarketDataConnector* connector; // connector related to this server 214 | 215 | public: 216 | // ctor and dtor 217 | MarketDataService(const string& _host, const string& _port); 218 | ~MarketDataService()=default; 219 | 220 | // Get data on our service given a key 221 | OrderBook& GetData(string key) override; 222 | 223 | // The callback that a Connector should invoke for any new or updated data 224 | void OnMessage(OrderBook& data) override; 225 | 226 | // Add a listener to the Service for callbacks on add, remove, and update events 227 | void AddListener(ServiceListener>* listener) override; 228 | 229 | // Get all listeners on the Service 230 | const vector>*>& GetListeners() const override; 231 | 232 | // Get the connector 233 | MarketDataConnector* GetConnector(); 234 | 235 | // Get the book depth 236 | int GetBookDepth() const; 237 | 238 | // Get the best bid/offer order 239 | const BidOffer& GetBestBidOffer(const string &productId); 240 | 241 | // Aggregate the order book 242 | const OrderBook& AggregateDepth(const string &productId); 243 | 244 | }; 245 | 246 | 247 | template 248 | MarketDataService::MarketDataService(const string& _host, const string& _port) 249 | : host(_host), port(_port) 250 | { 251 | bookDepth = 5; // default book depth 252 | connector = new MarketDataConnector(this, host, port); // connector related to this server 253 | } 254 | 255 | template 256 | OrderBook& MarketDataService::GetData(string key) 257 | { 258 | // if the order book does not exist, create a new one 259 | if (orderBookMap.find(key) == orderBookMap.end()) { 260 | orderBookMap.insert(pair>(key, OrderBook(key))); 261 | } 262 | return orderBookMap[key]; 263 | } 264 | 265 | template 266 | void MarketDataService::OnMessage(OrderBook& data) 267 | { 268 | string key = data.GetProduct().GetProductId(); 269 | if (orderBookMap.find(key) != orderBookMap.end()) { orderBookMap.erase(key); } 270 | orderBookMap.insert(pair>(key, data)); 271 | 272 | 273 | for (auto& listener : listeners) 274 | { 275 | listener->ProcessAdd(data); 276 | } 277 | } 278 | 279 | template 280 | void MarketDataService::AddListener(ServiceListener>* listener) 281 | { 282 | listeners.push_back(listener); 283 | } 284 | 285 | template 286 | const vector>*>& MarketDataService::GetListeners() const 287 | { 288 | return listeners; 289 | } 290 | 291 | template 292 | MarketDataConnector* MarketDataService::GetConnector() 293 | { 294 | return connector; 295 | } 296 | 297 | template 298 | int MarketDataService::GetBookDepth() const 299 | { 300 | return bookDepth; 301 | } 302 | 303 | template 304 | const BidOffer& MarketDataService::GetBestBidOffer(const string &productId) 305 | { 306 | return orderBookMap[productId].GetBestBidOffer(); 307 | } 308 | 309 | template 310 | const OrderBook& MarketDataService::AggregateDepth(const string &productId) 311 | { 312 | // get the order book 313 | OrderBook& orderBook = orderBookMap[productId]; 314 | // get the bid stack and offer stack 315 | vector& bidStack = orderBook.GetBidStack(); 316 | vector& offerStack = orderBook.GetOfferStack(); 317 | // aggregate the bid stack 318 | unordered_map aggBidMap; 319 | for (auto& order : bidStack){ 320 | double price = order.GetPrice(); 321 | long quantity = order.GetQuantity(); 322 | if (aggBidMap.find(price) != aggBidMap.end()) { 323 | aggBidMap[price] += quantity; 324 | } 325 | else { 326 | aggBidMap.insert(pair(price, quantity)); 327 | } 328 | } 329 | vector aggBid; 330 | for (auto& item : aggBidMap) { 331 | aggBid.push_back(Order(item.first, item.second, BID)); 332 | } 333 | // (optional) sort the aggregated bid stack 334 | // sort(aggBid.begin(), aggBid.end(), [](const Order& a, const Order& b) {return a.GetPrice() > b.GetPrice(); }); 335 | 336 | // aggregate the offer stack 337 | unordered_map aggOfferMap; 338 | for (auto& order : offerStack) { 339 | double price = order.GetPrice(); 340 | long quantity = order.GetQuantity(); 341 | if (aggOfferMap.find(price) != aggOfferMap.end()) { 342 | aggOfferMap[price] += quantity; 343 | } 344 | else { 345 | aggOfferMap.insert(pair(price, quantity)); 346 | } 347 | } 348 | vector aggOffer; 349 | for (auto& item : aggOfferMap) { 350 | aggOffer.push_back(Order(item.first, item.second, OFFER)); 351 | } 352 | // (optional) sort the aggregated offer stack 353 | // sort(aggOffer.begin(), aggOffer.end(), [](const Order& a, const Order& b) {return a.GetPrice() < b.GetPrice(); }); 354 | 355 | // update the order book 356 | orderBook = OrderBook(orderBook.GetProduct(), aggBid, aggOffer); 357 | return orderBook; 358 | } 359 | 360 | /** 361 | * Market Data Connector subscribing data from socket to Market Data Service. 362 | * Type T is the product type. 363 | */ 364 | template 365 | class MarketDataConnector : public Connector> 366 | { 367 | private: 368 | MarketDataService* service; 369 | string host; // host name 370 | string port; // port number 371 | boost::asio::io_service io_service; // io service 372 | boost::asio::ip::tcp::socket socket; // socket 373 | 374 | void handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request); 375 | void start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service); 376 | 377 | public: 378 | // ctor 379 | MarketDataConnector(MarketDataService* _service, const string& _host, const string& _port); 380 | // dtor: close the socket 381 | ~MarketDataConnector(); 382 | 383 | // Publish data to the Connector 384 | void Publish(OrderBook& data) override; 385 | 386 | // Subscribe data 387 | void Subscribe(); 388 | 389 | }; 390 | 391 | template 392 | MarketDataConnector::MarketDataConnector(MarketDataService* _service, const string& _host, const string& _port) 393 | : service(_service), host(_host), port(_port), socket(io_service) 394 | { 395 | } 396 | 397 | template 398 | MarketDataConnector::~MarketDataConnector() 399 | { 400 | socket.close(); 401 | } 402 | 403 | template 404 | void MarketDataConnector::start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service) { 405 | boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*io_service); 406 | acceptor->async_accept(*socket, [this, socket, acceptor, io_service](const boost::system::error_code& ec) { 407 | if (!ec) { 408 | boost::asio::streambuf* request = new boost::asio::streambuf; 409 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&MarketDataConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 410 | } 411 | start_accept(acceptor, io_service); // accept the next connection 412 | }); 413 | } 414 | 415 | template 416 | void MarketDataConnector::handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request) { 417 | if (!ec) { 418 | std::string data = std::string(boost::asio::buffers_begin(request->data()), boost::asio::buffers_end(request->data())); 419 | // find the last newline 420 | std::size_t last_newline = data.rfind('\n'); 421 | if (last_newline != std::string::npos) { 422 | // consume only up to the last newline 423 | request->consume(last_newline + 1); 424 | // only process the data up to the last newline 425 | data = data.substr(0, last_newline); 426 | } else { 427 | // if there's no newline, don't process any data 428 | data.clear(); 429 | } 430 | 431 | // split the data into lines 432 | std::stringstream ss(data); 433 | std::string line; 434 | while (std::getline(ss, line)) { 435 | // parse the line 436 | vector lineData; 437 | stringstream lineStream(line); // turn the line into a stream 438 | string word; 439 | while (getline(lineStream, word, ',')) 440 | { 441 | lineData.push_back(word); 442 | } 443 | string timestamp = lineData[0]; 444 | string productId = lineData[1]; 445 | OrderBook& orderBook = service->GetData(productId); 446 | 447 | string bidPrice, bidQty, askPrice, askQty; 448 | Order bidOrder, askOrder; 449 | for (int k = 0; k < service->GetBookDepth(); k++){ 450 | bidPrice = lineData[4*k+2]; 451 | bidQty = lineData[4*k+3]; 452 | askPrice = lineData[4*k+4]; 453 | askQty = lineData[4*k+5]; 454 | bidOrder = Order(convertPrice(bidPrice), stol(bidQty), BID); 455 | askOrder = Order(convertPrice(askPrice), stol(askQty), OFFER); 456 | orderBook.GetBidStack().push_back(bidOrder); 457 | orderBook.GetOfferStack().push_back(askOrder); 458 | } 459 | // aggregate the order book, get a copy 460 | OrderBook aggOrderBook = service->AggregateDepth(productId); 461 | // publish the order book to the service 462 | service->OnMessage(aggOrderBook); 463 | } 464 | 465 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&MarketDataConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 466 | } else { 467 | delete request; // delete the streambuf when we're done with it 468 | delete socket; // delete the socket when we're done with it 469 | } 470 | } 471 | 472 | template 473 | void MarketDataConnector::Publish(OrderBook& data) 474 | { 475 | } 476 | 477 | template 478 | void MarketDataConnector::Subscribe() 479 | { 480 | log(LogLevel::NOTE, "Market data server listening on " + host + ":" + port); 481 | try { 482 | // connect to the socket 483 | boost::asio::ip::tcp::resolver resolver(io_service); 484 | boost::asio::ip::tcp::resolver::query query(host, port); 485 | boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); 486 | boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint); 487 | start_accept(&acceptor, &io_service); 488 | io_service.run(); 489 | } 490 | catch (std::exception& e){ 491 | // throw error log 492 | log(LogLevel::ERROR, e.what()); 493 | return; 494 | } 495 | } 496 | #endif 497 | -------------------------------------------------------------------------------- /headers/positionservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * positionservice.hpp 3 | * Defines the data types and Service for positions. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | #ifndef POSITION_SERVICE_HPP 8 | #define POSITION_SERVICE_HPP 9 | 10 | #include 11 | #include 12 | #include "soa.hpp" 13 | #include "tradebookingservice.hpp" 14 | 15 | using namespace std; 16 | 17 | /** 18 | * Position class in a particular book. 19 | * Type T is the product type. 20 | */ 21 | template 22 | class Position 23 | { 24 | 25 | public: 26 | 27 | // ctor for a position 28 | Position() = default; 29 | Position(const T &productId); 30 | 31 | // Get the product 32 | const T& GetProduct() const; 33 | 34 | // Get the position quantity 35 | long GetPosition(string &book); 36 | 37 | // Get the aggregate position 38 | long GetAggregatePosition(); 39 | 40 | // send position to risk service through listener 41 | void AddPosition(string &book, long position); 42 | 43 | // object printer 44 | template 45 | friend ostream& operator<<(ostream& os, const Position& position); 46 | 47 | private: 48 | T product; 49 | map bookPositionMap; 50 | 51 | }; 52 | 53 | 54 | template 55 | Position::Position(const T &productId) : 56 | product(productId) 57 | { 58 | } 59 | 60 | template 61 | const T& Position::GetProduct() const 62 | { 63 | return product; 64 | } 65 | 66 | template 67 | long Position::GetPosition(string &book) 68 | { 69 | return bookPositionMap[book]; 70 | } 71 | 72 | template 73 | long Position::GetAggregatePosition() 74 | { 75 | long sum = 0; 76 | for (auto it = bookPositionMap.begin(); it != bookPositionMap.end(); ++it) 77 | { 78 | sum += it->second; 79 | } 80 | return sum; 81 | } 82 | 83 | template 84 | void Position::AddPosition(string &book, long position) 85 | { 86 | if (bookPositionMap.find(book) == bookPositionMap.end()) 87 | { 88 | bookPositionMap.insert(pair(book,position)); 89 | } 90 | else 91 | { 92 | bookPositionMap[book] += position; 93 | } 94 | } 95 | 96 | template 97 | ostream& operator<<(ostream& os, const Position& position) 98 | { 99 | T product = position.GetProduct(); 100 | string productId = product.GetProductId(); 101 | vector _positions; 102 | for (auto& p : position.bookPositionMap) 103 | { 104 | string _book = p.first; 105 | string _position = to_string(p.second); 106 | _positions.push_back(_book); 107 | _positions.push_back(_position); 108 | } 109 | vector _strings; 110 | _strings.push_back(productId); 111 | _strings.insert(_strings.end(), _positions.begin(), _positions.end()); 112 | string _str = join(_strings, ","); 113 | os << _str; 114 | return os; 115 | } 116 | 117 | // Pre-declaration of a listener used to subscribe data from trade booking service 118 | template 119 | class PositionServiceListener; 120 | 121 | /** 122 | * Position Service to manage positions across multiple books and securities. 123 | * Keyed on product identifier. 124 | * Type T is the product type. 125 | */ 126 | template 127 | class PositionService : public Service > 128 | { 129 | private: 130 | map> positionMap; 131 | vector>*> listeners; 132 | PositionServiceListener* positionlistener; 133 | 134 | public: 135 | // ctor and dtor 136 | PositionService(); 137 | ~PositionService()=default; 138 | 139 | // Get data on our service given a key 140 | Position& GetData(string key); 141 | 142 | // The callback that a Connector should invoke for any new or updated data 143 | void OnMessage(Position &data); 144 | 145 | // Add a listener to the Service for callbacks on add, remove, and update events 146 | // for data to the Service. 147 | void AddListener(ServiceListener> *listener); 148 | 149 | // Get all listeners on the Service. 150 | const vector>*>& GetListeners() const; 151 | 152 | // Get the special listener for trade booking service 153 | PositionServiceListener* GetPositionListener(); 154 | 155 | // Add a trade to the service 156 | void AddTrade(const Trade &trade); 157 | 158 | }; 159 | 160 | template 161 | PositionService::PositionService() 162 | { 163 | positionlistener = new PositionServiceListener(this); 164 | } 165 | 166 | template 167 | Position& PositionService::GetData(string key) 168 | { 169 | return positionMap[key]; 170 | } 171 | 172 | /** 173 | * OnMessage() used to be called by an input connector to subscribe data from socket 174 | * no need to implement here. 175 | */ 176 | template 177 | void PositionService::OnMessage(Position &data) 178 | { 179 | } 180 | 181 | template 182 | void PositionService::AddListener(ServiceListener> *listener) 183 | { 184 | listeners.push_back(listener); 185 | } 186 | 187 | template 188 | const vector>*>& PositionService::GetListeners() const 189 | { 190 | return listeners; 191 | } 192 | 193 | template 194 | PositionServiceListener* PositionService::GetPositionListener() 195 | { 196 | return positionlistener; 197 | } 198 | 199 | /** 200 | * AddTrade() method is used to subscribe data from trade booking service, 201 | * transfer Trade data to Position data and save it to the service. 202 | */ 203 | template 204 | void PositionService::AddTrade(const Trade &trade) 205 | { 206 | T product = trade.GetProduct(); 207 | string productId = product.GetProductId(); 208 | string book = trade.GetBook(); 209 | long quantity = (trade.GetSide() == BUY) ? trade.GetQuantity() : -trade.GetQuantity(); 210 | if (positionMap.find(productId) == positionMap.end()) 211 | { 212 | Position position(product); 213 | position.AddPosition(book,quantity); 214 | positionMap.insert(pair>(productId,position)); 215 | } 216 | else 217 | { 218 | positionMap[productId].AddPosition(book,quantity); 219 | } 220 | for (auto& listener: listeners) 221 | { 222 | listener->ProcessAdd(positionMap[productId]); 223 | } 224 | 225 | } 226 | 227 | /** 228 | * Listener class for PositionService. 229 | * Used to subscribe data from trade booking service instead of connector. 230 | * Type T is the product type. 231 | */ 232 | template 233 | class PositionServiceListener : public ServiceListener> 234 | { 235 | private: 236 | PositionService* positionservice; 237 | 238 | public: 239 | // ctor 240 | PositionServiceListener(PositionService* _positionservice); 241 | 242 | // Listener callback to process an add event to the Service 243 | void ProcessAdd(Trade &data); 244 | 245 | // Listener callback to process a remove event to the Service 246 | void ProcessRemove(Trade &data); 247 | 248 | // Listener callback to process an update event to the Service 249 | void ProcessUpdate(Trade &data); 250 | 251 | }; 252 | 253 | template 254 | PositionServiceListener::PositionServiceListener(PositionService* _positionservice) 255 | { 256 | positionservice = _positionservice; 257 | } 258 | 259 | /** 260 | * ProcessAdd() method is used to subscribe data from trade booking service, 261 | * and then call AddTrade() method to add the trade to the service. 262 | */ 263 | template 264 | void PositionServiceListener::ProcessAdd(Trade &data) 265 | { 266 | positionservice->AddTrade(data); 267 | } 268 | 269 | template 270 | void PositionServiceListener::ProcessRemove(Trade &data) 271 | { 272 | } 273 | 274 | template 275 | void PositionServiceListener::ProcessUpdate(Trade &data) 276 | { 277 | } 278 | 279 | 280 | 281 | #endif 282 | -------------------------------------------------------------------------------- /headers/pricingservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * pricingservice.hpp 3 | * Defines the data types and Service for internal prices. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | #ifndef PRICINGservice_HPP 8 | #define PRICINGservice_HPP 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "soa.hpp" 18 | #include "utils.hpp" 19 | 20 | /** 21 | * A price object consisting of mid and bid/offer spread. 22 | * Type T is the product type. 23 | */ 24 | template 25 | class Price 26 | { 27 | 28 | public: 29 | // default ctor (needed for map data structure later) 30 | Price() = default; 31 | 32 | // ctor for a price 33 | Price(const T& _product, double _mid, double _bidOfferSpread); 34 | 35 | // Get the product 36 | const T& GetProduct() const; 37 | 38 | // Get the mid price 39 | double GetMid() const; 40 | 41 | // Get the bid/offer spread around the mid 42 | double GetBidOfferSpread() const; 43 | 44 | // object printer 45 | template 46 | friend ostream& operator<<(ostream& os, const Price& price); 47 | 48 | private: 49 | T product; 50 | double mid; 51 | double bidOfferSpread; 52 | 53 | }; 54 | 55 | template 56 | Price::Price(const T& _product, double _mid, double _bidOfferSpread) 57 | : product(_product), mid(_mid), bidOfferSpread(_bidOfferSpread) 58 | { 59 | } 60 | 61 | template 62 | const T& Price::GetProduct() const 63 | { 64 | return product; 65 | } 66 | 67 | template 68 | double Price::GetMid() const 69 | { 70 | return mid; 71 | } 72 | 73 | template 74 | double Price::GetBidOfferSpread() const 75 | { 76 | return bidOfferSpread; 77 | } 78 | 79 | template 80 | ostream& operator<<(ostream& os, const Price& price) 81 | { 82 | T product = price.GetProduct(); 83 | string _product = product.GetProductId(); 84 | double mid = price.GetMid(); 85 | double bidOfferSpread = price.GetBidOfferSpread(); 86 | string _mid = convertPrice(mid); 87 | string _bidOfferSpread = convertPrice(bidOfferSpread); 88 | 89 | vector _strings; 90 | _strings.push_back(_product); 91 | _strings.push_back(_mid); 92 | _strings.push_back(_bidOfferSpread); 93 | string _str = join(_strings, ","); 94 | os << _str; 95 | 96 | return os; 97 | } 98 | 99 | // forward declaration of PriceDataConnector 100 | template 101 | class PriceDataConnector; 102 | 103 | /** 104 | * Pricing Service managing mid prices and bid/offers. 105 | * Keyed on product identifier. 106 | * Type T is the product type. 107 | */ 108 | template 109 | class PricingService : public Service> 110 | { 111 | private: 112 | map> priceMap; // store price data keyed by product identifier 113 | vector>*> listeners; // list of listeners to this service 114 | string host; // host name for inbound connector 115 | string port; // port number for inbound connector 116 | PriceDataConnector* connector; // connector related to this server 117 | 118 | public: 119 | // ctor 120 | PricingService(const string& _host, const string& _port); 121 | // dtor 122 | ~PricingService() = default; 123 | 124 | 125 | // Get data on our service given a key 126 | Price& GetData(string key) override; 127 | 128 | // The callback that a Connector should invoke for any new or updated data 129 | void OnMessage(Price &data) override; 130 | 131 | // Add a listener to the Service for callbacks on add, remove, and update events 132 | // for data to the Service. 133 | void AddListener(ServiceListener> *listener) override; 134 | 135 | // Get all listeners on the Service. 136 | const vector< ServiceListener>* >& GetListeners() const override; 137 | 138 | // Get the connector 139 | PriceDataConnector* GetConnector(); 140 | 141 | }; 142 | 143 | template 144 | PricingService::PricingService(const string& _host, const string& _port) 145 | : host(_host), port(_port) 146 | { 147 | connector = new PriceDataConnector(this, host, port); // connector related to this server 148 | } 149 | 150 | template 151 | Price& PricingService::GetData(string key) 152 | { 153 | return priceMap[key]; 154 | } 155 | 156 | template 157 | void PricingService::OnMessage(Price &data) 158 | { 159 | // flow data 160 | string key = data.GetProduct().GetProductId(); 161 | // update the price map 162 | if (priceMap.find(key) != priceMap.end()) {priceMap.erase(key);} 163 | priceMap.insert(pair > (key, data)); 164 | 165 | // flow the data to listeners 166 | for (auto& l : listeners) { 167 | l -> ProcessAdd(data); 168 | } 169 | } 170 | 171 | template 172 | void PricingService::AddListener(ServiceListener> *listener) 173 | { 174 | listeners.push_back(listener); 175 | } 176 | 177 | template 178 | const vector< ServiceListener>* >& PricingService::GetListeners() const 179 | { 180 | return listeners; 181 | } 182 | 183 | template 184 | PriceDataConnector* PricingService::GetConnector() 185 | { 186 | return connector; 187 | } 188 | 189 | /** 190 | * PriceDataConnector: an inbound connector that subscribes data from socket to pricing service. 191 | * Type T is the product type. 192 | */ 193 | template 194 | class PriceDataConnector : public Connector> 195 | { 196 | private: 197 | PricingService* service; 198 | string host; // host name 199 | string port; // port number 200 | boost::asio::io_service io_service; // io service 201 | boost::asio::ip::tcp::socket socket; // socket 202 | 203 | void handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request); 204 | void start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service); 205 | 206 | public: 207 | // ctor 208 | PriceDataConnector(PricingService* _service, const string& _host, const string& _port); 209 | // dtor 210 | ~PriceDataConnector(); 211 | 212 | // Publish data to the Connector 213 | void Publish(Price &data) override; 214 | 215 | // Subscribe data from socket 216 | void Subscribe(); 217 | 218 | }; 219 | 220 | template 221 | PriceDataConnector::PriceDataConnector(PricingService* _service, const string& _host, const string& _port) 222 | : service(_service), host(_host), port(_port), socket(io_service) 223 | { 224 | } 225 | 226 | template 227 | PriceDataConnector::~PriceDataConnector() 228 | { 229 | socket.close(); 230 | } 231 | 232 | template 233 | void PriceDataConnector::start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service) { 234 | boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*io_service); 235 | acceptor->async_accept(*socket, [this, socket, acceptor, io_service](const boost::system::error_code& ec) { 236 | if (!ec) { 237 | boost::asio::streambuf* request = new boost::asio::streambuf; 238 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&PriceDataConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 239 | } 240 | start_accept(acceptor, io_service); // accept the next connection 241 | }); 242 | } 243 | 244 | template 245 | void PriceDataConnector::handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request) { 246 | if (!ec) { 247 | // get the entire data 248 | std::string data = std::string(boost::asio::buffers_begin(request->data()), boost::asio::buffers_end(request->data())); 249 | // find the last newline 250 | std::size_t last_newline = data.rfind('\n'); 251 | if (last_newline != std::string::npos) { 252 | // consume only up to the last newline 253 | request->consume(last_newline + 1); 254 | // only process the data up to the last newline 255 | data = data.substr(0, last_newline); 256 | } else { 257 | // if there's no newline, don't process any data 258 | data.clear(); 259 | } 260 | 261 | // split the data into lines 262 | std::stringstream ss(data); 263 | std::string line; 264 | while (std::getline(ss, line)) { 265 | // process each line 266 | vector lineData; 267 | stringstream lineStream(line); // turn the line into a stream 268 | string word; 269 | while (getline(lineStream, word, ',')) 270 | { 271 | lineData.push_back(word); 272 | } 273 | string timestamp = lineData[0]; 274 | string productId = lineData[1]; 275 | double bid = convertPrice(lineData[2]); 276 | double ask = convertPrice(lineData[3]); 277 | double spread = stod(lineData[4]); 278 | double mid = (bid + ask) / 2.0; 279 | // create product object based on product id 280 | T product = getProductObject(productId); 281 | // create price object based on product, mid price and bid/offer spread 282 | Price price(product, mid, spread); 283 | // publish data to service 284 | service->OnMessage(price); 285 | } 286 | 287 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&PriceDataConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 288 | } else { 289 | delete request; // delete the streambuf when we're done with it 290 | delete socket; // delete the socket when we're done with it 291 | } 292 | } 293 | // inbound connector, does nothing 294 | template 295 | void PriceDataConnector::Publish(Price &data) 296 | { 297 | } 298 | 299 | template 300 | void PriceDataConnector::Subscribe() 301 | { 302 | log(LogLevel::NOTE, "Price data server listening on " + host + ":" + port); 303 | try { 304 | // connect to the socket 305 | boost::asio::ip::tcp::resolver resolver(io_service); 306 | boost::asio::ip::tcp::resolver::query query(host, port); 307 | boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); 308 | boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint); 309 | start_accept(&acceptor, &io_service); 310 | io_service.run(); 311 | } 312 | catch (std::exception& e){ 313 | // throw error log 314 | log(LogLevel::ERROR, e.what()); 315 | socket.close(); 316 | return; 317 | } 318 | } 319 | 320 | #endif 321 | -------------------------------------------------------------------------------- /headers/riskservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * riskservice.hpp 3 | * Defines the data types and Service for fixed income risk. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | #ifndef RISK_SERVICE_HPP 8 | #define RISK_SERVICE_HPP 9 | 10 | #include "soa.hpp" 11 | #include "positionservice.hpp" 12 | #include "utils.hpp" 13 | 14 | /** 15 | * PV01 risk. 16 | * Type T is the product type. 17 | */ 18 | template 19 | class PV01 20 | { 21 | 22 | public: 23 | 24 | // Default ctor 25 | PV01() = default; 26 | // ctor for a PV01 value 27 | PV01(const T &_product, double _pv01, long _quantity); 28 | 29 | // Get the product on this PV01 value 30 | const T& GetProduct() const; 31 | 32 | // Get the PV01 value 33 | double GetPV01() const; 34 | 35 | // Get the quantity that this risk value is associated with 36 | long GetQuantity() const; 37 | 38 | // Add quantity associated with this risk value 39 | void AddQuantity(long _quantity); 40 | 41 | // Object printer 42 | template 43 | friend ostream& operator<<(ostream& os, const PV01& pv01); 44 | 45 | private: 46 | T product; 47 | double pv01; 48 | long quantity; 49 | 50 | }; 51 | 52 | template 53 | PV01::PV01(const T &_product, double _pv01, long _quantity) : 54 | product(_product) 55 | { 56 | pv01 = _pv01; 57 | quantity = _quantity; 58 | } 59 | 60 | template 61 | const T& PV01::GetProduct() const 62 | { 63 | return product; 64 | } 65 | 66 | template 67 | double PV01::GetPV01() const 68 | { 69 | return pv01; 70 | } 71 | 72 | template 73 | long PV01::GetQuantity() const 74 | { 75 | return quantity; 76 | } 77 | 78 | template 79 | void PV01::AddQuantity(long _quantity) 80 | { 81 | quantity += _quantity; 82 | } 83 | 84 | template 85 | ostream& operator<<(ostream& os, const PV01& pv01) 86 | { 87 | T product = pv01.GetProduct(); 88 | string _product = product.GetProductId(); 89 | double pv01_value = pv01.GetPV01(); 90 | long quantity = pv01.GetQuantity(); 91 | string _pv01 = to_string(pv01_value); 92 | string _quantity = to_string(quantity); 93 | 94 | vector _strings; 95 | _strings.push_back(_product); 96 | _strings.push_back(_pv01); 97 | _strings.push_back(_quantity); 98 | string _str = join(_strings, ","); 99 | os << _str; 100 | return os; 101 | } 102 | 103 | /** 104 | * A bucket sector to bucket a group of securities. 105 | * We can then aggregate bucketed risk to this bucket. 106 | * Type T is the product type. 107 | */ 108 | template 109 | class BucketedSector 110 | { 111 | public: 112 | // ctor for a bucket sector 113 | BucketedSector(const vector &_products, string _name); 114 | 115 | // Get the products associated with this bucket 116 | const vector& GetProducts() const; 117 | 118 | // Get the name of the bucket 119 | const string& GetName() const; 120 | 121 | private: 122 | vector products; 123 | string name; 124 | 125 | }; 126 | 127 | template 128 | BucketedSector::BucketedSector(const vector& _products, string _name) : 129 | products(_products) 130 | { 131 | name = _name; 132 | } 133 | 134 | template 135 | const vector& BucketedSector::GetProducts() const 136 | { 137 | return products; 138 | } 139 | 140 | template 141 | const string& BucketedSector::GetName() const 142 | { 143 | return name; 144 | } 145 | 146 | // forward declaration of RiskServiceListener 147 | template 148 | class RiskServiceListener; 149 | 150 | 151 | /** 152 | * Risk Service to vend out risk for a particular security and across a risk bucketed sector. 153 | * Keyed on product identifier. 154 | * Type T is the product type. 155 | */ 156 | template 157 | class RiskService : public Service > 158 | { 159 | private: 160 | vector>*> listeners; 161 | map> pv01Map; 162 | RiskServiceListener* riskservicelistener; 163 | 164 | public: 165 | // ctor and dtor 166 | RiskService(); 167 | ~RiskService()=default; 168 | 169 | // Get data on our service given a key 170 | PV01& GetData(string key); 171 | 172 | // The callback that a Connector should invoke for any new or updated data 173 | void OnMessage(PV01 &data); 174 | 175 | // Add a listener to the Service for callbacks on add, remove, and update events 176 | // for data to the Service. 177 | void AddListener(ServiceListener> *listener); 178 | 179 | // Get all listeners on the Service. 180 | const vector< ServiceListener>* >& GetListeners() const; 181 | 182 | // Get the special listener for risk service 183 | RiskServiceListener* GetRiskServiceListener(); 184 | 185 | // Add a position that the service will risk 186 | void AddPosition(Position &position); 187 | 188 | // Get the bucketed risk for the bucket sector 189 | const PV01< BucketedSector >& GetBucketedRisk(const BucketedSector §or) const; 190 | 191 | }; 192 | 193 | template 194 | RiskService::RiskService() 195 | { 196 | riskservicelistener = new RiskServiceListener(this); 197 | } 198 | 199 | template 200 | PV01& RiskService::GetData(string key) 201 | { 202 | return pv01Map[key]; 203 | } 204 | 205 | /** 206 | * OnMessage() used to be called by connector to subscribe data 207 | * no need to implement here. 208 | */ 209 | template 210 | void RiskService::OnMessage(PV01 &data) 211 | { 212 | } 213 | 214 | template 215 | void RiskService::AddListener(ServiceListener> *listener) 216 | { 217 | listeners.push_back(listener); 218 | } 219 | 220 | template 221 | const vector< ServiceListener>* >& RiskService::GetListeners() const 222 | { 223 | return listeners; 224 | } 225 | 226 | template 227 | RiskServiceListener* RiskService::GetRiskServiceListener() 228 | { 229 | return riskservicelistener; 230 | } 231 | 232 | template 233 | void RiskService::AddPosition(Position &position) 234 | { 235 | T product = position.GetProduct(); 236 | string productId = product.GetProductId(); 237 | long quantity = position.GetAggregatePosition(); 238 | // note: this gives the PV01 value for a single unit 239 | double pv01Val = getPV01(productId); 240 | 241 | // create a PV01 object and publish it to the service 242 | PV01 pv01(product, pv01Val, quantity); 243 | if (pv01Map.find(productId) != pv01Map.end()){ 244 | pv01Map[productId].AddQuantity(quantity); 245 | }else{ 246 | pv01Map.insert(pair>(productId, pv01)); 247 | } 248 | 249 | // notify listeners 250 | for(auto& listener : listeners) 251 | listener->ProcessAdd(pv01); 252 | } 253 | 254 | template 255 | const PV01>& RiskService::GetBucketedRisk(const BucketedSector §or) const 256 | { 257 | // create a bucketed sector object and publish it to the service 258 | vector& products = sector.GetProducts(); 259 | string name = sector.GetName(); 260 | double pv01Val = 0.0; 261 | long quantity = 0; 262 | for (auto& product : products){ 263 | string productId = product.GetProductId(); 264 | if (pv01Map.find(productId) != pv01Map.end()){ 265 | // total pv01 value for the sector is the weighted average 266 | pv01Val += pv01Map[productId].GetPV01()*pv01Map[productId].GetQuantity(); 267 | // total quantity for the sector 268 | quantity += pv01Map[productId].GetQuantity(); 269 | } 270 | } 271 | // note: for PV01 object of a sector, we store the total PV01 value instead of a single unit 272 | PV01> pv01(sector, pv01Val, quantity); 273 | return pv01; 274 | } 275 | 276 | 277 | /** 278 | * Risk Service Listener subscribing data from Position Service to Risk Service. 279 | * Type T is the product type. 280 | */ 281 | template 282 | class RiskServiceListener : public ServiceListener> 283 | { 284 | private: 285 | RiskService* riskservice; 286 | 287 | public: 288 | // ctor and dtor 289 | RiskServiceListener(RiskService* _riskservice); 290 | ~RiskServiceListener()=default; 291 | 292 | // Listener callback to process an add event to the Service 293 | void ProcessAdd(Position &data); 294 | 295 | // Listener callback to process a remove event to the Service 296 | void ProcessRemove(Position &data); 297 | 298 | // Listener callback to process an update event to the Service 299 | void ProcessUpdate(Position &data); 300 | 301 | }; 302 | 303 | template 304 | RiskServiceListener::RiskServiceListener(RiskService* _riskservice) 305 | { 306 | riskservice = _riskservice; 307 | } 308 | 309 | /** 310 | * ProcessAdd() method is used to transfer Position data to PV01 data, 311 | * then publish the PV01 data to Risk Service. 312 | */ 313 | template 314 | void RiskServiceListener::ProcessAdd(Position &data) 315 | { 316 | riskservice->AddPosition(data); 317 | } 318 | 319 | template 320 | void RiskServiceListener::ProcessRemove(Position &data) 321 | { 322 | } 323 | 324 | template 325 | void RiskServiceListener::ProcessUpdate(Position &data) 326 | { 327 | } 328 | 329 | 330 | 331 | #endif 332 | -------------------------------------------------------------------------------- /headers/soa.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * soa.hpp 3 | * Definition of our Service Oriented Architecture (SOA) Service base class 4 | * 5 | * @author Boyu Yang 6 | */ 7 | 8 | #ifndef SOA_HPP 9 | #define SOA_HPP 10 | 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | /** 17 | * Definition of a generic base class ServiceListener to listen to add, update, and remove 18 | * events on a Service. This listener should be registered on a Service for the Service 19 | * to notify all listeners for these events. 20 | */ 21 | template 22 | class ServiceListener 23 | { 24 | 25 | public: 26 | 27 | // Listener callback to process an add event to the Service 28 | virtual void ProcessAdd(V &data) = 0; 29 | 30 | // Listener callback to process a remove event to the Service 31 | virtual void ProcessRemove(V &data) = 0; 32 | 33 | // Listener callback to process an update event to the Service 34 | virtual void ProcessUpdate(V &data) = 0; 35 | 36 | }; 37 | 38 | /** 39 | * Definition of a generic base class Service. 40 | * Uses key generic type K and value generic type V. 41 | */ 42 | template 43 | class Service 44 | { 45 | 46 | public: 47 | 48 | // Get data on our service given a key 49 | virtual V& GetData(K key) = 0; 50 | 51 | // The callback that a Connector should invoke for any new or updated data 52 | virtual void OnMessage(V &data) = 0; 53 | 54 | // Add a listener to the Service for callbacks on add, remove, and update events 55 | // for data to the Service. 56 | virtual void AddListener(ServiceListener *listener) = 0; 57 | 58 | // Get all listeners on the Service. 59 | virtual const vector< ServiceListener* >& GetListeners() const = 0; 60 | 61 | }; 62 | 63 | /** 64 | * Definition of a Connector class. 65 | * This will invoke the Service.OnMessage() method for subscriber Connectors 66 | * to push data to the Service. 67 | * Services can invoke the Publish() method on this Service to publish data to the Connector 68 | * for a publisher Connector. 69 | * Note that a Connector can be publisher-only, subscriber-only, or both publisher and susbcriber. 70 | */ 71 | template 72 | class Connector 73 | { 74 | 75 | public: 76 | 77 | // Publish data to the Connector 78 | virtual void Publish(V &data) = 0; 79 | 80 | }; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /headers/streamingservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * streamingservice.hpp 3 | * Defines the data types and Service for price streams. 4 | * 5 | * @author Boyu Yang 6 | */ 7 | #ifndef STREAMING_SERVICE_HPP 8 | #define STREAMING_SERVICE_HPP 9 | 10 | #include "soa.hpp" 11 | #include "algostreamingservice.hpp" 12 | 13 | /** 14 | * Forward declaration of StreamOutputConnector and StreamingServiceListener. 15 | * As described, streaming service needs a publish-only connector to publish streams (which notify the listener and print the execution data) 16 | * into a separate process which listens to the streams on the socket via its own Connector. 17 | * Type T is the product type. 18 | */ 19 | template 20 | class StreamOutputConnector; 21 | template 22 | class StreamingServiceListener; 23 | 24 | /** 25 | * Streaming service to publish two-way prices. 26 | * Keyed on product identifier. 27 | * Type T is the product type. 28 | */ 29 | template 30 | class StreamingService : public Service > 31 | { 32 | private: 33 | map> priceStreamMap; // store price stream data keyed by product identifier 34 | vector>*> listeners; // list of listeners to this service 35 | string host; // host name for inbound connector 36 | string port; // port number for inbound connector 37 | StreamOutputConnector* connector; // connector related to this server 38 | StreamingServiceListener* streamingservicelistener; // listener related to this server 39 | 40 | public: 41 | // ctor and dtor 42 | StreamingService(const string& _host, const string& _port); 43 | ~StreamingService()=default; 44 | 45 | // Get data on our service given a key 46 | PriceStream& GetData(string key) override; 47 | 48 | // The callback that a Connector should invoke for any new or updated data 49 | void OnMessage(PriceStream& data) override; 50 | 51 | // Add a listener to the Service for callbacks on add, remove, and update events for data to the Service 52 | void AddListener(ServiceListener> *listener) override; 53 | 54 | // Get all listeners on the Service. 55 | const vector< ServiceListener>* >& GetListeners() const override; 56 | 57 | // Get the special listener for streaming service 58 | StreamingServiceListener* GetStreamingServiceListener(); 59 | 60 | // Get the connector 61 | StreamOutputConnector* GetConnector(); 62 | 63 | // Publish two-way prices (called by the publish-only connector to publish streams) 64 | void PublishPrice(const PriceStream& priceStream); 65 | 66 | // called by streaming service listener to subscribe data from algo streaming service 67 | void AddPriceStream(const AlgoStream& algoStream); 68 | 69 | }; 70 | 71 | template 72 | StreamingService::StreamingService(const string& _host, const string& _port) 73 | { 74 | host = _host; 75 | port = _port; 76 | connector = new StreamOutputConnector(this, host, port); // connector related to this server 77 | streamingservicelistener = new StreamingServiceListener(this); // listener related to this server 78 | } 79 | 80 | template 81 | PriceStream& StreamingService::GetData(string key) 82 | { 83 | return priceStreamMap[key]; 84 | } 85 | 86 | /** 87 | * OnMessage() used to be called by input connector to subscribe data 88 | * no need to implement here. 89 | */ 90 | template 91 | void StreamingService::OnMessage(PriceStream& data) 92 | { 93 | } 94 | 95 | template 96 | void StreamingService::AddListener(ServiceListener> *listener) 97 | { 98 | listeners.push_back(listener); 99 | } 100 | 101 | template 102 | const vector< ServiceListener>* >& StreamingService::GetListeners() const 103 | { 104 | return listeners; 105 | } 106 | 107 | template 108 | StreamingServiceListener* StreamingService::GetStreamingServiceListener() 109 | { 110 | return streamingservicelistener; 111 | } 112 | 113 | template 114 | StreamOutputConnector* StreamingService::GetConnector() 115 | { 116 | return connector; 117 | } 118 | 119 | // call the publish-only connector to publish streams 120 | template 121 | void StreamingService::PublishPrice(const PriceStream& priceStream) 122 | { 123 | connector->Publish(priceStream); 124 | } 125 | 126 | // called by streaming service listener to subscribe data from algo streaming service 127 | template 128 | void StreamingService::AddPriceStream(const AlgoStream& algoStream) 129 | { 130 | PriceStream priceStream = algoStream.GetPriceStream(); 131 | string key = priceStream.GetProduct().GetProductId(); 132 | // update the price stream map 133 | if (priceStreamMap.find(key) != priceStreamMap.end()) {priceStreamMap.erase(key);} 134 | priceStreamMap.insert(pair > (key, priceStream)); 135 | 136 | // flow the data to listeners 137 | for (auto& l : listeners) { 138 | l -> ProcessAdd(priceStream); 139 | } 140 | } 141 | 142 | /** 143 | * StreamOutputConnector: publish data to socket. 144 | * Type T is the product type. 145 | */ 146 | template 147 | class StreamOutputConnector 148 | { 149 | private: 150 | StreamingService* service; 151 | string host; // host name 152 | string port; // port number 153 | boost::asio::io_service io_service; // io service 154 | boost::asio::ip::tcp::socket socket; // socket 155 | 156 | void handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request); 157 | void start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service); 158 | 159 | public: 160 | // ctor 161 | StreamOutputConnector(StreamingService* _service, const string& _host, const string& _port); 162 | // dtor: close the socket 163 | ~StreamOutputConnector(); 164 | 165 | // Publish data to the socket 166 | void Publish(const PriceStream& data); 167 | 168 | // Here the subscribe is used to open a process and listen to the socket 169 | void Subscribe(); 170 | 171 | }; 172 | 173 | template 174 | StreamOutputConnector::StreamOutputConnector(StreamingService* _service, const string& _host, const string& _port) 175 | : service(_service), host(_host), port(_port), socket(io_service) 176 | { 177 | 178 | } 179 | 180 | template 181 | StreamOutputConnector::~StreamOutputConnector() 182 | { 183 | socket.close(); 184 | } 185 | 186 | template 187 | void StreamOutputConnector::start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service) { 188 | boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*io_service); 189 | acceptor->async_accept(*socket, [this, socket, acceptor, io_service](const boost::system::error_code& ec) { 190 | if (!ec) { 191 | boost::asio::streambuf* request = new boost::asio::streambuf; 192 | boost::asio::async_read_until(*socket, *request, "\r", std::bind(&StreamOutputConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 193 | } 194 | start_accept(acceptor, io_service); // accept the next connection 195 | }); 196 | } 197 | 198 | template 199 | void StreamOutputConnector::handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request) { 200 | if (!ec) { 201 | // get the entire data 202 | std::string data = std::string(boost::asio::buffers_begin(request->data()), boost::asio::buffers_end(request->data())); 203 | // find the last newline 204 | std::size_t last_newline = data.rfind('\r'); 205 | if (last_newline != std::string::npos) { 206 | // consume only up to the last newline 207 | request->consume(last_newline + 1); 208 | // only process the data up to the last newline 209 | data = data.substr(0, last_newline); 210 | } else { 211 | // if there's no newline, don't process any data 212 | data.clear(); 213 | } 214 | // server receives and prints data 215 | cout << data << endl; 216 | 217 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&StreamOutputConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 218 | } else { 219 | delete request; // delete the streambuf when we're done with it 220 | delete socket; // delete the socket when we're done with it 221 | } 222 | } 223 | 224 | /** 225 | * Publish() method is used by the publish-only connector to publish streams. 226 | */ 227 | template 228 | void StreamOutputConnector::Publish(const PriceStream& data) 229 | { 230 | // connect to the socket 231 | boost::asio::ip::tcp::resolver resolver(io_service); 232 | boost::asio::ip::tcp::resolver::query query(host, port); 233 | boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); 234 | boost::asio::connect(socket, endpoint_iterator); 235 | 236 | // print the price stream data 237 | T product = data.GetProduct(); 238 | string productId = product.GetProductId(); 239 | PriceStreamOrder bid = data.GetBidOrder(); 240 | PriceStreamOrder offer = data.GetOfferOrder(); 241 | 242 | string dataLine = string("Price Stream ") + "(Product " + productId + "): \n" 243 | + "\tBid\t" + "Price: " + std::to_string(bid.GetPrice()) + "\tVisibleQuantity: " + std::to_string(bid.GetVisibleQuantity()) 244 | + "\tHiddenQuantity: " + std::to_string(bid.GetHiddenQuantity()) + "\n" 245 | + "\tAsk\t" + "Price: " + std::to_string(offer.GetPrice()) + "\tVisibleQuantity: " + std::to_string(offer.GetVisibleQuantity()) 246 | + "\tHiddenQuantity: " + std::to_string(offer.GetHiddenQuantity()) + "\n"; 247 | 248 | // publish the data string to socket 249 | // asynchronous operation ensures server gets all data 250 | boost::asio::async_write(socket, boost::asio::buffer(dataLine + "\r"), [](boost::system::error_code /*ec*/, std::size_t /*length*/) {}); 251 | } 252 | 253 | template 254 | void StreamOutputConnector::Subscribe() 255 | { 256 | log(LogLevel::NOTE, "Streaming output server listening on " + host + ":" + port); 257 | try { 258 | // connect to the socket 259 | boost::asio::ip::tcp::resolver resolver(io_service); 260 | boost::asio::ip::tcp::resolver::query query(host, port); 261 | boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); 262 | boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint); 263 | start_accept(&acceptor, &io_service); 264 | io_service.run(); 265 | } 266 | catch (std::exception& e){ 267 | // throw error log 268 | log(LogLevel::ERROR, e.what()); 269 | return; 270 | } 271 | } 272 | 273 | /** 274 | * Streaming Service Listener subscribing data from Algo Streaming Service to Streaming Service. 275 | * Type T is the product type. 276 | */ 277 | template 278 | class StreamingServiceListener : public ServiceListener> 279 | { 280 | private: 281 | StreamingService* streamingService; 282 | 283 | public: 284 | // ctor 285 | StreamingServiceListener(StreamingService* _streamingService); 286 | 287 | // Listener callback to process an add event to the Service 288 | void ProcessAdd(AlgoStream& data) override; 289 | 290 | // Listener callback to process a remove event to the Service 291 | void ProcessRemove(AlgoStream& data) override; 292 | 293 | // Listener callback to process an update event to the Service 294 | void ProcessUpdate(AlgoStream& data) override; 295 | 296 | }; 297 | 298 | template 299 | StreamingServiceListener::StreamingServiceListener(StreamingService* _streamingService) 300 | { 301 | streamingService = _streamingService; 302 | } 303 | 304 | /** 305 | * ProcessAdd() method is used by listener to subscribe data from Algo Streaming Service to Streaming Service. 306 | * It calls PublishPrice() method and the AddPriceStream() method 307 | */ 308 | template 309 | void StreamingServiceListener::ProcessAdd(AlgoStream& data) 310 | { 311 | // save algo stream info into streaming service 312 | // directly pass in AlgoStream type and transit to PriceStream type inside the function 313 | streamingService->AddPriceStream(data); 314 | 315 | // call the connector to publish price streams 316 | PriceStream priceStream = data.GetPriceStream(); 317 | streamingService->PublishPrice(priceStream); 318 | } 319 | 320 | template 321 | void StreamingServiceListener::ProcessRemove(AlgoStream& data) 322 | { 323 | } 324 | 325 | template 326 | void StreamingServiceListener::ProcessUpdate(AlgoStream& data) 327 | { 328 | } 329 | 330 | #endif 331 | -------------------------------------------------------------------------------- /headers/tradebookingservice.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * tradebookingservice.hpp 3 | * Defines the data types and Service for trade booking. 4 | * Trade booking service has a special listener to subscribe data from execution service. 5 | * 6 | * @author Boyu Yang 7 | */ 8 | #ifndef TRADE_BOOKING_SERVICE_HPP 9 | #define TRADE_BOOKING_SERVICE_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "soa.hpp" 19 | #include "utils.hpp" 20 | #include "executionservice.hpp" 21 | 22 | // Trade sides 23 | enum Side { BUY, SELL }; 24 | 25 | /** 26 | * Trade object with a price, side, and quantity on a particular book. 27 | * Type T is the product type. 28 | */ 29 | template 30 | class Trade 31 | { 32 | 33 | public: 34 | 35 | // ctor for a trade 36 | Trade() = default; 37 | Trade(const T &_product, string _tradeId, double _price, string _book, long _quantity, Side _side); 38 | 39 | // Get the product 40 | const T& GetProduct() const; 41 | 42 | // Get the trade ID 43 | const string& GetTradeId() const; 44 | 45 | // Get the mid price 46 | double GetPrice() const; 47 | 48 | // Get the book 49 | const string& GetBook() const; 50 | 51 | // Get the quantity 52 | long GetQuantity() const; 53 | 54 | // Get the side 55 | Side GetSide() const; 56 | 57 | private: 58 | T product; 59 | string tradeId; 60 | double price; 61 | string book; 62 | long quantity; 63 | Side side; 64 | 65 | }; 66 | 67 | 68 | template 69 | Trade::Trade(const T &_product, string _tradeId, double _price, string _book, long _quantity, Side _side) : 70 | product(_product) 71 | { 72 | tradeId = _tradeId; 73 | price = _price; 74 | book = _book; 75 | quantity = _quantity; 76 | side = _side; 77 | } 78 | 79 | template 80 | const T& Trade::GetProduct() const 81 | { 82 | return product; 83 | } 84 | 85 | template 86 | const string& Trade::GetTradeId() const 87 | { 88 | return tradeId; 89 | } 90 | 91 | template 92 | double Trade::GetPrice() const 93 | { 94 | return price; 95 | } 96 | 97 | template 98 | const string& Trade::GetBook() const 99 | { 100 | return book; 101 | } 102 | 103 | template 104 | long Trade::GetQuantity() const 105 | { 106 | return quantity; 107 | } 108 | 109 | template 110 | Side Trade::GetSide() const 111 | { 112 | return side; 113 | } 114 | 115 | // forward declaration of an inbound connector and a trade booking listener that subscribes data from execution service 116 | template 117 | class TradeBookingServiceListener; 118 | template 119 | class TradeDataConnector; 120 | 121 | /** 122 | * Trade Booking Service to book trades to a particular book. 123 | * Keyed on trade id. 124 | * Type T is the product type. 125 | */ 126 | template 127 | class TradeBookingService : public Service > 128 | { 129 | private: 130 | map> tradeMap; // store trade data keyed by trade id 131 | vector>*> listeners; // list of listeners to this service 132 | TradeBookingServiceListener* tradebookinglistener; 133 | string host; // host name for inbound connector 134 | string port; // port number for inbound connector 135 | TradeDataConnector* connector; // connector related to this server 136 | 137 | public: 138 | // ctor and dtor 139 | TradeBookingService(const string& _host, const string& _port); 140 | ~TradeBookingService()=default; 141 | 142 | // Get data 143 | Trade& GetData(string key); 144 | 145 | // The callback that a Connector should invoke for any new or updated data 146 | void OnMessage(Trade &data); 147 | 148 | // Add a listener to the Service for callbacks on add, remove, and update events 149 | // for data to the Service. 150 | void AddListener(ServiceListener> *listener); 151 | 152 | // Get all listeners on the Service. 153 | const vector< ServiceListener>* >& GetListeners() const; 154 | 155 | // get the connector 156 | TradeDataConnector* GetConnector(); 157 | 158 | // Get associated trade book listener 159 | TradeBookingServiceListener* GetTradeBookingServiceListener(); 160 | 161 | void BookTrade(Trade &trade); 162 | 163 | }; 164 | 165 | template 166 | TradeBookingService::TradeBookingService(const string& _host, const string& _port) 167 | : host(_host), port(_port) 168 | { 169 | connector = new TradeDataConnector(this, host, port); // connector related to this server 170 | tradebookinglistener = new TradeBookingServiceListener(this); // listener related to this server 171 | } 172 | 173 | 174 | template 175 | Trade& TradeBookingService::GetData(string key) 176 | { 177 | return tradeMap[key]; 178 | } 179 | 180 | template 181 | void TradeBookingService::OnMessage(Trade &data) 182 | { 183 | string key = data.GetTradeId(); 184 | if (tradeMap.find(key) != tradeMap.end()) 185 | tradeMap[key] = data; 186 | else 187 | tradeMap.insert(pair>(key, data)); 188 | 189 | for(auto& listener : listeners) 190 | listener->ProcessAdd(data); 191 | } 192 | 193 | template 194 | void TradeBookingService::AddListener(ServiceListener> *listener) 195 | { 196 | listeners.push_back(listener); 197 | } 198 | 199 | template 200 | const vector< ServiceListener>* >& TradeBookingService::GetListeners() const 201 | { 202 | return listeners; 203 | } 204 | 205 | template 206 | TradeDataConnector* TradeBookingService::GetConnector() 207 | { 208 | return connector; 209 | } 210 | 211 | template 212 | TradeBookingServiceListener* TradeBookingService::GetTradeBookingServiceListener() 213 | { 214 | return tradebookinglistener; 215 | } 216 | 217 | 218 | /** 219 | * Book a trade and send the information to the listener 220 | */ 221 | template 222 | void TradeBookingService::BookTrade(Trade &trade) 223 | { 224 | // flow the data to listeners 225 | // before this, make sure the special listener is added to the service 226 | // As we notify the special listener, ProcessAdd() will be called to connect data between different services 227 | for(auto& l : listeners) 228 | l->ProcessAdd(trade); 229 | 230 | } 231 | 232 | /** 233 | * Connector that subscribes data from socket to trade booking service. 234 | * Type T is the product type. 235 | */ 236 | template 237 | class TradeDataConnector : public Connector> 238 | { 239 | private: 240 | TradeBookingService* service; 241 | string host; // host name 242 | string port; // port number 243 | boost::asio::io_service io_service; // io service 244 | boost::asio::ip::tcp::socket socket; // socket 245 | 246 | void handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request); 247 | void start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service); 248 | 249 | public: 250 | // ctor 251 | TradeDataConnector(TradeBookingService* _service, const string& _host, const string& _port); 252 | // dtor: close the socket 253 | ~TradeDataConnector(); 254 | 255 | // Publish data to the Connector 256 | void Publish(Trade &data) override; 257 | 258 | // Subscribe data from the Connector 259 | void Subscribe(); 260 | 261 | }; 262 | 263 | template 264 | TradeDataConnector::TradeDataConnector(TradeBookingService* _service, const string& _host, const string& _port) 265 | : service(_service), host(_host), port(_port), socket(io_service) 266 | { 267 | } 268 | 269 | template 270 | TradeDataConnector::~TradeDataConnector() 271 | { 272 | socket.close(); 273 | } 274 | 275 | template 276 | void TradeDataConnector::start_accept(boost::asio::ip::tcp::acceptor* acceptor, boost::asio::io_service* io_service) { 277 | boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*io_service); 278 | acceptor->async_accept(*socket, [this, socket, acceptor, io_service](const boost::system::error_code& ec) { 279 | if (!ec) { 280 | boost::asio::streambuf* request = new boost::asio::streambuf; 281 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&TradeDataConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 282 | } 283 | start_accept(acceptor, io_service); // accept the next connection 284 | }); 285 | } 286 | 287 | 288 | template 289 | void TradeDataConnector::handle_read(const boost::system::error_code& ec, std::size_t length, boost::asio::ip::tcp::socket* socket, boost::asio::streambuf* request) { 290 | if (!ec) { 291 | std::string data = std::string(boost::asio::buffers_begin(request->data()), boost::asio::buffers_end(request->data())); 292 | // find the last newline 293 | std::size_t last_newline = data.rfind('\n'); 294 | if (last_newline != std::string::npos) { 295 | // consume only up to the last newline 296 | request->consume(last_newline + 1); 297 | // only process the data up to the last newline 298 | data = data.substr(0, last_newline); 299 | } else { 300 | // if there's no newline, don't process any data 301 | data.clear(); 302 | } 303 | 304 | // split the data into lines 305 | std::stringstream ss(data); 306 | std::string line; 307 | while (std::getline(ss, line)) { 308 | // parse the line 309 | vector tokens; 310 | stringstream lineStream(line); 311 | string token; 312 | while(getline(lineStream, token, ',')) 313 | tokens.push_back(token); 314 | 315 | // create a trade object 316 | string productId = tokens[0]; 317 | // create product object based on product id 318 | T product = getProductObject(productId); 319 | string tradeId = tokens[1]; 320 | double price = convertPrice(tokens[2]); 321 | string book = tokens[3]; 322 | long quantity = stol(tokens[4]); 323 | Side side = tokens[5] == "BUY" ? BUY : SELL; 324 | Trade trade(product, tradeId, price, book, quantity, side); 325 | 326 | // flows data to trade booking service 327 | service->OnMessage(trade); 328 | } 329 | 330 | boost::asio::async_read_until(*socket, *request, "\n", std::bind(&TradeDataConnector::handle_read, this, std::placeholders::_1, std::placeholders::_2, socket, request)); 331 | } else { 332 | delete request; // delete the streambuf when we're done with it 333 | delete socket; // delete the socket when we're done with it 334 | } 335 | } 336 | 337 | template 338 | void TradeDataConnector::Publish(Trade &data) 339 | { 340 | } 341 | 342 | template 343 | void TradeDataConnector::Subscribe() 344 | { 345 | log(LogLevel::NOTE, "Trade data server listening on " + host + ":" + port); 346 | try { 347 | // connect to the socket 348 | boost::asio::ip::tcp::resolver resolver(io_service); 349 | boost::asio::ip::tcp::resolver::query query(host, port); 350 | boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); 351 | boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint); 352 | start_accept(&acceptor, &io_service); 353 | io_service.run(); 354 | } 355 | catch (std::exception& e){ 356 | // throw error log 357 | log(LogLevel::ERROR, e.what()); 358 | return; 359 | } 360 | } 361 | 362 | /** 363 | * Trade Booking Execution Listener subscribing from execution service. 364 | * Basically, this listener is used to subscribe data from execution service, 365 | * transfer the ExecutionOrder data to Trade data, and call BookTrade() 366 | * method to publish the Trade data to Trade Booking Service. 367 | */ 368 | template 369 | class TradeBookingServiceListener : public ServiceListener> 370 | { 371 | private: 372 | TradeBookingService* service; 373 | long count; 374 | 375 | public: 376 | // ctor 377 | TradeBookingServiceListener(TradeBookingService* _service); 378 | // dtor 379 | ~TradeBookingServiceListener()=default; 380 | 381 | // Listener callback to process an add event to the Service 382 | void ProcessAdd(ExecutionOrder &data) override; 383 | 384 | // Listener callback to process a remove event to the Service 385 | void ProcessRemove(ExecutionOrder &data) override; 386 | 387 | // Listener callback to process an update event to the Service 388 | void ProcessUpdate(ExecutionOrder &data) override; 389 | 390 | }; 391 | 392 | template 393 | TradeBookingServiceListener::TradeBookingServiceListener(TradeBookingService* _service) 394 | { 395 | service = _service; 396 | count = 0; 397 | } 398 | 399 | 400 | /** 401 | * ProcessAdd() method is used to transfer ExecutionOrder data to Trade data, 402 | * and then call BookTrade() method to publish the Trade data to Trade Booking Service. 403 | */ 404 | template 405 | void TradeBookingServiceListener::ProcessAdd(ExecutionOrder &data) 406 | { 407 | T product = data.GetProduct(); 408 | string orderId = data.GetOrderId(); 409 | double price = data.GetPrice(); 410 | PricingSide pside = data.GetSide(); 411 | long vQty = data.GetVisibleQuantity(); 412 | long hQty = data.GetHiddenQuantity(); 413 | long quantity = vQty + hQty; 414 | Side side; 415 | switch (pside){ 416 | case BID: 417 | side = BUY; 418 | break; 419 | case OFFER: 420 | side = SELL; 421 | break; 422 | } 423 | string book; 424 | // switch book based on count 425 | count++; 426 | switch (count%3) 427 | { 428 | case 0: 429 | book = "TRSY1"; 430 | break; 431 | case 1: 432 | book = "TRSY2"; 433 | break; 434 | case 2: 435 | book = "TRSY3"; 436 | break; 437 | } 438 | 439 | Trade trade(product, orderId, price, book, quantity, side); 440 | 441 | // book the trade 442 | service->BookTrade(trade); 443 | } 444 | 445 | template 446 | void TradeBookingServiceListener::ProcessRemove(ExecutionOrder &data) 447 | { 448 | } 449 | 450 | template 451 | void TradeBookingServiceListener::ProcessUpdate(ExecutionOrder &data) 452 | { 453 | } 454 | 455 | 456 | #endif 457 | 458 | 459 | 460 | 461 | 462 | 463 | -------------------------------------------------------------------------------- /headers/utils.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * utils.hpp 3 | * A collection of utility functions 4 | * 5 | * @author Boyu Yang 6 | */ 7 | 8 | #ifndef UTILS_HPP 9 | #define UTILS_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "products.hpp" 21 | 22 | using namespace std; 23 | 24 | // Color codes 25 | #define RESET "\033[0m" 26 | #define RED "\033[31m" 27 | #define GREEN "\033[32m" 28 | #define YELLOW "\033[33m" 29 | #define BLUE "\033[34m" 30 | #define MAGENTA "\033[35m" 31 | #define CYAN "\033[36m" 32 | #define WHITE "\033[37m" 33 | 34 | // join a string vector with a delimiter 35 | string join(const vector& v, const string& delimiter) { 36 | string res; 37 | for (const auto& s : v) { 38 | res += s + delimiter; 39 | } 40 | return res.substr(0, res.size() - delimiter.size()); 41 | } 42 | 43 | 44 | // get the system time and return in milliseconds format (e.g. 2023-12-23 22:42:44.260) 45 | string getTime() { 46 | auto now = std::chrono::system_clock::now(); 47 | std::time_t now_c = std::chrono::system_clock::to_time_t(now); 48 | 49 | // change the system time to local time 50 | std::tm now_tm = *std::localtime(&now_c); 51 | 52 | // use stringstream to format the time 53 | stringstream ss; 54 | ss << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S"); 55 | 56 | // get the milliseconds separately 57 | auto milli = std::chrono::duration_cast(now.time_since_epoch()) % 1000; 58 | ss << '.' << std::setfill('0') << std::setw(3) << milli.count(); 59 | 60 | return ss.str(); 61 | } 62 | 63 | // change time point format to string 64 | string getTime(std::chrono::system_clock::time_point now) { 65 | std::time_t now_c = std::chrono::system_clock::to_time_t(now); 66 | std::tm now_tm = *std::localtime(&now_c); 67 | stringstream ss; 68 | ss << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S"); 69 | auto milli = std::chrono::duration_cast(now.time_since_epoch()) % 1000; 70 | ss << '.' << std::setfill('0') << std::setw(3) << milli.count(); 71 | return ss.str(); 72 | } 73 | 74 | 75 | enum class LogLevel { 76 | INFO, 77 | NOTE, 78 | WARNING, 79 | ERROR 80 | }; 81 | 82 | // log messages with different levels (colors) 83 | void log(LogLevel level, const string& message) { 84 | string levelStr; 85 | string color; 86 | switch (level) { 87 | case LogLevel::INFO: 88 | levelStr = "INFO"; 89 | color = GREEN; 90 | break; 91 | case LogLevel::NOTE: 92 | levelStr = "NOTE"; 93 | color = CYAN; 94 | break; 95 | case LogLevel::WARNING: 96 | levelStr = "WARNING"; 97 | color = YELLOW; 98 | break; 99 | case LogLevel::ERROR: 100 | levelStr = "ERROR"; 101 | color = RED; 102 | break; 103 | } 104 | cout << color << getTime() << " [" << levelStr << "] " << message << RESET << endl; 105 | } 106 | 107 | // get Product object from identifier 108 | // Define a type for a function that takes no arguments and returns a T 109 | template 110 | using ProductConstructor = std::function; 111 | 112 | // Define a map from CUSIPs to product constructors 113 | template 114 | std::map> productConstructors = { 115 | {"9128283H1", []() { return Bond("9128283H1", CUSIP, "US2Y", 0.01750, from_string("2019/11/30")); }}, 116 | {"9128283L2", []() { return Bond("9128283L2", CUSIP, "US3Y", 0.01875, from_string("2020/12/15")); }}, 117 | {"912828M80", []() { return Bond("912828M80", CUSIP, "US5Y", 0.02000, from_string("2022/11/30")); }}, 118 | {"9128283J7", []() { return Bond("9128283J7", CUSIP, "US7Y", 0.02125, from_string("2024/11/30")); }}, 119 | {"9128283F5", []() { return Bond("9128283F5", CUSIP, "US10Y", 0.02250, from_string("2027/12/15")); }}, 120 | {"912810TW8", []() { return Bond("912810TW8", CUSIP, "US20Y", 0.02500, from_string("2037/12/15")); }}, 121 | {"912810RZ3", []() { return Bond("912810RZ3", CUSIP, "US30Y", 0.02750, from_string("2047/12/15")); }}, 122 | }; 123 | 124 | template 125 | T getProductObject(const string& cusip) { 126 | auto it = productConstructors.find(cusip); 127 | if (it == productConstructors.end()) { 128 | throw std::invalid_argument("Unknown CUSIP: " + cusip); 129 | } 130 | return it->second(); 131 | } 132 | 133 | 134 | // function to calculate PV01 135 | double calculate_pv01(double face_value, double coupon_rate, double yield_rate, int years_to_maturity, int frequency) { 136 | double coupon = face_value * coupon_rate / frequency; 137 | double pv01 = 0.0; 138 | for (int t = 1; t <= years_to_maturity * frequency; ++t) { 139 | double discount_factor = 1.0 / pow(1.0 + yield_rate / frequency, t); 140 | pv01 += coupon * discount_factor; 141 | } 142 | pv01 += face_value / pow(1.0 + yield_rate / frequency, years_to_maturity * frequency); 143 | double pv01_initial = pv01; 144 | yield_rate += 0.0001; 145 | pv01 = 0.0; 146 | for (int t = 1; t <= years_to_maturity * frequency; ++t) { 147 | double discount_factor = 1.0 / pow(1.0 + yield_rate / frequency, t); 148 | pv01 += coupon * discount_factor; 149 | } 150 | pv01 += face_value / pow(1.0 + yield_rate / frequency, years_to_maturity * frequency); 151 | double pv01_change = pv01_initial - pv01; 152 | return pv01_change; 153 | } 154 | 155 | 156 | // Define a map from CUSIPs to PV01 157 | // Current yield for 2,3,5,7,10,20,30 year US treasury bonds: 0.0464, 0.0440, 0.0412, 0.043, 0.0428, 0.0461, 0.0443 158 | std::map pv01 = { 159 | {"9128283H1", calculate_pv01(1000, 0.01750, 0.0464, 2, 2)}, 160 | {"9128283L2", calculate_pv01(1000, 0.01875, 0.0440, 3, 2)}, 161 | {"912828M80", calculate_pv01(1000, 0.02000, 0.0412, 5, 2)}, 162 | {"9128283J7", calculate_pv01(1000, 0.02125, 0.0430, 7, 2)}, 163 | {"9128283F5", calculate_pv01(1000, 0.02250, 0.0428, 10, 2)}, 164 | {"912810TW8", calculate_pv01(1000, 0.02500, 0.0461, 20, 2)}, 165 | {"912810RZ3", calculate_pv01(1000, 0.02750, 0.0443, 30, 2)}, 166 | }; 167 | 168 | // Get unit PV01 value from CUSIP 169 | double getPV01(const string& cusip) { 170 | auto it = pv01.find(cusip); 171 | if (it == pv01.end()) { 172 | throw std::invalid_argument("Unknown CUSIP: " + cusip); 173 | } 174 | return it->second; 175 | } 176 | 177 | // change US treasury prices from fractional notation to decimal notation 178 | double convertPrice(const string& priceStr) { 179 | // if the price is in decimal notation, return it directly 180 | if (priceStr.find_first_of('-') == string::npos) { 181 | return stod(priceStr); 182 | } 183 | 184 | // if the price is in fractional notation, convert it to decimal notation 185 | // first, split the string into two parts 186 | size_t pos = priceStr.find_first_of('-'); 187 | string str1 = priceStr.substr(0, pos); 188 | string str2 = priceStr.substr(pos+1, 2); 189 | string str3 = priceStr.substr(pos+3, 1); 190 | if (str3 == "+") { 191 | str3 = "4"; 192 | } 193 | 194 | double res = stod(str1) + stod(str2)*1.0 / 32.0 + stod(str3)*1.0 / 256.0; 195 | return res; 196 | } 197 | 198 | // change prices from decimal notation to fractional notation 199 | string convertPrice(double price) { 200 | int intPart = floor(price); 201 | double fraction = price - intPart; 202 | int xy = floor(fraction * 32); 203 | int z = static_cast((fraction*256)) % 8; 204 | // be careful about corner cases 205 | return to_string(intPart) + "-" + (xy < 10 ? "0" : "") + std::to_string(xy) + (z == 4 ? "+" : std::to_string(z)); 206 | 207 | } 208 | 209 | // generate oscillating spread between 1/64 and 1/128 210 | double genRandomSpread(std::mt19937& gen) { 211 | std::uniform_real_distribution dist(1.0/128.0, 1.0/64.0); 212 | return dist(gen); 213 | } 214 | 215 | // Generate random ID with numbers and letters 216 | string GenerateRandomId(long length) 217 | { 218 | string id = ""; 219 | for (int j = 0; j < length; ++j) { 220 | int random = rand() % 36; 221 | if (random < 10) { 222 | id += to_string(random); 223 | } else { 224 | id += static_cast('A' + random - 10); 225 | } 226 | } 227 | return id; 228 | } 229 | 230 | /** 231 | * 1. Generate prices that oscillate between 99 and 101 and write to prices.txt 232 | * 2. Generate order book data with fivel levels of bids and offers and write to marketdata.txt 233 | */ 234 | void genOrderBook(const vector& products, const string& priceFile, const string& orderbookFile, long long seed, const int numDataPoints) { 235 | std::ofstream pFile(priceFile); 236 | std::ofstream oFile(orderbookFile); 237 | std::mt19937 gen(seed); 238 | std::uniform_int_distribution<> ms_dist(1, 20); // simulate milliseconds increments 239 | 240 | // price file format: Timestamp, CUSIP, Bid, Ask 241 | // orderbook file format: Timestamp, CUSIP, Bid1, BidSize1, Ask1, AskSize1, Bid2, BidSize2, Ask2, AskSize2, Bid3, BidSize3, Ask3, AskSize3, Bid4, BidSize4, Ask4, AskSize4, Bid5, BidSize5, Ask5, AskSize5 242 | 243 | for (const auto& product : products) { 244 | double midPrice = 99.00; 245 | bool priceIncreasing = true; 246 | bool spreadIncreasing = true; 247 | double fixSpread = 1.0/128.0; 248 | auto curTime = std::chrono::system_clock::now(); 249 | string timestamp; 250 | 251 | // number of data points 252 | for (int i = 0; i < numDataPoints; ++i) { 253 | 254 | // generate price data 255 | double randomSpread = genRandomSpread(gen); 256 | curTime += std::chrono::milliseconds(ms_dist(gen)); 257 | timestamp = getTime(curTime); 258 | 259 | double randomBid = midPrice - randomSpread / 2.0; 260 | double randomAsk = midPrice + randomSpread / 2.0; 261 | pFile << timestamp << "," << product << "," << convertPrice(randomBid) << "," << convertPrice(randomAsk) << "," << randomSpread << endl; 262 | 263 | // generate order book data 264 | oFile << timestamp << "," << product; 265 | for (int level=1; level<=5; ++level){ 266 | double fixBid = midPrice - fixSpread * level / 2.0; 267 | double fixAsk = midPrice + fixSpread * level / 2.0; 268 | int size = level * 1'000'000; 269 | oFile << "," << convertPrice(fixBid) << "," << size << "," << convertPrice(fixAsk) << "," << size; 270 | } 271 | oFile << endl; 272 | 273 | // oscillate mid price 274 | if (priceIncreasing) { 275 | midPrice += 1.0 / 256.0; 276 | if (randomAsk >= 101.0) { 277 | priceIncreasing = false; 278 | } 279 | } else { 280 | midPrice -= 1.0 / 256.0; 281 | if (randomBid <= 99.0) { 282 | priceIncreasing = true; 283 | } 284 | } 285 | 286 | // oscillate spread 287 | if (spreadIncreasing) { 288 | fixSpread += 1.0 / 128.0; 289 | if (fixSpread >= 1.0 / 32.0) { 290 | spreadIncreasing = false; 291 | } 292 | } else { 293 | fixSpread -= 1.0 / 128.0; 294 | if (fixSpread <= 1.0 / 128.0) { 295 | spreadIncreasing = true; 296 | } 297 | } 298 | } 299 | } 300 | 301 | pFile.close(); 302 | oFile.close(); 303 | } 304 | 305 | /** 306 | * Generate trades data 307 | */ 308 | void genTrades(const vector& products, const string& tradeFile, long long seed) { 309 | vector books = {"TRSY1", "TRSY2", "TRSY3"}; 310 | vector quantities = {1000000, 2000000, 3000000, 4000000, 5000000}; 311 | std::ofstream tFile(tradeFile); 312 | std::mt19937 gen(seed); 313 | 314 | for (const auto& product : products) { 315 | for (int i = 0; i < 10; ++i) { 316 | string side = (i % 2 == 0) ? "BUY" : "SELL"; 317 | // generate a 12 digit random trade id with number and letters 318 | string tradeId = GenerateRandomId(12); 319 | // generate random buy price 99-100 and random sell price 100-101 with given seed 320 | std::uniform_real_distribution dist(side == "BUY" ? 99.0 : 100.0, side == "BUY" ? 100.0 : 101.0); 321 | double price = dist(gen); 322 | long quantity = quantities[i % quantities.size()]; 323 | string book = books[i % books.size()]; 324 | 325 | tFile << product << "," << tradeId << "," << convertPrice(price) << "," << book << "," << quantity << "," << side << endl; 326 | } 327 | } 328 | 329 | tFile.close(); 330 | } 331 | 332 | /** 333 | * Generate inquiry data 334 | */ 335 | void genInquiries(const vector& products, const string& inquiryFile, long long seed){ 336 | std::ofstream iFile(inquiryFile); 337 | std::mt19937 gen(seed); 338 | vector quantities = {1000000, 2000000, 3000000, 4000000, 5000000}; 339 | 340 | for (const auto& product : products) { 341 | for (int i = 0; i < 10; ++i) { 342 | string side = (i % 2 == 0) ? "BUY" : "SELL"; 343 | // generate a 12 digit random inquiry id with number and letters 344 | string inquiryId = GenerateRandomId(12); 345 | // generate random buy price 99-100 and random sell price 100-101 with given seed 346 | std::uniform_real_distribution dist(side == "BUY" ? 99.0 : 100.0, side == "BUY" ? 100.0 : 101.0); 347 | double price = dist(gen); 348 | long quantity = quantities[i % quantities.size()]; 349 | string status = "RECEIVED"; 350 | 351 | iFile << inquiryId << "," << product << "," << side << "," << quantity << "," << convertPrice(price) << "," << status << endl; 352 | } 353 | } 354 | } 355 | 356 | // function to check whether a thread has completed and join it if yes 357 | // introduce the try-catch block as required 358 | void join(std::thread& t) { 359 | if (t.joinable()) { 360 | t.join(); 361 | } 362 | else { 363 | try { 364 | t.join(); 365 | } 366 | catch (std::exception& e) { 367 | std::cout << "Exception caught: " << e.what() << std::endl; 368 | } 369 | } 370 | } 371 | 372 | #endif 373 | -------------------------------------------------------------------------------- /imgs/clientserver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bradleyboyuyang/Trading-System/dc7ef2fbcf5f51b0d418ea4453654601b75dae7e/imgs/clientserver.png -------------------------------------------------------------------------------- /imgs/serviceflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bradleyboyuyang/Trading-System/dc7ef2fbcf5f51b0d418ea4453654601b75dae7e/imgs/serviceflow.png -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * main.hpp 3 | * Main function to start six trading system servers 4 | * 5 | * External data flows in the system through: 6 | * 1. price data -> pricing service -> algo streaming service -> streaming service -> historical data service 7 | (another data flow: pricing service -> GUI service -> GUI data output) 8 | * 2. orderbook data -> market data service -> algo execution service -> execution service -> historical data service 9 | * (another data flow: execution service -> trade booking service -> position service -> risk service -> historical data service) 10 | * 3. trade data -> trade booking service -> position service -> risk service -> historical data service 11 | * 4. inquiry data -> inquiry service -> historical data service 12 | * 13 | * @author Boyu Yang 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "headers/soa.hpp" 23 | #include "headers/products.hpp" 24 | #include "headers/marketdataservice.hpp" 25 | #include "headers/pricingservice.hpp" 26 | #include "headers/riskservice.hpp" 27 | #include "headers/executionservice.hpp" 28 | #include "headers/positionservice.hpp" 29 | #include "headers/inquiryservice.hpp" 30 | #include "headers/historicaldataservice.hpp" 31 | #include "headers/streamingservice.hpp" 32 | #include "headers/algostreamingservice.hpp" 33 | #include "headers/tradebookingservice.hpp" 34 | #include "headers/algoexecutionservice.hpp" 35 | #include "headers/guiservice.hpp" 36 | #include "headers/utils.hpp" 37 | 38 | using namespace std; 39 | 40 | /** 41 | * server thread function 42 | * The whole system is designed so that subscribe() method 43 | * opens a separate process and listens to the data on the socket 44 | */ 45 | template 46 | void Server(T& service) 47 | { 48 | service.GetConnector()->Subscribe(); 49 | } 50 | 51 | int main(int, char**){ 52 | 53 | // 1. define data path and generate data 54 | // 1.1 create folders that store data and results 55 | string dataPath = "../data"; 56 | if (filesystem::exists(dataPath)) { 57 | filesystem::remove_all(dataPath); 58 | } 59 | filesystem::create_directory(dataPath); 60 | 61 | string resPath = "../res"; 62 | if (filesystem::exists(resPath)) { 63 | filesystem::remove_all(resPath); 64 | } 65 | filesystem::create_directory(resPath); 66 | 67 | // 1.2 define data path 68 | const string pricePath = "../data/prices.txt"; 69 | const string marketDataPath = "../data/marketdata.txt"; 70 | const string tradePath = "../data/trades.txt"; 71 | const string inquiryPath = "../data/inquiries.txt"; 72 | // 1.3 generate data 73 | log(LogLevel::INFO, "Generating price and orderbook data..."); 74 | // tickers 75 | vector bonds = {"9128283H1", "9128283L2", "912828M80", "9128283J7", "9128283F5", "912810TW8", "912810RZ3"}; 76 | // generate price and orderbook data (specify random seed and number of data points) 77 | genOrderBook(bonds, pricePath, marketDataPath, 39373, 5000); 78 | log(LogLevel::INFO, "Generating trade data..."); 79 | genTrades(bonds, tradePath, 39373); 80 | log(LogLevel::INFO, "Generating inquiry data..."); 81 | genInquiries(bonds, inquiryPath, 39373); 82 | log(LogLevel::INFO, "Generating data finished."); 83 | 84 | // 2. start trading service 85 | log(LogLevel::INFO, "Starting trading system..."); 86 | // 2.1 create four servers with host adn different ports 87 | log(LogLevel::INFO, "Initializing service components..."); 88 | PricingService pricingService("localhost", "3000"); 89 | MarketDataService marketDataService("localhost", "3001"); 90 | TradeBookingService tradeBookingService("localhost", "3002"); 91 | InquiryService inquiryService("localhost", "3003"); 92 | StreamingService streamingService("localhost", "3004"); 93 | ExecutionService executionService("localhost", "3005"); 94 | 95 | AlgoStreamingService algoStreamingService; 96 | AlgoExecutionService algoExecutionService; 97 | PositionService positionService; 98 | RiskService riskService; 99 | GUIService guiService; 100 | 101 | HistoricalDataService> historicalPositionService(POSITION); 102 | HistoricalDataService> historicalRiskService(RISK); 103 | HistoricalDataService> historicalExecutionService(EXECUTION); 104 | HistoricalDataService> historicalStreamingService(STREAMING); 105 | HistoricalDataService> historicalInquiryService(INQUIRY); 106 | log(LogLevel::INFO, "Trading service initialized."); 107 | 108 | // 2.2 create listeners 109 | log(LogLevel::INFO, "Linking service listeners..."); 110 | pricingService.AddListener(algoStreamingService.GetAlgoStreamingListener()); 111 | pricingService.AddListener(guiService.GetGUIServiceListener()); 112 | algoStreamingService.AddListener(streamingService.GetStreamingServiceListener()); 113 | marketDataService.AddListener(algoExecutionService.GetAlgoExecutionServiceListener()); 114 | algoExecutionService.AddListener(executionService.GetExecutionServiceListener()); 115 | executionService.AddListener(tradeBookingService.GetTradeBookingServiceListener()); 116 | tradeBookingService.AddListener(positionService.GetPositionListener()); 117 | positionService.AddListener(riskService.GetRiskServiceListener()); 118 | 119 | positionService.AddListener(historicalPositionService.GetHistoricalDataServiceListener()); 120 | executionService.AddListener(historicalExecutionService.GetHistoricalDataServiceListener()); 121 | streamingService.AddListener(historicalStreamingService.GetHistoricalDataServiceListener()); 122 | riskService.AddListener(historicalRiskService.GetHistoricalDataServiceListener()); 123 | inquiryService.AddListener(historicalInquiryService.GetHistoricalDataServiceListener()); 124 | log(LogLevel::INFO, "Service listeners linked."); 125 | 126 | // 3. start six system servers in different threads 127 | cout << fixed << setprecision(6); 128 | vector threads; 129 | 130 | // four input servers: pricing, market data, trade booking, inquiry 131 | threads.push_back(thread(Server>, ref(pricingService))); 132 | threads.push_back(thread(Server>, ref(marketDataService))); 133 | threads.push_back(thread(Server>, ref(tradeBookingService))); 134 | threads.push_back(thread(Server>, ref(inquiryService))); 135 | 136 | // two output servers: streaming, execution 137 | threads.push_back(thread(Server>, ref(streamingService))); 138 | threads.push_back(thread(Server>, ref(executionService))); 139 | 140 | for (auto& thread : threads) { 141 | join(thread); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /res/allinquiries.txt: -------------------------------------------------------------------------------- 1 | 2024-01-02 16:51:50.078,OM595MUHI7N7,9128283H1,BID,1000000,99-285,DONE 2 | 2024-01-02 16:51:50.078,OM595MUHI7N7,9128283H1,BID,1000000,99-285,DONE 3 | 2024-01-02 16:51:50.078,OM595MUHI7N7,9128283H1,BID,1000000,99-285,DONE 4 | 2024-01-02 16:51:50.078,JZWQP3UVQYU3,9128283H1,OFFER,2000000,100-031,DONE 5 | 2024-01-02 16:51:50.078,JZWQP3UVQYU3,9128283H1,OFFER,2000000,100-031,DONE 6 | 2024-01-02 16:51:50.078,JZWQP3UVQYU3,9128283H1,OFFER,2000000,100-031,DONE 7 | 2024-01-02 16:51:50.078,XXQTD3WHQHRB,9128283H1,BID,3000000,99-241,DONE 8 | 2024-01-02 16:51:50.078,XXQTD3WHQHRB,9128283H1,BID,3000000,99-241,DONE 9 | 2024-01-02 16:51:50.078,XXQTD3WHQHRB,9128283H1,BID,3000000,99-241,DONE 10 | 2024-01-02 16:51:50.078,4LSMSF9SULYJ,9128283H1,OFFER,4000000,100-161,DONE 11 | 2024-01-02 16:51:50.078,4LSMSF9SULYJ,9128283H1,OFFER,4000000,100-161,DONE 12 | 2024-01-02 16:51:50.078,4LSMSF9SULYJ,9128283H1,OFFER,4000000,100-161,DONE 13 | 2024-01-02 16:51:50.078,P8FV6PF4M5DF,9128283H1,BID,5000000,99-237,DONE 14 | 2024-01-02 16:51:50.078,P8FV6PF4M5DF,9128283H1,BID,5000000,99-237,DONE 15 | 2024-01-02 16:51:50.078,P8FV6PF4M5DF,9128283H1,BID,5000000,99-237,DONE 16 | 2024-01-02 16:51:50.078,OQXU746RPYTY,9128283H1,OFFER,1000000,100-310,DONE 17 | 2024-01-02 16:51:50.078,OQXU746RPYTY,9128283H1,OFFER,1000000,100-310,DONE 18 | 2024-01-02 16:51:50.078,OQXU746RPYTY,9128283H1,OFFER,1000000,100-310,DONE 19 | 2024-01-02 16:51:50.078,U36444O9CJ5I,9128283H1,BID,2000000,99-283,DONE 20 | 2024-01-02 16:51:50.078,U36444O9CJ5I,9128283H1,BID,2000000,99-283,DONE 21 | 2024-01-02 16:51:50.078,U36444O9CJ5I,9128283H1,BID,2000000,99-283,DONE 22 | 2024-01-02 16:51:50.078,802B5G696J4T,9128283H1,OFFER,3000000,100-147,DONE 23 | 2024-01-02 16:51:50.078,802B5G696J4T,9128283H1,OFFER,3000000,100-147,DONE 24 | 2024-01-02 16:51:50.078,802B5G696J4T,9128283H1,OFFER,3000000,100-147,DONE 25 | 2024-01-02 16:51:50.078,NALTOERYXX3I,9128283H1,BID,4000000,99-067,DONE 26 | 2024-01-02 16:51:50.078,NALTOERYXX3I,9128283H1,BID,4000000,99-067,DONE 27 | 2024-01-02 16:51:50.078,NALTOERYXX3I,9128283H1,BID,4000000,99-067,DONE 28 | 2024-01-02 16:51:50.078,H7R9QCRECA5X,9128283H1,OFFER,5000000,100-265,DONE 29 | 2024-01-02 16:51:50.078,H7R9QCRECA5X,9128283H1,OFFER,5000000,100-265,DONE 30 | 2024-01-02 16:51:50.078,H7R9QCRECA5X,9128283H1,OFFER,5000000,100-265,DONE 31 | 2024-01-02 16:51:50.078,6C7SVBLZLMSP,9128283L2,BID,1000000,99-185,DONE 32 | 2024-01-02 16:51:50.078,6C7SVBLZLMSP,9128283L2,BID,1000000,99-185,DONE 33 | 2024-01-02 16:51:50.078,6C7SVBLZLMSP,9128283L2,BID,1000000,99-185,DONE 34 | 2024-01-02 16:51:50.079,HJOEWRCTY424,9128283L2,OFFER,2000000,100-236,DONE 35 | 2024-01-02 16:51:50.079,HJOEWRCTY424,9128283L2,OFFER,2000000,100-236,DONE 36 | 2024-01-02 16:51:50.079,HJOEWRCTY424,9128283L2,OFFER,2000000,100-236,DONE 37 | 2024-01-02 16:51:50.079,WTY9J46PWTH7,9128283L2,BID,3000000,99-312,DONE 38 | 2024-01-02 16:51:50.079,WTY9J46PWTH7,9128283L2,BID,3000000,99-312,DONE 39 | 2024-01-02 16:51:50.079,WTY9J46PWTH7,9128283L2,BID,3000000,99-312,DONE 40 | 2024-01-02 16:51:50.079,4J655YVMXZH9,9128283L2,OFFER,4000000,100-060,DONE 41 | 2024-01-02 16:51:50.079,4J655YVMXZH9,9128283L2,OFFER,4000000,100-060,DONE 42 | 2024-01-02 16:51:50.079,4J655YVMXZH9,9128283L2,OFFER,4000000,100-060,DONE 43 | 2024-01-02 16:51:50.079,QT24D4OAE2ZX,9128283L2,BID,5000000,99-031,DONE 44 | 2024-01-02 16:51:50.079,QT24D4OAE2ZX,9128283L2,BID,5000000,99-031,DONE 45 | 2024-01-02 16:51:50.079,QT24D4OAE2ZX,9128283L2,BID,5000000,99-031,DONE 46 | 2024-01-02 16:51:50.079,653IF0QJJW55,9128283L2,OFFER,1000000,100-247,DONE 47 | 2024-01-02 16:51:50.079,653IF0QJJW55,9128283L2,OFFER,1000000,100-247,DONE 48 | 2024-01-02 16:51:50.079,653IF0QJJW55,9128283L2,OFFER,1000000,100-247,DONE 49 | 2024-01-02 16:51:50.079,BGR8F8Y5I0PV,9128283L2,BID,2000000,99-163,DONE 50 | 2024-01-02 16:51:50.079,BGR8F8Y5I0PV,9128283L2,BID,2000000,99-163,DONE 51 | 2024-01-02 16:51:50.079,BGR8F8Y5I0PV,9128283L2,BID,2000000,99-163,DONE 52 | 2024-01-02 16:51:50.079,5D5ZVKW26F0L,9128283L2,OFFER,3000000,100-301,DONE 53 | 2024-01-02 16:51:50.079,5D5ZVKW26F0L,9128283L2,OFFER,3000000,100-301,DONE 54 | 2024-01-02 16:51:50.079,5D5ZVKW26F0L,9128283L2,OFFER,3000000,100-301,DONE 55 | 2024-01-02 16:51:50.079,GQKF3PKE5S2K,9128283L2,BID,4000000,99-246,DONE 56 | 2024-01-02 16:51:50.079,GQKF3PKE5S2K,9128283L2,BID,4000000,99-246,DONE 57 | 2024-01-02 16:51:50.079,GQKF3PKE5S2K,9128283L2,BID,4000000,99-246,DONE 58 | 2024-01-02 16:51:50.079,G05YHUU2NF1J,9128283L2,OFFER,5000000,100-287,DONE 59 | 2024-01-02 16:51:50.079,G05YHUU2NF1J,9128283L2,OFFER,5000000,100-287,DONE 60 | 2024-01-02 16:51:50.079,G05YHUU2NF1J,9128283L2,OFFER,5000000,100-287,DONE 61 | 2024-01-02 16:51:50.079,0D16TH7P8R4R,912828M80,BID,1000000,99-300,DONE 62 | 2024-01-02 16:51:50.079,0D16TH7P8R4R,912828M80,BID,1000000,99-300,DONE 63 | 2024-01-02 16:51:50.079,0D16TH7P8R4R,912828M80,BID,1000000,99-300,DONE 64 | 2024-01-02 16:51:50.079,H552XNND48SL,912828M80,OFFER,2000000,100-256,DONE 65 | 2024-01-02 16:51:50.079,H552XNND48SL,912828M80,OFFER,2000000,100-256,DONE 66 | 2024-01-02 16:51:50.079,H552XNND48SL,912828M80,OFFER,2000000,100-256,DONE 67 | 2024-01-02 16:51:50.079,JM3MH4LXH2JA,912828M80,BID,3000000,99-243,DONE 68 | 2024-01-02 16:51:50.079,JM3MH4LXH2JA,912828M80,BID,3000000,99-243,DONE 69 | 2024-01-02 16:51:50.079,JM3MH4LXH2JA,912828M80,BID,3000000,99-243,DONE 70 | 2024-01-02 16:51:50.079,KQFSYKJF54X2,912828M80,OFFER,4000000,100-10+,DONE 71 | 2024-01-02 16:51:50.079,KQFSYKJF54X2,912828M80,OFFER,4000000,100-10+,DONE 72 | 2024-01-02 16:51:50.079,KQFSYKJF54X2,912828M80,OFFER,4000000,100-10+,DONE 73 | 2024-01-02 16:51:50.079,70VB9NCSPFUN,912828M80,BID,5000000,99-13+,DONE 74 | 2024-01-02 16:51:50.079,70VB9NCSPFUN,912828M80,BID,5000000,99-13+,DONE 75 | 2024-01-02 16:51:50.079,70VB9NCSPFUN,912828M80,BID,5000000,99-13+,DONE 76 | 2024-01-02 16:51:50.079,ZGKHIK72QNAO,912828M80,OFFER,1000000,100-14+,DONE 77 | 2024-01-02 16:51:50.079,ZGKHIK72QNAO,912828M80,OFFER,1000000,100-14+,DONE 78 | 2024-01-02 16:51:50.079,ZGKHIK72QNAO,912828M80,OFFER,1000000,100-14+,DONE 79 | 2024-01-02 16:51:50.079,NTJSDHALHLC6,912828M80,BID,2000000,99-013,DONE 80 | 2024-01-02 16:51:50.079,NTJSDHALHLC6,912828M80,BID,2000000,99-013,DONE 81 | 2024-01-02 16:51:50.079,NTJSDHALHLC6,912828M80,BID,2000000,99-013,DONE 82 | 2024-01-02 16:51:50.079,95EE0910P2XN,912828M80,OFFER,3000000,100-120,DONE 83 | 2024-01-02 16:51:50.079,95EE0910P2XN,912828M80,OFFER,3000000,100-120,DONE 84 | 2024-01-02 16:51:50.079,95EE0910P2XN,912828M80,OFFER,3000000,100-120,DONE 85 | 2024-01-02 16:51:50.079,M4QS70HUAG2N,912828M80,BID,4000000,99-217,DONE 86 | 2024-01-02 16:51:50.079,M4QS70HUAG2N,912828M80,BID,4000000,99-217,DONE 87 | 2024-01-02 16:51:50.079,M4QS70HUAG2N,912828M80,BID,4000000,99-217,DONE 88 | 2024-01-02 16:51:50.079,DS8VE1H36WH6,912828M80,OFFER,5000000,100-14+,DONE 89 | 2024-01-02 16:51:50.079,DS8VE1H36WH6,912828M80,OFFER,5000000,100-14+,DONE 90 | 2024-01-02 16:51:50.079,DS8VE1H36WH6,912828M80,OFFER,5000000,100-14+,DONE 91 | 2024-01-02 16:51:50.079,5J6AL3XNO3FV,9128283J7,BID,1000000,99-260,DONE 92 | 2024-01-02 16:51:50.079,5J6AL3XNO3FV,9128283J7,BID,1000000,99-260,DONE 93 | 2024-01-02 16:51:50.079,5J6AL3XNO3FV,9128283J7,BID,1000000,99-260,DONE 94 | 2024-01-02 16:51:50.079,4C6UTOHMH6HB,9128283J7,OFFER,2000000,100-241,DONE 95 | 2024-01-02 16:51:50.079,4C6UTOHMH6HB,9128283J7,OFFER,2000000,100-241,DONE 96 | 2024-01-02 16:51:50.079,4C6UTOHMH6HB,9128283J7,OFFER,2000000,100-241,DONE 97 | 2024-01-02 16:51:50.079,7FETBBZWUMMV,9128283J7,BID,3000000,99-052,DONE 98 | 2024-01-02 16:51:50.079,7FETBBZWUMMV,9128283J7,BID,3000000,99-052,DONE 99 | 2024-01-02 16:51:50.079,7FETBBZWUMMV,9128283J7,BID,3000000,99-052,DONE 100 | 2024-01-02 16:51:50.079,5JI93ELNQRHZ,9128283J7,OFFER,4000000,100-247,DONE 101 | 2024-01-02 16:51:50.079,5JI93ELNQRHZ,9128283J7,OFFER,4000000,100-247,DONE 102 | 2024-01-02 16:51:50.079,5JI93ELNQRHZ,9128283J7,OFFER,4000000,100-247,DONE 103 | 2024-01-02 16:51:50.079,FYMCKJ37YH0P,9128283J7,BID,5000000,99-076,DONE 104 | 2024-01-02 16:51:50.079,FYMCKJ37YH0P,9128283J7,BID,5000000,99-076,DONE 105 | 2024-01-02 16:51:50.079,FYMCKJ37YH0P,9128283J7,BID,5000000,99-076,DONE 106 | 2024-01-02 16:51:50.079,TGL327ZN7XDQ,9128283J7,OFFER,1000000,100-097,DONE 107 | 2024-01-02 16:51:50.079,TGL327ZN7XDQ,9128283J7,OFFER,1000000,100-097,DONE 108 | 2024-01-02 16:51:50.079,TGL327ZN7XDQ,9128283J7,OFFER,1000000,100-097,DONE 109 | 2024-01-02 16:51:50.079,BYDIPUHK8JDT,9128283J7,BID,2000000,99-026,DONE 110 | 2024-01-02 16:51:50.079,BYDIPUHK8JDT,9128283J7,BID,2000000,99-026,DONE 111 | 2024-01-02 16:51:50.079,BYDIPUHK8JDT,9128283J7,BID,2000000,99-026,DONE 112 | 2024-01-02 16:51:50.079,JG0HE1NNXOQF,9128283J7,OFFER,3000000,100-176,DONE 113 | 2024-01-02 16:51:50.079,JG0HE1NNXOQF,9128283J7,OFFER,3000000,100-176,DONE 114 | 2024-01-02 16:51:50.079,JG0HE1NNXOQF,9128283J7,OFFER,3000000,100-176,DONE 115 | 2024-01-02 16:51:50.079,C52J3FPED2WI,9128283J7,BID,4000000,99-293,DONE 116 | 2024-01-02 16:51:50.079,C52J3FPED2WI,9128283J7,BID,4000000,99-293,DONE 117 | 2024-01-02 16:51:50.079,C52J3FPED2WI,9128283J7,BID,4000000,99-293,DONE 118 | 2024-01-02 16:51:50.079,WUJKDWDWCUU6,9128283J7,OFFER,5000000,100-111,DONE 119 | 2024-01-02 16:51:50.079,WUJKDWDWCUU6,9128283J7,OFFER,5000000,100-111,DONE 120 | 2024-01-02 16:51:50.079,WUJKDWDWCUU6,9128283J7,OFFER,5000000,100-111,DONE 121 | 2024-01-02 16:51:50.079,BHT8LKNX55W8,9128283F5,BID,1000000,99-303,DONE 122 | 2024-01-02 16:51:50.079,BHT8LKNX55W8,9128283F5,BID,1000000,99-303,DONE 123 | 2024-01-02 16:51:50.079,BHT8LKNX55W8,9128283F5,BID,1000000,99-303,DONE 124 | 2024-01-02 16:51:50.079,1LNENZDZTWKN,9128283F5,OFFER,2000000,100-273,DONE 125 | 2024-01-02 16:51:50.079,1LNENZDZTWKN,9128283F5,OFFER,2000000,100-273,DONE 126 | 2024-01-02 16:51:50.079,1LNENZDZTWKN,9128283F5,OFFER,2000000,100-273,DONE 127 | 2024-01-02 16:51:50.079,SDZK79RY606S,9128283F5,BID,3000000,99-083,DONE 128 | 2024-01-02 16:51:50.079,SDZK79RY606S,9128283F5,BID,3000000,99-083,DONE 129 | 2024-01-02 16:51:50.079,SDZK79RY606S,9128283F5,BID,3000000,99-083,DONE 130 | 2024-01-02 16:51:50.079,K9P6FMEG7HAB,9128283F5,OFFER,4000000,100-17+,DONE 131 | 2024-01-02 16:51:50.079,K9P6FMEG7HAB,9128283F5,OFFER,4000000,100-17+,DONE 132 | 2024-01-02 16:51:50.079,K9P6FMEG7HAB,9128283F5,OFFER,4000000,100-17+,DONE 133 | 2024-01-02 16:51:50.080,H3AQZAT7OTSB,9128283F5,BID,5000000,99-051,DONE 134 | 2024-01-02 16:51:50.080,H3AQZAT7OTSB,9128283F5,BID,5000000,99-051,DONE 135 | 2024-01-02 16:51:50.080,H3AQZAT7OTSB,9128283F5,BID,5000000,99-051,DONE 136 | 2024-01-02 16:51:50.080,2ZA9ZW10MQ61,9128283F5,OFFER,1000000,100-271,DONE 137 | 2024-01-02 16:51:50.080,2ZA9ZW10MQ61,9128283F5,OFFER,1000000,100-271,DONE 138 | 2024-01-02 16:51:50.080,2ZA9ZW10MQ61,9128283F5,OFFER,1000000,100-271,DONE 139 | 2024-01-02 16:51:50.080,SKX0INBZR15Q,9128283F5,BID,2000000,99-313,DONE 140 | 2024-01-02 16:51:50.080,SKX0INBZR15Q,9128283F5,BID,2000000,99-313,DONE 141 | 2024-01-02 16:51:50.080,SKX0INBZR15Q,9128283F5,BID,2000000,99-313,DONE 142 | 2024-01-02 16:51:50.080,SZYGS67A5XJK,9128283F5,OFFER,3000000,100-216,DONE 143 | 2024-01-02 16:51:50.080,SZYGS67A5XJK,9128283F5,OFFER,3000000,100-216,DONE 144 | 2024-01-02 16:51:50.080,SZYGS67A5XJK,9128283F5,OFFER,3000000,100-216,DONE 145 | 2024-01-02 16:51:50.080,A0KW7QDZRAZ9,9128283F5,BID,4000000,99-01+,DONE 146 | 2024-01-02 16:51:50.080,A0KW7QDZRAZ9,9128283F5,BID,4000000,99-01+,DONE 147 | 2024-01-02 16:51:50.080,A0KW7QDZRAZ9,9128283F5,BID,4000000,99-01+,DONE 148 | 2024-01-02 16:51:50.080,XQOO8TF08TWG,9128283F5,OFFER,5000000,100-173,DONE 149 | 2024-01-02 16:51:50.080,XQOO8TF08TWG,9128283F5,OFFER,5000000,100-173,DONE 150 | 2024-01-02 16:51:50.080,XQOO8TF08TWG,9128283F5,OFFER,5000000,100-173,DONE 151 | 2024-01-02 16:51:50.080,Z3RKHQ4R753E,912810TW8,BID,1000000,99-001,DONE 152 | 2024-01-02 16:51:50.080,Z3RKHQ4R753E,912810TW8,BID,1000000,99-001,DONE 153 | 2024-01-02 16:51:50.080,Z3RKHQ4R753E,912810TW8,BID,1000000,99-001,DONE 154 | 2024-01-02 16:51:50.080,VGD2QTR3ZV87,912810TW8,OFFER,2000000,100-065,DONE 155 | 2024-01-02 16:51:50.080,VGD2QTR3ZV87,912810TW8,OFFER,2000000,100-065,DONE 156 | 2024-01-02 16:51:50.080,VGD2QTR3ZV87,912810TW8,OFFER,2000000,100-065,DONE 157 | 2024-01-02 16:51:50.080,537DW3UBN1VK,912810TW8,BID,3000000,99-156,DONE 158 | 2024-01-02 16:51:50.080,537DW3UBN1VK,912810TW8,BID,3000000,99-156,DONE 159 | 2024-01-02 16:51:50.080,537DW3UBN1VK,912810TW8,BID,3000000,99-156,DONE 160 | 2024-01-02 16:51:50.080,RFRYKUSWA6EG,912810TW8,OFFER,4000000,100-233,DONE 161 | 2024-01-02 16:51:50.080,RFRYKUSWA6EG,912810TW8,OFFER,4000000,100-233,DONE 162 | 2024-01-02 16:51:50.080,RFRYKUSWA6EG,912810TW8,OFFER,4000000,100-233,DONE 163 | 2024-01-02 16:51:50.080,F6ZE17M6A90M,912810TW8,BID,5000000,99-227,DONE 164 | 2024-01-02 16:51:50.080,F6ZE17M6A90M,912810TW8,BID,5000000,99-227,DONE 165 | 2024-01-02 16:51:50.080,F6ZE17M6A90M,912810TW8,BID,5000000,99-227,DONE 166 | 2024-01-02 16:51:50.080,DUXGV802ORH8,912810TW8,OFFER,1000000,100-147,DONE 167 | 2024-01-02 16:51:50.080,DUXGV802ORH8,912810TW8,OFFER,1000000,100-147,DONE 168 | 2024-01-02 16:51:50.080,DUXGV802ORH8,912810TW8,OFFER,1000000,100-147,DONE 169 | 2024-01-02 16:51:50.080,L9KBVZ7A5M5M,912810TW8,BID,2000000,99-07+,DONE 170 | 2024-01-02 16:51:50.080,L9KBVZ7A5M5M,912810TW8,BID,2000000,99-07+,DONE 171 | 2024-01-02 16:51:50.080,L9KBVZ7A5M5M,912810TW8,BID,2000000,99-07+,DONE 172 | 2024-01-02 16:51:50.080,UR9K097TJ49U,912810TW8,OFFER,3000000,100-002,DONE 173 | 2024-01-02 16:51:50.080,UR9K097TJ49U,912810TW8,OFFER,3000000,100-002,DONE 174 | 2024-01-02 16:51:50.080,UR9K097TJ49U,912810TW8,OFFER,3000000,100-002,DONE 175 | 2024-01-02 16:51:50.080,T9WHGT5HJ6SE,912810TW8,BID,4000000,99-292,DONE 176 | 2024-01-02 16:51:50.080,T9WHGT5HJ6SE,912810TW8,BID,4000000,99-292,DONE 177 | 2024-01-02 16:51:50.080,T9WHGT5HJ6SE,912810TW8,BID,4000000,99-292,DONE 178 | 2024-01-02 16:51:50.080,5F5Q2ACC11CH,912810TW8,OFFER,5000000,100-297,DONE 179 | 2024-01-02 16:51:50.080,5F5Q2ACC11CH,912810TW8,OFFER,5000000,100-297,DONE 180 | 2024-01-02 16:51:50.080,5F5Q2ACC11CH,912810TW8,OFFER,5000000,100-297,DONE 181 | 2024-01-02 16:51:50.080,AJR9403XQGUM,912810RZ3,BID,1000000,99-305,DONE 182 | 2024-01-02 16:51:50.080,AJR9403XQGUM,912810RZ3,BID,1000000,99-305,DONE 183 | 2024-01-02 16:51:50.080,AJR9403XQGUM,912810RZ3,BID,1000000,99-305,DONE 184 | 2024-01-02 16:51:50.080,9F4SLWNQSSWA,912810RZ3,OFFER,2000000,100-035,DONE 185 | 2024-01-02 16:51:50.080,9F4SLWNQSSWA,912810RZ3,OFFER,2000000,100-035,DONE 186 | 2024-01-02 16:51:50.080,9F4SLWNQSSWA,912810RZ3,OFFER,2000000,100-035,DONE 187 | 2024-01-02 16:51:50.080,IP2ZQEGHY7QI,912810RZ3,BID,3000000,99-027,DONE 188 | 2024-01-02 16:51:50.080,IP2ZQEGHY7QI,912810RZ3,BID,3000000,99-027,DONE 189 | 2024-01-02 16:51:50.080,IP2ZQEGHY7QI,912810RZ3,BID,3000000,99-027,DONE 190 | 2024-01-02 16:51:50.080,OUVUAPGZKKS6,912810RZ3,OFFER,4000000,100-217,DONE 191 | 2024-01-02 16:51:50.080,OUVUAPGZKKS6,912810RZ3,OFFER,4000000,100-217,DONE 192 | 2024-01-02 16:51:50.080,OUVUAPGZKKS6,912810RZ3,OFFER,4000000,100-217,DONE 193 | 2024-01-02 16:51:50.080,XVC539FLYHK4,912810RZ3,BID,5000000,99-207,DONE 194 | 2024-01-02 16:51:50.080,XVC539FLYHK4,912810RZ3,BID,5000000,99-207,DONE 195 | 2024-01-02 16:51:50.080,XVC539FLYHK4,912810RZ3,BID,5000000,99-207,DONE 196 | 2024-01-02 16:51:50.080,BGLPOC7S622G,912810RZ3,OFFER,1000000,100-287,DONE 197 | 2024-01-02 16:51:50.080,BGLPOC7S622G,912810RZ3,OFFER,1000000,100-287,DONE 198 | 2024-01-02 16:51:50.080,BGLPOC7S622G,912810RZ3,OFFER,1000000,100-287,DONE 199 | 2024-01-02 16:51:50.080,7IF8JNUWY611,912810RZ3,BID,2000000,99-097,DONE 200 | 2024-01-02 16:51:50.080,7IF8JNUWY611,912810RZ3,BID,2000000,99-097,DONE 201 | 2024-01-02 16:51:50.080,7IF8JNUWY611,912810RZ3,BID,2000000,99-097,DONE 202 | 2024-01-02 16:51:50.080,VGM9D6E4NZUR,912810RZ3,OFFER,3000000,100-31+,DONE 203 | 2024-01-02 16:51:50.080,VGM9D6E4NZUR,912810RZ3,OFFER,3000000,100-31+,DONE 204 | 2024-01-02 16:51:50.080,VGM9D6E4NZUR,912810RZ3,OFFER,3000000,100-31+,DONE 205 | 2024-01-02 16:51:50.080,B1ZXK1T7ZPVI,912810RZ3,BID,4000000,99-032,DONE 206 | 2024-01-02 16:51:50.080,B1ZXK1T7ZPVI,912810RZ3,BID,4000000,99-032,DONE 207 | 2024-01-02 16:51:50.080,B1ZXK1T7ZPVI,912810RZ3,BID,4000000,99-032,DONE 208 | 2024-01-02 16:51:50.080,CPERCVS7RVX4,912810RZ3,OFFER,5000000,100-133,DONE 209 | 2024-01-02 16:51:50.080,CPERCVS7RVX4,912810RZ3,OFFER,5000000,100-133,DONE 210 | 2024-01-02 16:51:50.080,CPERCVS7RVX4,912810RZ3,OFFER,5000000,100-133,DONE 211 | -------------------------------------------------------------------------------- /res/gui.txt: -------------------------------------------------------------------------------- 1 | 2024-01-02 16:51:43.039,9128283H1,98-317,0-003 2 | 2024-01-02 16:51:43.340,9128283H1,99-312,0-002 3 | 2024-01-02 16:51:43.641,9128283L2,100-315,0-002 4 | 2024-01-02 16:51:43.942,9128283L2,99-206,0-002 5 | 2024-01-02 16:51:44.243,912828M80,100-036,0-003 6 | 2024-01-02 16:51:44.545,912828M80,100-085,0-002 7 | 2024-01-02 16:51:44.846,9128283J7,99-080,0-003 8 | 2024-01-02 16:51:45.147,9128283J7,99-285,0-002 9 | 2024-01-02 16:51:45.448,9128283F5,100-107,0-003 10 | 2024-01-02 16:51:45.749,9128283F5,100-110,0-003 11 | 2024-01-02 16:51:46.050,912810TW8,100-016,0-002 12 | 2024-01-02 16:51:46.351,912810RZ3,99-111,0-003 13 | 2024-01-02 16:51:46.652,912810RZ3,99-27+,0-003 14 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # make and enter build directory 4 | mkdir -p build 5 | cd build || exit 6 | 7 | # build project 8 | cmake .. 9 | 10 | # if successfully compiled with multiple threads 11 | if make -j8; then 12 | # start server in background and print its output to the console 13 | ./server & 14 | server_pid=$! 15 | sleep 3 16 | 17 | # start all clients in background 18 | ./price & 19 | ./trade & 20 | ./market & 21 | ./inquiry & 22 | 23 | # trap SIGINT and terminate all processes 24 | trap 'kill $server_pid; pkill -P $$; exit' SIGINT 25 | 26 | # you can press any key to terminate all processes 27 | read -p "Press any key to terminate all processes." -n 1 -r 28 | echo 29 | 30 | # wait all processes to exit 31 | wait 32 | 33 | # kill all processes 34 | kill $server_pid 35 | pkill -P $$ 36 | 37 | cd .. 38 | 39 | else 40 | echo "Build failed" 41 | fi 42 | --------------------------------------------------------------------------------