Compare commits

...
145 Commits
Author SHA1 Message Date
Kevin Darby 8bbe9cfd98 Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-06 12:56:02 -05:00
Kevin Darby ba633532eb hit a wall here with MDClient, going to refactor 2018-12-06 12:55:21 -05:00
Kevin Darby 7bf50415ca nearly ready for testing 2018-12-05 17:55:06 -05:00
Kevin Darby edbaa9c435 looks like we have dropcopy 2018-12-05 15:35:49 -05:00
Kevin Darby 7789f39c9d handle some messages 2018-12-04 18:20:23 -05:00
Kevin Darby 6e188b4f73 shaping up 2018-12-04 16:37:41 -05:00
Pedro Pinto 93abf315a3 Added dev process doc. 2018-12-04 20:01:25 +00:00
Kevin Darby 33e8ff58c5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-04 09:42:30 -05:00
Kevin Darby 24ae03a9f8 access permissions api so fix session can report fills and orders 2018-12-03 18:19:20 -05:00
Kevin Darby 0d706efb10 Merged in sdk_doc (pull request #114)
Sdk doc - Ready to merge

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 22:52:35 +00:00
Pedro Pinto 73fe142904 Merged from master 2018-12-03 17:51:04 -05:00
Pedro Pinto 8668c54784 Updated release notes 2018-12-03 17:49:44 -05:00
Kevin Darby f29e04751d Merged in SPARK_1909 (pull request #118)
added to release_notes, SPARK-1909
2018-12-03 22:43:39 +00:00
Pedro Pinto f9f64c23c1 Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc 2018-12-03 22:37:39 +00:00
Pedro Pinto d3e9f15e99 --no-undefined is a link flag, not a compilation flag.
this was breaking the clang build.
2018-12-03 22:36:29 +00:00
Kevin Darby 6dba376e8b PR feedback 2018-12-03 17:21:46 -05:00
Kevin Darby 5fe0335e1a Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 17:02:34 -05:00
Kevin Darby 99b380a9f3 added to release_notes, SPARK-1909 2018-12-03 16:59:10 -05:00
Pedro Pinto 175061a81e Merged 2018-12-03 16:44:47 -05:00
Eric Harding 3daad4f7e7 Merged in SPARK-1512 (pull request #113)
SPARK-1512 Allow the same name for a new spread when previous one was deleted

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 21:39:37 +00:00
Kevin Darby 6cbf707094 Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 16:06:58 -05:00
Kevin Darby 8bcf2fde87 Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc 2018-12-03 16:05:42 -05:00
Pedro Pinto a5d20157f0 Merged 2018-12-03 15:51:07 -05:00
Kevin Darby d3deac46d4 Merged in fix_mics (pull request #98)
Fix mics - Ready to Merge

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-12-03 20:45:57 +00:00
Kevin Darby 7e4ad55d9d established a session 2018-12-03 15:32:45 -05:00
Eric Harding 288e8a364a Merged in no_mono (pull request #116)
No mono - ready to merge

Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2018-12-03 20:08:17 +00:00
Kevin Darby 35a0074014 bts fix44 adapter 2018-12-03 14:49:35 -05:00
Eric Harding 61c6d0513c Merged in SPARK-1907 (pull request #117)
SPARK-1907 fix crash on server start due to failure to check Result success
2018-12-03 19:48:12 +00:00
Eric Harding b0f2b8e906 SPARK-1907 fix crash on server start due to failure to check Result success 2018-12-03 14:22:27 -05:00
Pedro Pinto c194e76e46 Merge branch 'fix_mics' of bitbucket.org:bluetradesys/spark into fix_mics 2018-12-03 18:58:28 +00:00
Eric Harding e7f8cfaec1 Merged master into no_mono 2018-12-03 13:53:35 -05:00
Kevin Darby 0aa4f80752 add mics deployment notes to ops_notes 2018-12-03 11:26:59 -05:00
Pedro Pinto b8eecfd853 Merge branch 'master' into fix_mics 2018-12-03 15:50:39 +00:00
Jake Norbut 2960ff0996 Merged in jetfix_tools (pull request #110)
iLink purge - removing purged MBX session; adding output for CGW config prints
2018-12-03 15:42:43 +00:00
Michael Harding a071b62560 Merged in Documentation (pull request #109)
Documentation

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 15:41:45 +00:00
Kevin Darby b8dd5c119d Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 10:41:34 -05:00
Pedro Pinto 95004adbad Cleaned up release notes 2018-12-03 15:39:56 +00:00
Pedro Pinto f28334f551 Merge branch 'master' into Documentation 2018-12-03 15:39:03 +00:00
Eric Harding 718a12200b Merged in SPARK-954 (pull request #111)
SPARK-954

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 15:36:10 +00:00
Pedro Pinto d6dab59254 Updated release notes 2018-12-03 15:25:13 +00:00
Pedro Pinto 9e6bdf74f6 Merge branch 'master' into SPARK-954 2018-12-03 15:14:48 +00:00
Pedro Pinto 030a8d5614 Updated release notes 2018-12-03 15:05:35 +00:00
Pedro Pinto 39946baf94 Merged master 2018-12-03 09:57:51 -05:00
Jake Norbut 19be7d7740 Merged in SPARK-1880 (pull request #115)
Security order in synthetic creation follows grid sort

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-12-03 14:49:33 +00:00
Pedro Pinto 37856b9768 Fixed release notes merge 2018-12-03 09:48:22 -05:00
Pedro Pinto dd2067802f Merged master 2018-12-03 09:47:42 -05:00
Eric Harding de6c53a765 Merged in SPARK-1902 (pull request #112)
SPARK-1902 OrderRecordTupleSource Iterator invalidated

Approved-by: John Colvin <john.colvin@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 14:41:46 +00:00
Pedro Pinto b2a20b47ff Updated release notes. 2018-12-03 14:37:20 +00:00
Jake Norbut be16bc2a27 PR - adding 1.4.2 release notes section and documenting this under bug fixes 2018-12-03 08:23:13 -06:00
Eric Harding 71c66f127d What about this? 2018-12-03 08:46:53 -05:00
Kevin Darby 4987fc9fc1 this? 2018-12-02 21:20:00 -05:00
Kevin Darby 16a4c315a2 PR comments 2018-12-02 11:05:21 -05:00
Eric Harding 71619c2e15 web build script should check for npx and paket at start 2018-12-01 22:35:58 -05:00
Eric Harding 5392b706df Remove web dependency on mono 2018-12-01 12:33:55 -05:00
Kevin Darby 8ada73aa64 Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-11-30 18:37:39 -05:00
Kevin Darby cbda28c8b7 save some build time, only generate package on release build 2018-11-30 18:37:11 -05:00
Kevin Darby a16ff30873 lets see how hard this is 2018-11-30 18:32:51 -05:00
Kevin Darby 3956455e81 readme typo 2018-11-30 17:42:40 -05:00
Kevin Darby 76eb541878 Eric removed the dependency on defaults, I'm cleaning up all the places we try to copy them 2018-11-30 17:21:50 -05:00
Kevin Darby d17c56b799 Merge branch 'static_defaults' of bitbucket.org:bluetradesys/spark into sdk_doc 2018-11-30 16:29:04 -05:00
Eric Harding e3795f9498 This file is obsolete and producing warnings 2018-11-30 16:14:40 -05:00
Kevin Darby 8da2569a27 permanent fix for bts_trace-- the definition is in sparklib now, not in sparkd, market_datapublisher_example, etc
this setup will allow us to run .net from linux if we copy the libspark.so to the right place
2018-11-30 16:14:05 -05:00
Eric Harding a598188c14 Load defaults from program segment instead of files 2018-11-30 15:21:43 -05:00
Eric Harding 3648f18a09 Generate an include file with the defaults text as as a string 2018-11-30 15:05:23 -05:00
Kevin Darby 736e051225 works on linux, let's see if this is sustainable 2018-11-30 10:59:11 -05:00
Kevin Darby e007958afd testing linux 2018-11-30 10:08:48 -05:00
Jake Norbut 1682513af1 PR edits, Dicts intsead of HashSets 2018-11-30 09:02:39 -06:00
Kevin Darby cc7e66aa61 looks like excel will work in the sdk now 2018-11-29 16:46:42 -05:00
Jake Norbut cea153df5a Security order in synthetic creation follows grid sort 2018-11-29 14:36:14 -06:00
Kevin Darby 9903b340a6 added brian's excel 2018-11-29 15:05:55 -05:00
Kevin Darby f07b272cff client side stuff works on .netcore2 2018-11-29 12:50:45 -05:00
Kevin Darby 9d74a9d114 sever side stuff updated with group feedback 2018-11-29 11:39:15 -05:00
Kevin Darby e8f9bb8226 slight change to windows batch 2018-11-29 09:38:07 -05:00
Eric Harding 033c1ec5de Add timestamp option to servicetail 2018-11-29 09:37:55 -05:00
Kevin Darby 7fd2c075a1 added excel to client side sdk 2018-11-28 23:24:50 -05:00
Kevin Darby 125773b840 client side sdk to gh 2018-11-28 18:33:10 -05:00
Eric Harding 4cb743004b Move service tailer to it's own top level command 2018-11-28 18:00:55 -05:00
Eric Harding 9dbc9df2a4 Add "sparkcmd service --tail=ServiceName.stream" to watch service updates in the console 2018-11-28 17:12:30 -05:00
Kevin Darby 7c1016d940 script to set up client side sdk 2018-11-28 14:26:55 -05:00
Eric Harding 43198f9602 SPARK-1512 Allow the same name for a new spread when previous one was deleted 2018-11-28 10:24:15 -05:00
Eric Harding 8b1a195121 SPARK-1512 Fix iterator invalidation bug in MDSource 2018-11-28 10:23:30 -05:00
Eric Harding 1cde234be4 SPARK-1902 OrderRecordTupleSource Iterator invalidated 2018-11-28 09:00:08 -05:00
Kevin Darby bba6da76fb straighten up readme and github scripts 2018-11-27 17:31:16 -05:00
Kevin Darby 0f68a7f56b added a little doc and some scripts to help the sdk 2018-11-27 15:48:55 -05:00
Brian Muhr a4963f8bd9 Spreader.md edited online with Bitbucket 2018-11-27 20:35:51 +00:00
Eric Harding 58a762a18a merge from master 2018-11-27 14:08:51 -05:00
Eric Harding 90c13d526a Additional synthetic tests 2018-11-27 14:03:37 -05:00
Eric Harding 743d8e0e81 Add round_to_quote_price for IntPrice 2018-11-27 13:44:18 -05:00
Kevin Darby eb7a79488f starting sdk docs 2018-11-26 18:22:35 -05:00
Jake Norbut 3dd7b9638b HMAC update after iLink purge and new CGW ilink 2018-11-26 15:23:39 -06:00
Jake Norbut 771d4976e9 iLink purge - removing purged MBX session; adding output for CGW config prints 2018-11-26 15:11:01 -06:00
Eric Harding f0a08c6f83 Rename some functions for easier profiling 2018-11-26 13:43:11 -05:00
Kevin Darby c7097faba2 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-26 13:42:41 -05:00
Michael Harding e9a341a013 Pass manual through a grammer checker 2018-11-26 12:57:08 -05:00
Michael Harding 15839ba75d Add videos to manual 2018-11-26 10:55:48 -05:00
Eric Harding cd0b24c16a Add 'allblank' synthetic merge test 2018-11-26 09:44:46 -05:00
Pedro Pinto 7151619645 Dev docs. 2018-11-24 16:35:39 +00:00
Pedro Pinto 684c4037b6 Fixed clang build. 2018-11-24 16:34:41 +00:00
Eric Harding 97a50faf16 Switch from precise to pessimistic(tt) building of synthetic books 2018-11-23 16:31:04 -05:00
Eric Harding ca2a6a1e60 SPARK-954 Update synthetic book merge to floating point sizes 2018-11-23 11:29:35 -05:00
Eric Harding 61a112c221 Add a short sleep to watch_tests 2018-11-21 14:11:04 -05:00
Eric Harding 8e079a970f Update watch_tests.sh to not try to build. Just run when sparkd changes 2018-11-20 19:31:57 -05:00
Eric Harding a4bc22fc3b Add std=c++1z to sdk build script 2018-11-20 17:19:03 -05:00
Kevin Darby 0f81ce1716 hopefully last fix for fix_mics
Enforce at least 1 account selection on User Creation. One can currently create a User and not assign any Account Permissions. If you do this, you as the creator of said user will not see the user in your UI.

Also if a user can't add users he can't clone or remove them either
2018-11-20 17:16:50 -05:00
Eric Harding 97deb63deb Merged in simplify_md_sdk (pull request #102)
Simplify md sdk

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-11-20 21:56:17 +00:00
Eric Harding a3b91a4363 Should have included exchange key in Shmdump 2018-11-20 16:55:55 -05:00
Eric Harding 2551a0f11e Code review feedback 2018-11-20 16:19:11 -05:00
Eric Harding 2f5b06b628 merge from master 2018-11-20 16:08:19 -05:00
Eric Harding 0da184ab73 SPARK-954 add unit test 2018-11-19 17:19:22 -05:00
Kevin Darby d4744d300b jakes #7 2018-11-19 14:19:00 -05:00
Kevin Darby 9b7275e713 let's see if I can get this by Jake 2018-11-19 14:11:28 -05:00
Kevin Darby 51ed3a4c2c some of jake's feedback 2018-11-19 12:02:02 -05:00
Kevin Darby 256b296521 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-16 23:14:43 -05:00
Eric Harding 38668f8de1 Code review feedback 2018-11-16 13:09:05 -05:00
Eric Harding e22649c19d Merge branch 'master' of bitbucket.org:bluetradesys/spark into simplify_md_sdk 2018-11-16 11:23:51 -05:00
Eric Harding 25f749bb0c Simplify md security key generation for spreads. 2018-11-16 11:23:01 -05:00
Eric Harding 4b3cdf9ca4 Add tests for SecurityIDRecorder += SpreadResolver 2018-11-14 19:58:31 -05:00
Eric Harding 2fc4c8bd60 Move sqlite_utils.h out of utils:: namespace 2018-11-14 19:13:23 -05:00
Kevin Darby 7e0d79ac68 Merge branch 'master' of https://bitbucket.org/bluetradesys/spark into fix_mics 2018-11-14 15:17:59 -06:00
Kevin Darby 42b73006a8 AdminReport.cpp edited online with Bitbucket 2018-11-13 23:06:47 +00:00
Kevin Darby 2fc85d6810 AdminReport.cpp edited online with Bitbucket 2018-11-13 22:55:17 +00:00
Michael Harding 385387b739 Merge master into fix_mics 2018-11-13 16:11:04 -05:00
Kevin Darby ea3eacbbae async blows up brian's laptop, adding it as an option 2018-11-09 15:40:26 -05:00
Kevin Darby 9f3941d1a1 favor the async excel interface for our api calls 2018-11-09 13:39:06 -05:00
Kevin Darby 9c8bb7efeb excel is in with the mics 2018-11-09 12:12:31 -05:00
Kevin Darby a171abb741 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-08 13:39:22 -05:00
Kevin Darby 52825e0e59 sparkd adminreport md 2018-11-08 13:38:42 -05:00
Kevin Darby efc490cc0d -if a user creates an account, he is given admin permissions on that account
-if a user creates a user, he assigns that user trading permissions in one of the accounts he can administer (so he can still see that user, someone else can undo)
-bts user has the 'wheel'
-bts user can grant other users 'create' permissions, and those users can in turn grant other users create permissions
2018-11-08 11:52:58 -05:00
Kevin Darby 559ab69940 can_create is enforced 2018-11-07 15:53:40 -05:00
Kevin Darby e75be340e2 GatewayLoginResult 2018-11-06 18:12:31 -05:00
Kevin Darby a5b7332fa4 admin ui almost works with permissions 2018-11-06 16:17:21 -05:00
Kevin Darby 08c54f75eb Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-04 21:32:36 -05:00
Kevin Darby e332edb36b users should be active when created 2018-11-02 18:18:45 -04:00
Kevin Darby 70b5d9adc6 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-02 18:15:24 -04:00
Kevin Darby 23ee9508c2 now all we need to do is filter the webUI 2018-11-02 18:13:12 -04:00
Kevin Darby 1dc87de975 now just need to filter accounts and users in the webui 2018-11-01 23:41:42 -04:00
Kevin Darby 49ba2e72a9 no idea why this doesn't work 2018-11-01 18:35:19 -04:00
Kevin Darby 47dc0d579b mic aliases 2018-11-01 12:18:10 -04:00
Kevin Darby ef6662d4f8 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-01 11:59:20 -04:00
Kevin Darby b9aaaa7d37 mic mapping for brian 2018-11-01 10:37:09 -04:00
Kevin Darby ec43039b94 ok michael, all yours 2018-10-31 18:05:39 -04:00
Kevin Darby 4939b85819 I think I got it 2018-10-31 18:01:54 -04:00
Kevin Darby fab7596d06 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-10-31 17:27:13 -04:00
Kevin Darby 81d67f640b my head hurts from the f# 2018-10-31 17:26:34 -04:00
Kevin Darby fe8f217587 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-10-30 14:57:18 -04:00
170 changed files with 48957 additions and 1193 deletions

No files matched your search

+5 -6
View File
@@ -33,12 +33,12 @@
"requireExactSource": false
},
{
"name": "SDK Launch",
"name": "FIX Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
"args": ["md","GLBX","Future","ZN", "ZNU8"],
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": ["fix","-f","spark_fix.cfg","-d"],
"cwd": "${workspaceRoot}/build/Debug/bin/net471/",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
@@ -209,8 +209,7 @@
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
// "args": ["test","Co2RelayTests"],
"args": ["md", "run", "10"],
"args": ["test","Co2Tests"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
+1 -1
View File
@@ -41,6 +41,7 @@ else ()
-Wno-missing-braces -Wno-ignored-qualifiers \
--std=c++17 -fdiagnostics-color")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -DNDEBUG -O3")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wno-undef")
add_subdirectory(src/server/sparkd)
add_subdirectory(src/lib)
add_subdirectory(src/web)
@@ -51,7 +52,6 @@ else ()
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/server
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/sparkd ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/defaults ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/web ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
+44
View File
@@ -25,3 +25,47 @@
***** FillStore
***** SecuritySrc
* Benchmarks
* OrderMan
** OrderServices
*** callbacks
**** standard
- update
- cancel
- crack
- activate
- book
- settlement
- child order state
- timer
**** low level
- exe reports
- cancel rejects
- gateway status
*** services
**** standard
- secdb
- get time
- schedule actions
- order builder
- activate
- update
- cancel
- crack
**** low level
- governor
- send fix
- get time str
- limiter
** Internalize plan
- change order initiated update/cancel/crack/activate/report_state to
use direct method calls
- need to watch out for reentrancy
- need to also report state externally
- also want to separate active from non active orders in map to speed
up look ups
- want to use direct references in orders to avoid lookups
- want to change how time is captured, the trace is too big to copy
around
+71
View File
@@ -0,0 +1,71 @@
# The BTS Development Process #
## The Big Picture ##
Our development work is organized in projects. A project is an effort
to specifiy, develop and validate a related set of additions to our
Spark platform. A project can be assigned to one more developers and a
developer might concurrently be assigned to multiple
projects. Projects typically start out with specification work,
proceed to implementation and finish with validation efforts. At any
given time there will be multiple projects being worked on
concurrently. A project is concluded when the features it implements
are merged into the product and deployed to customers.
## The Details ##
Typically project ideas come from sales interactions, opposition
research and customer feedback, alltough on occasion development or
operational requirements will also suggest potentially valuable
ideas. Whatever the source of a project idea the final decision of
which projects to pursue is always done by management. Once a project
idea has been selected for implementation the corresponding project
will go through several phases: inception, specification, development
and merge. We detail each of these phases below.
* Inception. In this phase we create the infrastructure for the
project. Typically this means selecting a team, creating a project
case in Jira (that is a case assigned to the team@bluetradesys.com
Jira user) and possibly creating a new branch for the project. This
phase is mostly mechanical and the responsibility of Pedro.
* Specification. The specification phase turns a usually vague and
ill-considered idea project idea into a concrete, implementable
specification that attempts to maximize the value/cost ratio of the
project. It is hard to exaggerate the impact of a this phase on the
rest of the project. *Nothing* has more impact on our ability to
quickly deliver value to customers than good specification
work. Specification typically take the form of shared google docs,
using a mix of text and UI mockups to capture both user requirements
and suggested implementation strategies. Taha is responsible for this
phase.
* Development. Once a specification is complete, a team of developers
will use a project specific branch to implement the
specification. Periodically, typically once a week, progress on the
project is assessed by Pedro, possibly with the help of others. This
assessment takes the form of code reviews, demos, or just one-on-one
conversations. To ensure development does not go off on sub-optimal
tangents, developers are expected to continuously merge master into
the project branch as well as encouraged to create Pull Requests (PRs)
early in the project implementation, so that their work is kept
visible and integrated with that of the rest of the group. During the
implementation phase developers should reach out to Pedro for UI
reviews, architecture and design concerns as well as specification
trade off concerns.
* Merge. Once a project is considered complete by developers, a PR
that includes Pedro should be created (unless, as mentioned above, an
early PR was already created in the development phase). Whether the PR
already existed or not, its description should be changed to start
with the "Ready To Merge" statement. This marks the project as ready
to be merged into master. Once a week, typically on Mondays, Pedro
will go through every PR marked as "Ready To Merge", build it, check
it out and if everything looks good merge it to master. At the end of
the day the resulting master will be tagged with a new version
number. This version will be go through some additional testing by
Brian and will then be deployed by Karl and Pete to customers
throughout the rest of the week.
## Tools ##
* Pre-Merge checklist
* Code Review tutorial
+4
View File
@@ -1,5 +1,9 @@
#Help
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/yGQfr_puT3w" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
##Report a Problem
If there is any issue at all, you can click here
and it’ll bring up a “Report a Problem” window, which you can type
+6 -2
View File
@@ -1,5 +1,9 @@
#Module Launcher
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/OxVqQBKFIDw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- The module launcher is the first item that will come up after
logging in to Spark.
- This is your central control for all modules
@@ -9,7 +13,7 @@
![](images/image38.png)
- If you right click anywhere on the launcher you can modify
- If you right-click anywhere on the launcher you can modify
various display settings including:
- Vertical layout, instead of horizontal
- Showing the text of the individual modules
@@ -25,5 +29,5 @@
Some common features within BTS Spark
- All Windows are tabbed so they can be combined or separated to organize your workspace
- Some Spark windows have a menu on the left hand side where you can launch related modules or change settings. You can see the menu item names either by hovering your mouse over the icon, or clicking on the right edge of the menu (a blue bar will appear).
- Some Spark windows have a menu on the left-hand side where you can launch related modules or change settings. You can see the menu item names either by hovering your mouse over the icon or clicking on the right edge of the menu (a blue bar will appear).
-  Some table columns have filtering capabilities, where if you hover over the column header, it will display a filter icon, which enables you to filter the table based on the information in that column.
+16 -8
View File
@@ -9,6 +9,10 @@
##Market View
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Y54S1uscT_g" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- This provides a grid of outright, spread, and synthetic products
showing real-time market data all in one place
- You can create multiple market view grids and customize the
@@ -29,7 +33,7 @@
and all securities will be displayed.  This will show up in the
selected securities window as “&lt;symbol&gt; future at
&lt;exchange&gt;”
- Synthetic’s can be displayed by clicking on exchange “BTS”, then
- Synthetics can be displayed by clicking on exchange “BTS”, then
synthetic, symbol, and finally under the securities window.
- Below is an example of an outright, spread, all futures, and
synthetic chosen:
@@ -98,10 +102,10 @@
either the Bid or Ask price and volume.
- Highlighting a single security and clicking on the order ticket
![](images/image23.png) icon
on the left hand browser window of the market view
on the left-hand browser window of the market view
- Launch a ladder for 1 security
- You can double click on any security in the market view, besides
- You can double-click on any security in the market view, besides
the Bid Qty, Bid Price, Ask Price, or Ask Qty columns to launch a
ladder for that Security.
@@ -110,7 +114,7 @@
- If you hold down the Ctrl key and select multiple securities, you
can then click on the ladder icon
![](images/image69.png) on
the left hand side to bring up the selected ladders all at
the left-hand side to bring up the selected ladders all at
once.
- Launching a Fills window for 1 or many securities
@@ -118,7 +122,7 @@
- This is done by either highlighting 1 or many securities and then
clicking on the Fills icon
![](images/image20.png) on
the left hand side to bring up a fill window for the selected
the left-hand side to bring up a fill window for the selected
securities.  
- Please note - If there haven’t been any fills for the day in that
@@ -129,14 +133,14 @@
- This is accomplished by highlighting 1 or many securities and then
clicking on the time and sales icon
![](images/image49.png) on
the left hand side
the left-hand side
- Launching the synthetic window to create a synthetic product
- This is accomplished by highlighting either 1 or 2 securities and
clicking on the Synthetic icon
![](images/image34.png) on
the lower left hand side
the lower left-hand side
- Please note - If only 1 security is chosen, a create synthetic
window will appear, and you will be required to add another leg from
@@ -144,7 +148,11 @@
##Time and Sales
- Provides a display of real time fills as they happen for 1 or many
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/nEkSGacjt0s" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- Provides a display of real-time fills as they happen for 1 or many
securities
- You can create multiple time and sale windows and customize the
security selection to fit your needs
+6 -2
View File
@@ -1,5 +1,9 @@
#My Windows
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/pjm1JJ9Q3jM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
This is the housing of the windows that you have manually named and
creates a place where you can quickly and easily launch windows
which you’ve customized.
@@ -18,9 +22,9 @@ section of the module launcher.
![](images/image15.png)
Right clicking on a tab that has already had its default title modified
Right-clicking on a tab that has already had its default title modified
will show the following which will allow you to rename the modified
title or remove it from the my windows, which will then restore the
title or remove it from the My Windows, which will then restore the
default standard title.
![](images/image37.png)
+4
View File
@@ -1,6 +1,10 @@
#Settings
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/yGQfr_puT3w" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
##Workspaces
This is where your workspaces can be saved or switched
+34 -14
View File
@@ -1,14 +1,35 @@
#Spreader
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/XEZpuPAgMC0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/EVFK2yNQFS4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/d5ywlHen6yo" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Mmw8PNJA0Dw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/QK-bBSH6Rc0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- Launching the spreader from the module launcher will bring up the
synthetic management browser which appears like below:
![](images/image35.png)
- From this window it’ll show any synthetics that you have already
- From this window, it will show any synthetics that you have already
created as well as various other options of both managing the
synthetics and other tasks all from the left hand side pane within
synthetics and other tasks all from the left-hand side pane within
the window, including:
- Adding a new synthetic ![](images/image8.png)
@@ -30,7 +51,7 @@
- From the main module launcher, you can go to spreader, then
synthetics, then click add
- Highlighting one or more securities in a market view and clicking
on create synthetic
on Create Synthetic
- Cloning an existing security
- An empty synthetic browser will look like below without any legs
@@ -43,10 +64,10 @@
- Upon adding legs, you can then go through each of the parameters to
customize how you would like the spread to be worked in the
market
- We’ll start at the top of create synthetic window and work our way
- We will start at the top of the Create Synthetic window and work our way
down:
- At the very top of the create synthetic window you will have to
- At the very top of the Create Synthetic window you will have to
name your synthetic product.  By default, it will auto-populate
based on the symbols you have chosen:
@@ -73,16 +94,15 @@
exchange
- Furthermore, if you are ever unsure of what a parameter’s function
is and how it is controlled you can hover over the parameter’s title
and a “?” will be shown.  Clicking on this will expand built in
and a “?” will be shown.  Clicking on this will expand built-in
details of how that parameter functions.  Below is an example of the
throttle details displayed:
![](images/image56.png)
- Main parameters - these are the top 4 rows in the create synthetic
- Main parameters - these are the top 4 rows in the Create Synthetic
window which all synthetics will be required to have set.
 Everything below these are custom to a traders needs, however the
top 4 are the main foundational aspects used for the calculation of
 Everything below these can be customized to a trader's needs, however, the top 4 are the main foundational aspects used for the calculation of
the synthetic:
![](images/image10.png)
@@ -95,7 +115,7 @@
be priced, by default, it’ll be based on the same as the quantity of
the synthetic produced, but you can change it to modify the balance
of the price, giving more weighting to one leg over another.
- Enable quoting - This parameter is in the form of a check box,
- Enable quoting - This parameter is in the form of a checkbox,
where if the box is checked the leg will be quoted versus not
checked, it will not be shown in the market.  If it is not actively
quoted in the market, the leg will still be used for calculation of
@@ -107,7 +127,7 @@
 If your Hedge Ratio is 3 on the NQ leg, then you’ll work the ES
order based on where you could fill 6 (2 (Qty of NQ) \* 3 (Hedge
Ratio)).
- The next 6 parameters are all completely custom to a traders needs
- The next 6 parameters are all completely custom to a trader's needs
and can further define how a synthetic is worked.  As previously
mentioned, if you hover over any of these parameters a “?” will be
displayed and click on such will display further details on how this
@@ -118,7 +138,7 @@
- Quote
- Throttle - This can be enabled or disabled based on if the box is
checked per leg.  It’s main function is to reduce re-quoting updates
checked per leg.  Its main function is to reduce re-quoting updates
by defining a region on either side of the working quote order,
inside which quote updates will not be sent:
@@ -148,8 +168,8 @@
![](images/image70.png)
- Time Based Cross - Basically the exact same as ticks away cross,
however this is based on time (in ms) on when to automatically cross
- Time Based Cross - Basically the same as Ticks Away Cross,
however, this is based on time (in ms) on when to automatically cross
(fill) the market if they get too far away from the opposite inside
market.  If a legged hedge order’s price gets X amount of “ticks”
away from the opposite inside market, it is re-priced to cross the
+47 -33
View File
@@ -9,7 +9,11 @@
##Order Book
- This provides list of all orders a user has placed, by default it
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/ygMFkjBBGeU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- This provides a list of all orders a user has placed, by default it
will display only working orders
- You can create multiple order books filtered however you would
like
@@ -44,13 +48,13 @@
- Anytime
- Today
- Since Open
- A specific user defined period of time
- A specific user-defined period of time
![](images/image25.png)
- The left hand box within an order book provides users with further
- The left-hand box within an order book provides users with further
functionality to quickly perform various tasks with live orders as
well as launch other modules.  It’s appears like the below:
well as launch other modules.  It appears like the below:
![](images/image4.png)
@@ -62,7 +66,7 @@
- ![](images/image73.png)Cancel
selected - clicking on this will cancel the selected orders you have
highlighted in the order book.  You can choose more than one by
holding hold the Ctrl key when click on each row of orders
holding hold the Ctrl key when clicking on each row of orders
- ![](images/image74.png)Cancel
buys - clicking on this will cancel all the buy orders you have in
the order book
@@ -75,8 +79,8 @@
##Fills
- This provides list of all fills a user has had for a given time
period, by default it will open up with “since open” time frame
- This provides a list of all fills a user has had for a given time
period, by default it will open up with “since open” timeframe
selected showing you fills on the day thus far
- You can create multiple fill windows, filtered however you
like
@@ -85,8 +89,12 @@
##P&amp;L
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/xj6cEQ6By34" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- This provides a running ledger of a users account(s) profit and
loss broken down per symbol
loss, broken down per symbol
- You can create multiple P&L windows, filtered however you would
like
- All P&L windows are brought up as tabs and can be combined with all
@@ -101,6 +109,10 @@
##Ladder
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Q0qsyc1nvYo" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- The ladder was designed to be the most logically produced ladder on
the market, engineered and designed by professional traders for
professional traders
@@ -112,8 +124,8 @@
![](images/image36.png)
- The ladder consist of 2 major parts, the Ladder itself and the
information and control section on the left hand side
- The ladder consists of 2 major parts, the Ladder itself and the
information and control section on the left-hand side
- The information section on the left contains the following: &nbsp; ![](images/image1.png)
- Day - showing the current price of the security on the day
@@ -126,17 +138,17 @@
- The control section right underneath the information section on the
left hand side offers the following:
left-hand side offers the following:
![](images/image62.png)
- Account in which the orders will flow into when trading this
- The account in which the orders will flow into when trading this
security
- Length of time the order will remain in market, either DAY or
- Length of time the order will remain in the market, either DAY or
GTC
- Order type, by default orders will go in as Limit, however all of
- Order type, by default, orders will go in as Limit, however all of
the following are offered out of the box.  The top 5 are all fairly
standard order types, however the bottom 5 are all specialized
standard order types, however, the bottom 5 are all specialized
algos.  They are only offered by BTS and require specialized
parameters.  Please see below for each order type:
@@ -147,8 +159,8 @@
- Limit
- This is a buy or sell order designed to take place on the exact
price level you have entered the order at.
- This is a buy or sell order designed to take place on the
price level where you entered the order.
- Stop
@@ -218,8 +230,7 @@
market flips and has at least threshold size as defined.
- “Flip” refers to the event where the best offer becomes the best
bid or the market trades away from you, i.e. the line moves up in
the GUI.  This is all true in the reverse way as well if
selling.
the GUI.  The reverse is true if selling.
![](images/image41.png)
@@ -241,7 +252,7 @@
- You can manually type in a quantity here as well
- Furthermore, if you have a value in the volume bar, you can
increase this size by right clicking and the value will increase
increase this size by right-clicking and the value will increase
incrementally by 1.  The same is true if you would like to decrease
this value by left click and the value will decrease.
- By having the auto clear box checked, it will bring the value back
@@ -252,12 +263,12 @@
-  The bottom section of the control area of a ladder has 4 aspects
to it
- Cancel Buys - which will cancel all buys currently working on the
- Cancel Buys, which will cancel all buys currently working on the
ladder
- Cancel Sells - which will cancel all sells currently working on the
- Cancel Sells, which will cancel all sells currently working on the
ladder
- Cancel All - which will cancel all orders on the ladder
- Settings - this will launch the ladder settings window which
- Cancel All, which will cancel all orders on the ladder
- Settings, which will launch the ladder settings window which
appears like below
![](images/image71.png)
@@ -269,27 +280,26 @@
- There are 5 main columns
- Orders - which display current orders working in the market
- Bid - which displays the current bid volumes at each price
- Orders, which display current orders working in the market
- Bid, which displays the current bid volumes at each price
level
- Price - which displays the price levels of the symbol shown
- Ask - which displays the current ask volumes at each price
- Price, which displays the price levels of the symbol shown
- Ask, which displays the current ask volumes at each price
level
- Last Trade - which displays where the price level and volume of
- Last Trade, which displays the price level and volume
where the last trade occurred
- Functionality of the ladder
- To enter an order directly by clicking in the ladder, left click on
either the bid or ask volume with a volume greater than 0 on the
predefined value box on the left hand section
predefined value box on the left-hand section
- A badge will be displayed within the orders column of the value you
have chosen and the quantity filled as displayed below
![](images/image46.png)
- To change the price level you wish to work the order, you can right
click and hold on the badge to move it up or down the ladder for the
- To change the price level you wish to work the order, you can right-click and hold on the badge to move it up or down the ladder for the
desired price
- To cancel the order, left click on the badge
- Furthermore, you can click down on the scroll wheel of your mouse
@@ -298,6 +308,10 @@
## Order Ticket
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/bdC5pZ6T35I" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- The order ticket was designed to provide another means other than
the ladder to enter orders into the market
- You can bring up an order ticket directly from the module launcher,
@@ -307,7 +321,7 @@
![](images/image27.png)
- On the top left you have the price for the order as well as the
- On the top left, you have the price for the order as well as the
volume
- Beneath are the predefined volumes as well as a clear button to
clear out the volume
+1 -1
View File
@@ -1,3 +1,3 @@
Click on the headings on the the left to expand their contents. If you need specific information quickly use the Search button in the top right of this page.
Click on the headings on the left to expand their contents. If you need specific information quickly use the Search button in the top right of this page.
+37 -19
View File
@@ -1,13 +1,31 @@
# Release Notes
## 1.4.2
### User Interface Improvements
- When creating a synthetic from the Market View window, securities are now added to the Create Synthetic dialog in the same sort order as they appear in grid
### Bug Fixes
- Shell no longer crashes when removing a security from the synthetics window
- It is now possible to create a new synthetic using the same name as a previously deleted synthetic
### SDK Improvements
- Improved Client and Server SDK clarity and documentation
### Performance Improvements
- Synthetic calculation speed was improved by 400%.
### Risk / Entitlement System
- Risk Admin UI
- Spark users now have separate Trading, RiskAdmin, and/or MDAdmin permissions
- Spark users can now only add other users or accounts if permissioned to do so.
- Market Data Entitlement
- Automated market data reporting
## 1.4.1
### Tabs
### Tabs
- Tabs now shrink and expand to make better use of horizontal space
### Bug Fixes
- Fixed a bug that could cause excessive CPU usage when a connection broke
- Fixed a layout bug in hedging/requoting performance reports
- Fixed account name mismatching bug for old fills
- Fixed a layout bug in hedging/requoting performance reports
- Fixed account name mismatching bug for old fills
### Absolute Position limits
- Positions can now be netted or absolute for purposes of limit enforcing
@@ -17,7 +35,7 @@
- Improved requoting and hedging performance
- Improved order book form performance
### Server Side SDK
- Added beta version of the server side SDK
- Added a beta version of the server side SDK
### Front End
- Added status bars to all UI grids
### Reporting
@@ -29,7 +47,7 @@
- Further speed enhancements
### Conditional (new quoting parameter)
- Reduces re-quoting updates by only quoting if N ticks away from the opposite inside market.
- Quotes are cancelled when it gets further away.
- Quotes are canceled when it gets further away.
- "Price Type" dictates if this logic applies to the quoting market or to the synthetic spread market.
## 1.3.7
@@ -53,7 +71,7 @@
### Synthetic creation option to choose Merged or Direct
- The ability to choose Merged or Direct per leg when creating a synthetic
### Volume at price
- Within a ladder you can now see a volume at price graph
- Within a ladder, you can now see a volume at price graph
- This is configurable from a ladder's settings as well
### Customized settings in all windows
- All windows now contain a settings page where grid settings (font color, size, etc) are applied
@@ -71,7 +89,7 @@
### Order Book
- Many visual facets of the Order Book grid can now be customized
- The Order Book settings page can be access from the gears icon in the Order Book toolbar or from the right-click menu
- The Order Book settings page can be accessed from the gears icon in the Order Book toolbar or from the right-click menu
- The Expand/Collapse All button has been separated into two different buttons on the toolbar
### Sound Alerts
@@ -107,13 +125,13 @@
### Synthetics
- Hedge Ratio parameter has been renamed to Lean Qty Factor
- Spreader now includes a Hidden mode parameter
- A quoting leg can be hidden, ie an order will not sent out until the market crosses the computed quoting price. When that happens, the quote is sent out as an IOC order.
- A quoting leg can be hidden, ie an order will not be sent out until the market crosses the computed quoting price. When that happens, the quote is sent out as an IOC order.
### Algos
- Trigger orders now can contain a Trigger Exit parameter based on time or price; once the exit condition has been met the Trigger order will cancel
- IOC/FAK order type added
- Cover order type added
- Cancelling a synthetic order no longer cancels any working hedge orders
- Canceling a synthetic order no longer cancels any working hedge orders
### General
- A manual page has been added to the launcher Help menu
@@ -121,18 +139,18 @@
- Various cash per point values updated
- Time-based grid filters now have tick up/down buttons
- Numerous performance improvements and stability enhancements
- Automated position and orderbook maintenance during daily recycle
- Individual fills be compacted into start of day position records
- Automated position and Order Book maintenance during daily recycle
- Individual fills will be compacted into the Start of Day position records
- Orders from the day before the recycle will be deleted
##1.1
###1.1.22
- Orderbook: Orders in Error status can now be recreated
- Orderbook: Orders containing child orders will now be collapsed by default.
- Orderbook: Expand / Collapse All button added to the right-click menu and window action pane, denoted by ▲ icon
- Orderbook: Symbol column added to allow for filtering based on group symbol
- Order Book: Orders in Error status can now be recreated
- Order Book: Orders containing child orders will now be collapsed by default.
- Order Book: Expand / Collapse All button added to the right-click menu and window action pane, denoted by ▲ icon
- Order Book: Symbol column added to allow for filtering based on group symbol
- Time filters now have a quick Set Last Hour hyperlink which will set the Period Begin filter to (Now - 1 hour)
###1.1.20
@@ -151,15 +169,15 @@
- Fixed occasional crash when filtering fills
- Fixed several cash per point multipliers
- Colored time of sales row with aggressor side
- Iceberg orders now cancel (rathern than error) when a child is canceled
- Iceberg orders now cancel (rather than an error) when a child is canceled
###1.1.0
- Added per user self match protection, location and self match protection FIX tag settings
- Implemented a new "Trigger Order" algo
- Corrected tick formatting for a number of products
- Added the hability to recreate canceled orders
- Added leg fills to facilitate futured based PnL
- PnL report now has PnL columns calculated based on your start of day, current day, and total position
- Added the ability to recreate canceled orders
- Added leg fills to facilitate futures based PnL
- PnL report now has PnL columns calculated based on your Start of Day, current day, and total position
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
- Corrected cash per point values, used in pnl calculations, for a number of products
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
+7
View File
@@ -1,3 +1,10 @@
1.4.2
- Be sure the system has a bts user before deployment. bts is the superuser, he has all permissions and can see all market data
- After deployment re-permission all existing users for trading, riskadmin, and/or md admin permissions as appropriate
- Also note which users can add other users and accounts and permission them appropriately (Can Add?)
- Permission users for appropriate market data entitlements; this has monetary consequences so ask Kevin if you aren't sure.
- MIC reporting needs to be added to audit Jenkins job (https://bluetradesys.atlassian.net/browse/SPARK-1900)
1.3.5
- Change in market data wire format. Old shell will get only direct market data instead of merged which may not match what the servers is driving from.
+22
View File
@@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\sp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_excel", "src\client\spark_excel\spark_excel.csproj", "{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_fix", "src\client\spark_fix\spark_fix.csproj", "{D8031C13-7BEA-430A-BB60-40927D15CFC3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -219,6 +221,26 @@ Global
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x64.Build.0 = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x86.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|Any CPU.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x64.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x64.Build.0 = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x86.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|Any CPU.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x86.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+2 -1
View File
@@ -1,5 +1,6 @@
using bts;
using bts.api.admin;
using bts.api.core;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
@@ -36,7 +37,7 @@ namespace mbts.spark {
IEnumerable<BTSAccountImmutable> active_accounts =
from admin_data in update.all
from bts_account in admin_data.Accounts
where bts_account.IsPresent
where bts_account.IsPresent && _wrapper_subject.Value.can_view_account(bts_account.Name)
join account_id in (
from bts_user in active_users
from account_id in bts_user.UserData.AccountPermissions.AccountPermissions.Keys
+1 -1
View File
@@ -49,7 +49,7 @@ namespace bts {
.Subscribe(t => {
var deleted_synthetic_keys = new HashSet<uint>(t.Where(kv => kv.Value.Deleted).Select(kv => kv.Key));
// add back 'undeleted' synthetics
foreach (var kvp in _deleted_synthetics) {
foreach (var kvp in _deleted_synthetics.ToList()) {
if (!deleted_synthetic_keys.Contains(kvp.Key)) {
_exchange_sec_map.TryAdd(kvp.Key, kvp.Value);
_deleted_synthetics.Remove(kvp.Key);
@@ -65,7 +65,7 @@ namespace mbts.spark {
})
),
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs)),
SparkCommands.ShowSyntheticCreate.complete(_vm.sorted_selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs)),
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
SparkCommands.window_settings_command(new DialogCommand(() => {
@@ -6,6 +6,7 @@ using mbts.ui;
using mbts.ui.spark;
using mbts.ui.utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
@@ -43,8 +44,23 @@ namespace bts.shell {
name + "(1)";
}
async void _show_synthetic_create(SecurityCriteria security_criteria) =>
_show_synthetic_create((await security_criteria.securities(_bus)).ToList());
async void _show_synthetic_create(SecurityCriteria security_criteria) {
var found_securities = await security_criteria.securities(_bus);
var respect_clause_security_ordering_is_possible = security_criteria.clauses.Any(clause => clause.security_keys != null && clause.security_keys.Count > 0);
if (!respect_clause_security_ordering_is_possible) {
_show_synthetic_create(found_securities.Values.ToList());
} else {
List<SecurityImmutable> ordered_security_list = new List<SecurityImmutable>();
foreach (var clause in security_criteria.clauses) {
foreach (var sec_key in clause.security_keys) {
if (found_securities.TryGetValue(sec_key, out SecurityImmutable found_result)) {
ordered_security_list.Add(found_result);
}
}
_show_synthetic_create(ordered_security_list);
}
}
}
void _show_synthetic_create(IReadOnlyCollection<SecurityImmutable> securities) {
if (securities.Count == 1) {
@@ -95,7 +95,7 @@ namespace mbts.spark {
/// Warning: <see cref="_security_md_cache"/> must NOT be reassigned with <see cref="securities"/>; the latter is always the same object.
/// This would mean that the <see cref="GlobalExtensions.difference{T}(IEnumerable{T}, IEnumerable{T}, out IEnumerable{T}, out IEnumerable{T})"/>
/// call above would literally be calculating the difference between the keys of the same dictionary!
foreach (var k in removed_keys) {
foreach (var k in removed_keys.ToList()) {
if (_security_orders.try_lookup(k, out ISet<uint> order_keys)) {
SecurityMD old_sec_md = _security_md_cache[k];
removed_securities.Add(k, old_sec_md);
@@ -109,7 +109,7 @@ namespace mbts.spark {
}
_security_md_cache.Remove(k);
}
foreach (var k in added_keys) {
foreach (var k in added_keys.ToList()) {
SecurityMD new_sec_md = securities[k];
if (_security_orders.try_lookup(k, out ISet<uint> order_keys)) {
added_securities.Add(k, new_sec_md);
@@ -25,7 +25,7 @@ namespace mbts.spark
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
max_chase_ticks = new InlineUserProperty<uint>(2, false);
trade_toward_behavior = new InlineUserProperty<LeanOrderDefinition.Types.OnToward>(LeanOrderDefinition.Types.OnToward.Cancel, false);
trade_toward_behavior = new InlineUserProperty<LeanOrderDefinition.Types.OnToward>(LeanOrderDefinition.Types.OnToward.NoChange, false);
}
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
@@ -47,7 +47,7 @@ namespace mbts.spark
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
public uint max_chase_ticks { get; set; } = 2;
public LeanOrderDefinition.Types.OnToward trade_toward_behavior { get; set; } = LeanOrderDefinition.Types.OnToward.Cancel;
public LeanOrderDefinition.Types.OnToward trade_toward_behavior { get; set; } = LeanOrderDefinition.Types.OnToward.NoChange;
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
+3 -3
View File
@@ -44,8 +44,8 @@ namespace mbts.spark {
_dynamic_helper = dynamic_helper;
var scheduler = ThreadPoolScheduler.Instance;
var interval = TimeSpan.FromMilliseconds(100);
on_orders = _service.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_orders = _service.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
working_orders =
on_orders.Scan(
ImmutableDictionary<uint,OrderRecordImmutable>.Empty,
@@ -55,7 +55,7 @@ namespace mbts.spark {
on_order_tuples = order_record_tuple_source.subscribe_incremental_update();
on_order_tuples_table = order_record_tuple_source.subscribe_incremental_table_update();
on_order_securities = order_record_tuple_source.subscribe_order_securities();
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
_activity = new OrderActivitySummarizer(bus, on_orders);
on_fill_tuples = FillTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders, on_fills);
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
-6
View File
@@ -8,7 +8,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>Debug;Release</Configurations>
<Platforms>x64</Platforms>
<RuntimeIdentifiers>win7-x64</RuntimeIdentifiers>
<PlatformTarget>AnyCPU</PlatformTarget>
<LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets>
<ApplicationIcon>logo.ico</ApplicationIcon>
@@ -82,9 +81,4 @@
</ItemGroup>
<Target Name="diagnostic" AfterTargets="PreBuildEvent">
</Target>
<Target Name="copy_defaults" AfterTargets="PostBuildEvent">
<!-- this feels like the wrong place to copy defaults but it works alright -->
<!-- <Message Importance="High" Text="copy defaults :: @(ConfigDefaults)" /> -->
<Copy SourceFiles="@(ConfigDefaults)" DestinationFolder="$(OutputPath)\defaults\" SkipUnchangedFiles="true" />
</Target>
</Project>
@@ -18,10 +18,10 @@ namespace mbts.spark {
return self == null ? all.Values : all.Values.Where(md => ContractPickerModel.security_in_criteria(self, md.security));
}
public static async Task<IEnumerable<SecurityImmutable>> securities(this SecurityCriteria self, IBus bus) {
public static async Task<IDictionary<uint,SecurityImmutable>> securities(this SecurityCriteria self, IBus bus) {
var md_map = await bus.subscribe(SparkTags.SecuritiesTag).FirstAsync();
var all_secs = md_map.Values.Select(md => md.security);
return self == null ? all_secs : all_secs.Where(s => ContractPickerModel.security_in_criteria(self, s));
var all_secs = md_map.Values.Select(md => md.security).ToDictionary(key => key.Id);
return self == null ? all_secs : all_secs.Values.Where(s => ContractPickerModel.security_in_criteria(self, s)).ToDictionary(key => key.Id);
}
public static IObservable<IEnumerable<SecurityImmutable>> subscribe(this SecurityCriteria self, IBus bus) {
@@ -122,6 +122,21 @@ namespace mbts.spark
where TRow : class, ISecurityRow =>
security_grid.selected_securities.ObserveSelf();
public static IObservable<IReadOnlyList<SecurityImmutable>> sorted_selected_securities_observable<TRow, TRowModel>(this SecurityGridBase<TRow, TRowModel> security_grid)
where TRow : class, ISecurityRow =>
security_grid.selected_rows
.ObserveSelf()
.Select(rows =>
rows.Select(
row => (security: row.security, row_index: security_grid.rows_view.indexOf(row))
)
.OrderBy(sec_vs_index => sec_vs_index.row_index)
.Select(sec_vs_index => sec_vs_index.security)
.ToReadOnlyList()
);
public static IObservable<IReadOnlyList<SecurityMD>> selected_security_mds_observable<TRow, TRowModel>(this SecurityMDGrid<TRow, TRowModel> security_md_grid)
where TRow : class, ISecurityMDRow =>
security_md_grid.selected_rows.ObserveSelf(true).Select(rows => rows.Select(r => r.md).ToReadOnlyList());
@@ -3,4 +3,5 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("spark_core")]
[assembly: InternalsVisibleTo("sparkcmd")]
[assembly: InternalsVisibleTo("spark_test")]
[assembly: InternalsVisibleTo("spark_fix")]
+23
View File
@@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
using bts.api;
using bts.api.core;
namespace bts.api
{
public class Authenticator
{
readonly static Lazy<user.UserService> _auth = new Lazy<user.UserService>(() => Locator.locate<user.UserService>(), true);
user.AuthToken _token = null;
public Authenticator() { }
public user.AuthToken last_token => _token;
public async Task<user.AuthToken> login(string login, string password)
{
var res = await _auth.Value.authenticate_login_async(new user.AuthenticateLoginRequest { Login = login, Password = password });
if (res.success)
_token = res.value.Token;
return _token;
}
}
}
+17
View File
@@ -0,0 +1,17 @@
using System;
using System.Threading.Tasks;
namespace bts
{
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory) :
base(() => Task.Factory.StartNew(valueFactory))
{ }
public AsyncLazy(Func<Task<T>> taskFactory) :
base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap())
{ }
}
}
+16
View File
@@ -0,0 +1,16 @@
using System;
using System.Threading.Tasks;
using bts.api;
using bts.api.core;
namespace bts.api
{
public static class Locator
{
readonly static Lazy<ServiceContext> _context = new Lazy<ServiceContext>(() => new ServiceContext(), true);
public static T locate<T>(string qualifier="") where T : class, IApiClient
{
return _context.Value.locate<T>(qualifier);
}
}
}
+40 -17
View File
@@ -33,50 +33,54 @@ namespace bts.api.md
readonly ConcurrentDictionary<security_key, Data> _data = new ConcurrentDictionary<security_key, Data>();
readonly ConcurrentDictionary<security_key, string> _security_key_to_desc= new ConcurrentDictionary<security_key, string>();
readonly ConcurrentDictionary<string, security_key> _security_desc_to_key = new ConcurrentDictionary<string, security_key>();
readonly Lazy<MDRequest> _subscription;
readonly MDRequest _subscription;
readonly Venue _venue;
readonly static Lazy<MarketDataRelay> _md = new Lazy<MarketDataRelay>(() => locate(), true);
readonly static Lazy<MarketDataRelay> _md = new Lazy<MarketDataRelay>(() => Locator.locate<MarketDataRelay>(), true);
readonly static ConcurrentDictionary<api.Venue, MarketDataClient> _venue_cache = new ConcurrentDictionary<api.Venue, api.md.MarketDataClient>();
static readonly List<Product> _all_products;
static MarketDataClient()
{
_all_products = _md.Value.request_products(new VoidMessage()).Products.ToList();
}
public static bool create_or_get(api.Venue exchange, out MarketDataClient md)
public static IEnumerable<api.Venue> cached_exchanges => _venue_cache.Keys;
public static bool create_or_get(api.Venue exchange, out MarketDataClient md, user.AuthToken token = null)
{
if (!_venue_cache.TryGetValue(exchange, out md))
{
_venue_cache[exchange] = md = new MarketDataClient(exchange);
_venue_cache[exchange] = md = new MarketDataClient(exchange, token);
return true;
}
return false;
}
public MarketDataClient(bts.api.Venue exchange = Venue.Unset)
public MarketDataClient(bts.api.Venue exchange = Venue.Unset, user.AuthToken token = null)
{
_venue = exchange;
_subscription = new Lazy<MDRequest>(() => load_request(exchange));
_subscription = load_request(exchange, token);
}
MDRequest load_request(bts.api.Venue exchange)
public void set_auth_token(user.AuthToken toke)
{
var req = new MDRequest();
_subscription.Token = toke?.Token ?? string.Empty;
}
MDRequest load_request(bts.api.Venue exchange, user.AuthToken token)
{
var req = new MDRequest() { Token = token?.Token ?? string.Empty } ;
var prods = _all_products.Where(x=>x.Exchange == exchange || exchange == bts.api.Venue.Unset);
foreach(var prod in prods)
req.Channels.Add(new SecurityChannel() { Exchange = prod.Exchange, Type = prod.Type, Symbol = prod.Symbol, Flags = 0 });
return req;
}
static MarketDataRelay locate()
public IEnumerable<SecurityImmutable> get_securities()
{
using (var ctx = new api.core.ServiceContext())
{
return ctx.locate<MarketDataRelay>();
}
return _data.Values.Select(x => new SecurityImmutable(x.data.Security));
}
public Security lookup(security_key key)
public SecurityImmutable? lookup(security_key key)
{
Data value;
if(_data.TryGetValue(key, out value))
@@ -217,7 +221,7 @@ namespace bts.api.md
bool check_subscriptions(uint flags, Func<SecurityChannel, bool> selector)
{
foreach (var sub in _subscription.Value.Channels)
foreach (var sub in _subscription.Channels)
{
if (selector(sub))
{
@@ -234,6 +238,11 @@ namespace bts.api.md
/// <param name="interval"></param>
/// <returns></returns>
public async Task start_pump(TimeSpan? interval = null)
{
await _start_pump(interval);
}
async Task _start_pump(TimeSpan? interval = null, int num_pumps = -1)
{
var rep = await _md.Value.request_names_async(new VoidMessage());
int count = 0;
@@ -249,9 +258,10 @@ namespace bts.api.md
}
}
while (!rep.success && count++ < 5);
while (true)
count = 0;
while (num_pumps < 0 || count++ < num_pumps)
{
var reply = await _md.Value.requestmd_async(_subscription.Value);
var reply = await _md.Value.requestmd_async(_subscription);
if (reply.success)
{
foreach (var channel in reply.value.Channels)
@@ -299,6 +309,19 @@ namespace bts.api.md
await this.start_pump(interval);
}
public void pump_once()
{
if (_pump_task == null)
{
_pump_task = this._start_pump(null, 1)
.ContinueWith(ex =>
{
System.Diagnostics.Debug.Assert(ex == null, ex.ToString());
}
, TaskContinuationOptions.OnlyOnFaulted);
}
}
readonly CancellationTokenSource _cts = new CancellationTokenSource();
public void Dispose()
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using static bts.api.Security;
namespace bts.api {
struct FutureLeg {
public struct FutureLeg {
public readonly Venue exchange;
public readonly string symbol, exchange_desc;
public readonly FutureContractImmutable future;
@@ -4,7 +4,7 @@ using System.Linq;
using static bts.api.Security;
namespace bts.api {
internal static class SecurityHelper {
public static class SecurityHelper {
public static IEnumerable<FutureLeg> get_flat_future_legs(SecurityImmutable sec) {
switch (sec.ContractCase) {
case ContractOneofCase.Spread: return _get_flat_future_legs(sec.Spread.Legs, 1);
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace bts
{
public static class APIExtensions
{
public static T? TryFirst<T>(this IEnumerable<T> src) where T : struct
{
foreach (var v in src)
return v;
return null;
}
}
}
+24 -19
View File
@@ -32,12 +32,15 @@ namespace bts {
Testing,
Latency,
DataFlow,
Plugin
Plugin,
ClientSdk,
Fix
// Please keep this list in sync with logging.h/logging.cpp
}
readonly struct Logger {
public readonly struct Logger {
readonly Subsystem _sys;
public Logger(Subsystem sys) {
internal Logger(Subsystem sys) {
_sys = sys;
}
@@ -61,19 +64,21 @@ namespace bts {
static extern void Logger_log (Level level, Subsystem subsystem, string message, string file, int line);
}
static class Log {
public static Logger Gen => new Logger(Subsystem.gen);
public static Logger Config => new Logger(Subsystem.Config);
public static Logger QBus => new Logger(Subsystem.QBus);
public static Logger Md => new Logger(Subsystem.md);
public static Logger Api => new Logger(Subsystem.Api);
public static Logger Persist => new Logger(Subsystem.Persist);
public static Logger User => new Logger(Subsystem.User);
public static Logger OrderMan => new Logger(Subsystem.OrderMan);
public static Logger Assert => new Logger(Subsystem.Assert);
public static Logger Ui => new Logger(Subsystem.Ui);
public static Logger Testing => new Logger(Subsystem.Testing);
public static Logger DataFlow => new Logger(Subsystem.DataFlow);
public static class Log {
internal static Logger Gen => new Logger(Subsystem.gen);
internal static Logger Config => new Logger(Subsystem.Config);
internal static Logger QBus => new Logger(Subsystem.QBus);
internal static Logger Md => new Logger(Subsystem.md);
internal static Logger Api => new Logger(Subsystem.Api);
internal static Logger Persist => new Logger(Subsystem.Persist);
internal static Logger User => new Logger(Subsystem.User);
internal static Logger OrderMan => new Logger(Subsystem.OrderMan);
internal static Logger Assert => new Logger(Subsystem.Assert);
internal static Logger Ui => new Logger(Subsystem.Ui);
internal static Logger Testing => new Logger(Subsystem.Testing);
internal static Logger DataFlow => new Logger(Subsystem.DataFlow);
public static Logger ClientSdk => new Logger(Subsystem.ClientSdk);
internal static Logger FIX => new Logger(Subsystem.Fix);
[Conditional("DEBUG")]
@@ -99,10 +104,10 @@ namespace bts {
assert(o == null);
}
delegate void on_crash_callback([MarshalAs(UnmanagedType.LPStr)]string msg, [MarshalAs(UnmanagedType.LPStr)] string details);
public static void system_is_crashing(string message, object details) {
internal static void system_is_crashing(string message, object details) {
OnSystemCrashing?.Invoke(message, details);
}
public static event Action<string, object> OnSystemCrashing;
internal static event Action<string, object> OnSystemCrashing;
}
}
}
@@ -4,11 +4,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark
namespace bts.api.core
{
class AdminModelWrapper
public class AdminModelWrapper
{
readonly AdminDataImmutable? _admin_data;
public AdminDataImmutable? admin_data => _admin_data;
readonly string _user_name;
readonly List<BTSAccountImmutable> _allowed_accounts;
readonly Dictionary<string, string> _allowed_account_dict = new Dictionary<string, string>();
@@ -16,26 +17,26 @@ namespace mbts.spark
{
_admin_data = admin_data;
_user_name = user_name ?? string.Empty;
_allowed_accounts = search_admin(u => u.Name == _user_name).Select(x => x.Item1).ToList();
foreach(var val in _allowed_accounts)
_allowed_accounts = search_admin(u => u.Name == _user_name).Where(x =>
{
return x.Item2.HasPermissions || x.Item3.HasPermissions;
}).Select(xx => xx.Item1).ToList();
foreach (var val in _allowed_accounts)
_allowed_account_dict[val.Name] = val.Name;
}
public IEnumerable<Tuple<BTSAccountImmutable, TradingPermissionUnitImmutable>> get_trading_permissions(UInt32 user_id)
{
return search_admin(u => u.Id == user_id);
}
public IEnumerable<BTSAccountImmutable> viewable_accounts()
{
return _allowed_accounts;
}
public bool can_view_account(string account)
{
return _allowed_account_dict.ContainsKey(account) || _user_name.ToLower() == "bts";
return _allowed_account_dict.ContainsKey(account) || _user_name.ToLower().Equals("bts");
}
IEnumerable<Tuple<BTSAccountImmutable, TradingPermissionUnitImmutable>> search_admin(Func<BTSUserImmutable, bool> predicate)
IEnumerable<Tuple<BTSAccountImmutable, TradingPermissionUnit, AdminPermissionUnit>>
search_admin(Func<BTSUserImmutable, bool> predicate)
{
var rv = new List<Tuple<BTSAccountImmutable, TradingPermissionUnitImmutable>>();
var rv = new List<Tuple<BTSAccountImmutable, TradingPermissionUnit, AdminPermissionUnit>>();
if (_admin_data.HasValue)
{
var users = _admin_data.Value.Users.Where(predicate);
@@ -51,9 +52,7 @@ namespace mbts.spark
if (accounts != null && accounts.Count() == 1)
{
var account = accounts.ElementAt(0);
var tp = kv.Value.TradingPermission;
var rv_val = new TradingPermissionUnitImmutable(tp);
rv.Add(Tuple.Create(account, rv_val));
rv.Add(Tuple.Create(account, kv.Value.TradingPermission, kv.Value.AdminPermission));
}
}
}
@@ -4,10 +4,6 @@ using bts.api.core;
using bts.api.orderman;
using bts.api.position;
using bts.spark;
using mbts.collections;
using mbts.spark;
using mbts.spark.utilities;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -18,14 +14,14 @@ using System.Reactive.Linq;
namespace bts.api.core
{
static class PermissionFilters
public static class PermissionFilters
{
public static IObservable<Update<PositionUnitImmutable, int>> PermissionFilter(this IObservable<Update<PositionUnitImmutable, int>> source, AdminModel am)
public static IObservable<Update<PositionUnitImmutable, int>>
PermissionFilter(this IObservable<Update<PositionUnitImmutable, int>> source, IObservable<AdminModelWrapper> am)
{
var pred = Observable.Create<Func<PositionUnitImmutable, bool>>( observer =>
{
return am.on_admin_data.Subscribe(x =>
return am.Subscribe(x =>
{
Func<PositionUnitImmutable, bool> filter = (z => x.can_view_account(z.Account));
observer.OnNext(filter);
@@ -35,13 +31,14 @@ namespace bts.api.core
return source.Filter(pred);
}
public static IObservable<Update<OrderRecordImmutable, uint>> PermissionFilter(this IObservable<Update<OrderRecordImmutable, uint>> source, AdminModel am)
public static IObservable<Update<OrderRecordImmutable, uint>>
PermissionFilter(this IObservable<Update<OrderRecordImmutable, uint>> source, IObservable<AdminModelWrapper> am)
{
var pred = Observable.Create<Func<OrderRecordImmutable, bool>>(observer =>
{
return am.on_admin_data.Subscribe(x =>
return am.Subscribe(x =>
{
Func<OrderRecordImmutable, bool> filter = (z => x.can_view_account(z.account()));
Func<OrderRecordImmutable, bool> filter = (z => x.can_view_account(z.Def.Account ?? string.Empty));
observer.OnNext(filter);
});
});
@@ -49,11 +46,12 @@ namespace bts.api.core
return source.Filter(pred);
}
public static IObservable<Update<FillImmutable, string>> PermissionFilter(this IObservable<Update<FillImmutable, string>> source, AdminModel am)
public static IObservable<Update<FillImmutable, string>>
PermissionFilter(this IObservable<Update<FillImmutable, string>> source, IObservable<AdminModelWrapper> am)
{
var pred = Observable.Create<Func<FillImmutable, bool>>(observer =>
{
return am.on_admin_data.Subscribe(x =>
return am.Subscribe(x =>
{
Func<FillImmutable, bool> filter = (z => x.can_view_account(z.Account));
observer.OnNext(filter);
@@ -69,12 +69,5 @@ namespace mbts {
}
}
}
public static T? TryFirst<T>(this IEnumerable<T> src) where T : struct
{
foreach(var v in src)
return v;
return null;
}
}
}
+16
View File
@@ -69,5 +69,21 @@ namespace bts.excel
Utilities.bts_load_assembly(ass);
}
}
public static void calculate_full_rebuild()
{
object app = ExcelDnaUtil.Application;
var mi = app?.GetType().GetMethod("CalculateFullRebuild", BindingFlags.Instance | BindingFlags.Public);
if(mi != null)
mi.Invoke(app, null);
}
public static void calculate_full()
{
object app = ExcelDnaUtil.Application;
var mi = app?.GetType().GetMethod("CalculateFull", BindingFlags.Instance | BindingFlags.Public);
if (mi != null)
mi.Invoke(app, null);
}
}
}
@@ -38,7 +38,7 @@
<!--
Enable/Disable packing of .dna files
-->
<RunExcelDnaPack Condition="'$(RunExcelDnaPack)' == ''">false</RunExcelDnaPack>
<RunExcelDnaPack Condition="'$(RunExcelDnaPack)' == ''">true</RunExcelDnaPack>
<!--
Suffix used for packed .xll files e.g. MyAddIn-packed.xll
@@ -1,7 +1,10 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using ExcelDna.Integration;
using ExcelDna.Integration.Rtd;
using ExcelDna.Registration.Utils;
using bts.api.admin;
using bts.excel;
@@ -14,9 +17,30 @@ namespace bts.api.orderman
{
public partial class OrderService_Excel
{
readonly static Lazy<user.UserService> _users = new Lazy<user.UserService>(() => locate<user.UserService>(), true);
readonly static Lazy<admin.AdminService> _admin = new Lazy<admin.AdminService>(() => locate<admin.AdminService>(), true);
readonly static Lazy<user.UserService> _users = new Lazy<user.UserService>(() => Locator.locate<user.UserService>(), true);
readonly static Lazy<admin.AdminService> _admin = new Lazy<admin.AdminService>(() => Locator.locate<admin.AdminService>(), true);
[ExcelFunction(Category = "BTS", Description = "Get All Accessible Accounts")]
async public static void spark_get_accounts(string login_token, ExcelAsyncHandle handle)
{
try
{
List<string> accts = new List<string>();
var admin = await _admin.Value.get_filtered_admin_data_async(new FilteredAdminDataRequest { Token = login_token });
if (admin.success)
{
foreach (var acc in admin.value.Accounts)
if(acc.IsPresent)
accts.Add(acc.Name);
}
var res = Utilities.make_column_vector(accts.ToArray());
handle.SetResult(res);
}
catch (Exception ex)
{
handle.SetException(ex);
}
}
[ExcelFunction(Category = "BTS", Description = "Cancel an Order by Order ID", IsMacroType = true)]
public static object spark_orderservice_cancel_order(long order_id)
@@ -6,6 +6,7 @@ using System.Reactive.Linq;
using Google.Protobuf;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ExcelDna.Integration;
namespace bts.excel.rtd
{
@@ -17,7 +18,7 @@ namespace bts.excel.rtd
_data = data;
}
#region parser
#region parser
static IDictionary<string, object> parse_dictionary(object[,] range)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
@@ -3,10 +3,10 @@ using System;
namespace bts.excel.rtd
{
class TopMDFormatter : IExcelFormatter
class TopOfBookMDFormatter : IExcelFormatter
{
readonly co2.ChannelUpdate _update;
public TopMDFormatter(co2.ChannelUpdate upd)
public TopOfBookMDFormatter(co2.ChannelUpdate upd)
{
_update = upd;
}
@@ -0,0 +1,90 @@
using System;
namespace bts.excel.rtd
{
class TopicMDFormatter : IExcelFormatter
{
readonly co2.ChannelUpdate _update;
readonly string _topic;
public TopicMDFormatter(co2.ChannelUpdate upd, string topic)
{
_update = upd;
_topic = topic;
}
public object[,] format(bool include_headers = false)
{
var retval = new object[1, 1];
switch (_topic.ToLower())
{
case "bid":
case "bidprice":
retval[0, 0] = BookMDFormatter.format_md(_update?.Direct?.Bids?[0]?.Mantissa ?? 0, _update?.Direct?.Bids?[0]?.Exponent ?? 0);
break;
case "ask":
case "askprice":
retval[0, 0] = BookMDFormatter.format_md(_update?.Direct?.Asks?[0]?.Mantissa ?? 0, _update?.Direct?.Asks?[0]?.Exponent ?? 0);
break;
case "bidsize":
case "bidqty":
retval[0, 0] = _update?.Direct?.Bids?[0]?.Size;
break;
case "asksize":
case "askqty":
retval[0, 0] = _update?.Direct?.Asks?[0]?.Size;
break;
case "impliedbid":
case "impliedbidprice":
retval[0, 0] = BookMDFormatter.format_md(_update?.Implied?.Bids?[0]?.Mantissa ?? 0, _update?.Implied?.Bids?[0]?.Exponent ?? 0);
break;
case "impliedask":
case "impliedaskprice":
retval[0, 0] = BookMDFormatter.format_md(_update?.Implied?.Asks?[0]?.Mantissa ?? 0, _update?.Implied?.Asks?[0]?.Exponent ?? 0);
break;
case "impliedbidsize":
case "impliedbidqty":
retval[0, 0] = _update?.Implied?.Bids?[0]?.Size;
break;
case "impliedasksize":
case "impliedaskqty":
retval[0, 0] = _update?.Implied?.Asks?[0]?.Size;
break;
case "settlement":
case "settlementprice":
retval[0, 0] = BookMDFormatter.format_md(_update?.Misc?.Settlement?.Mantissa ?? 0, _update?.Misc?.Settlement?.Exponent ?? 0);
break;
case "previoussettlement":
case "previoussettlementprice":
retval[0, 0] = BookMDFormatter.format_md(_update?.Misc?.PreviousSettlementPrice?.Mantissa ?? 0, _update?.Misc?.PreviousSettlementPrice?.Exponent ?? 0);
break;
case "open":
case "openprice":
retval[0, 0] = _update?.Misc?.OpenPrice;
break;
case "close":
case "closeprice":
retval[0, 0] = _update?.Misc?.ClosePrice;
break;
case "high":
case "highprice":
retval[0, 0] = _update?.Misc?.SessionHigh;
break;
case "low":
case "lowprice":
retval[0, 0] = _update?.Misc?.SessionLow;
break;
case "last":
case "lastprice":
retval[0, 0] = BookMDFormatter.format_md(_update?.LastTrade?.Mantissa ?? 0, _update?.LastTrade?.Exponent ?? 0);
break;
case "lastsize":
case "lastqty":
retval[0, 0] = _update?.LastTrade?.Size;
break;
}
return retval;
}
}
}
@@ -4,16 +4,36 @@ using System.Linq;
using ExcelDna.Integration;
using ExcelDna.Integration.Rtd;
using ExcelDna.Registration.Utils;
using System.Reactive.Linq;
using bts.api;
using bts.api.md;
using bts.api.orderman;
using ExcelDna.Registration.Utils;
namespace bts.excel.rtd
{
public class MDSubscribe
{
static Authenticator _auth = new Authenticator();
[ExcelFunction(Category = "BTS", Description = "Log in to BTS Spark")]
async public static void spark_login(string login, string password, ExcelAsyncHandle handle)
{
try
{
var toke = await _auth.login(login, password);
foreach (var ven in MarketDataClient.cached_exchanges)
{
MarketDataClient.create_or_get(ven, out MarketDataClient md);
md?.set_auth_token(toke);
}
handle.SetResult(toke?.Token ?? "Could not log in check user/pass");
}
catch(Exception ex)
{
handle.SetException(ex);
}
}
public static MarketDataClient market_data(api.Venue exchange)
{
var newc = MarketDataClient.create_or_get(exchange, out MarketDataClient md);
@@ -83,18 +103,18 @@ namespace bts.excel.rtd
[ExcelFunction(Category = "BTS", Description = "Subscribe to BTS Spark top of book market data")]
public static object spark_md_top(
public static object spark_md_top_of_book(
[ExcelArgument("Required. The specific contract, GCZ8 or ZNU8-ZNZ8, etc")] string contract,
[ExcelArgument("Required. The symbol, GC or ZN, etc")] string symbol,
[ExcelArgument("Optional. The type, Future or Spread, default = Future")] string security_type = null,
[ExcelArgument("Optional. The exchange, default = GLBX")] string exchange = null)
{
var param = new object[] { contract, symbol, security_type, exchange };
return ObservableRtdUtil.Observe(nameof(spark_md_top), param,
return ObservableRtdUtil.Observe(nameof(spark_md_top_of_book), param,
() => md_observable(contract, symbol, security_type, exchange).Select(x =>
{
if (x is co2.ChannelUpdate upd)
return new TopMDFormatter(upd).format();
return new TopOfBookMDFormatter(upd).format();
return x;
}));
}
@@ -116,5 +136,23 @@ namespace bts.excel.rtd
}));
}
[ExcelFunction(Category = "BTS", Description = "Subscribe to BTS Spark top of book market data")]
public static object spark_md(
[ExcelArgument("Required. The 'Topic' string, 'bid', 'ask', etc")] string topic,
[ExcelArgument("Required. The specific contract, GCZ8 or ZNU8-ZNZ8, etc")] string contract,
[ExcelArgument("Required. The symbol, GC or ZN, etc")] string symbol,
[ExcelArgument("Optional. The type, Future or Spread, default = Future")] string security_type = null,
[ExcelArgument("Optional. The exchange, default = GLBX")] string exchange = null)
{
var param = new object[] { topic, contract, symbol, security_type, exchange };
return ObservableRtdUtil.Observe(nameof(spark_md), param,
() => md_observable(contract, symbol, security_type, exchange).Select(x =>
{
if (x is co2.ChannelUpdate upd)
return new TopicMDFormatter(upd, topic).format();
return x;
}));
}
}
}
+7 -2
View File
@@ -2,8 +2,13 @@
<ExternalLibrary Path="spark_excel.dll" ExplicitExports="false" LoadFromBytes="true" Pack="true" />
<Reference Path="ExcelDna.IntelliSense.dll" Pack="true" />
<Reference Path="nunit.framework.dll" Pack="true" ExplicitExports="true" />
<Reference Path="ExcelDna.Registration.dll" Pack="true" />
<Reference Path="ExcelDna.Integration.dll" Pack="true" />
<Reference Path="Google.Protobuf.dll" Pack="true" ExplicitExports="true" />
<Reference Path="System.Linq.Dynamic.dll" Pack="true" ExplicitExports="true" />
<Reference Path="Newtonsoft.Json.dll" Pack="true" ExplicitExports="true" />
<Reference Path="nunit.framework.dll" Pack="true" ExplicitExports="true" />
<Reference Path="System.Reactive.Core.dll" Pack="true" ExplicitExports="true" />
<Reference Path="System.Reactive.Interfaces.dll" Pack="true" ExplicitExports="true" />
<Reference Path="System.Reactive.Providers.dll" Pack="true" ExplicitExports="true" />
+1 -1
View File
@@ -62,7 +62,7 @@
</ItemGroup>
<ItemGroup>
<None Include="spark_excel_example.xlsm"><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></None>
<None Include="spark_excel_example_2018.xlsm"><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></None>
</ItemGroup>
<!-- <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
Binary file not shown.
Binary file not shown.
+16
View File
@@ -5,12 +5,28 @@ using System.Reflection;
using System.Text.RegularExpressions;
using ExcelDna.Integration;
using bts.api;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace bts.excel
{
public class Utilities
{
[ExcelFunction]
public static object bts_json_lookup(string json, string key1, string key2 = null, string key3 = null)
{
var jo = JObject.Parse(json);
JToken val = json;
if (!string.IsNullOrEmpty(key1) && !string.IsNullOrEmpty(key2) && !string.IsNullOrEmpty(key3))
val = jo[key1][key2][key3];
else if (!string.IsNullOrEmpty(key1) && !string.IsNullOrEmpty(key2))
val = jo[key1][key2];
else if (!string.IsNullOrEmpty(key1))
val = jo[key1];
return val.ToString();
}
public static bool parse_venue(string exch, out Venue ven)
{
if (!Enum.TryParse(exch.ToUpper(), true, out ven))
+55
View File
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QuickFix;
using QuickFix.Fields;
namespace bts.fix
{
public class FixInit
{
public static int start_acceptor(string settings_file, bool orders_allowed, string http_address = null)
{
try
{
SessionSettings settings = new SessionSettings(settings_file);
IApplication app = new FixRouter( (th, sess) =>
{
if(orders_allowed)
return new OrderSession(th, sess);
else
return new DropCopySession(th, sess);
});
IMessageStoreFactory storeFactory = new FileStoreFactory(settings);
ILogFactory logFactory = new FileLogFactory(settings);
IAcceptor acceptor = new ThreadedSocketAcceptor(app, storeFactory, settings, logFactory);
acceptor.Start();
HttpServer srv = null;
if (!string.IsNullOrEmpty(http_address))
{
if(!http_address.StartsWith("http"))
http_address = "http://127.0.0.1:5080/";
srv = new HttpServer(http_address, settings);
srv.Start();
}
Console.WriteLine("press <q> to quit");
while (Console.Read() != 'q') { }
acceptor.Stop();
if(srv != null)
srv.Stop();
return 0;
}
catch (System.Exception e)
{
bts.Log.FIX.fatal(e.ToString());
Console.WriteLine("==FATAL ERROR==");
Console.WriteLine(e.ToString());
return -1;
}
}
}
}
+106
View File
@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Text;
using QuickFix;
using QuickFix.Fields;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.user;
using bts.api.md;
using bts.api.admin;
using security_key = System.UInt32;
namespace bts.fix
{
interface IFixRouter
{
IEnumerable<SecurityImmutable> get_securities();
SecurityImmutable? lookup(security_key key);
SecurityImmutable? lookup(string desc);
}
class FixRouter : QuickFix.MessageCracker, QuickFix.IApplication, IFixRouter
{
readonly ConcurrentDictionary<SessionID, QuickFix.IApplication> _sessions = new ConcurrentDictionary<SessionID, IApplication>();
readonly Func<IFixRouter, SessionID, IApplication> _factory;
readonly MarketDataClient _mdglobal = new MarketDataClient();
public FixRouter(Func<IFixRouter, SessionID, IApplication> factory)
{
_factory = factory;
_mdglobal.pump_once(); //get securities
}
public IApplication session(SessionID sessionID)
{
IApplication app;
if (!_sessions.TryGetValue(sessionID, out app))
{
app = _factory(this, sessionID);
_sessions[sessionID] = app;
}
return app;
}
public void FromAdmin(Message message, SessionID sessionID)
{
session(sessionID).FromAdmin(message, sessionID);
}
public void FromApp(Message message, SessionID sessionID)
{
session(sessionID).FromApp(message, sessionID);
}
public void OnCreate(SessionID sessionID)
{
session(sessionID).OnCreate(sessionID);
}
public void OnLogon(SessionID sessionID)
{
session(sessionID).OnLogon(sessionID);
}
public void OnLogout(SessionID sessionID)
{
session(sessionID).OnLogout(sessionID);
}
public void ToAdmin(Message message, SessionID sessionID)
{
session(sessionID).ToAdmin(message, sessionID);
}
public void ToApp(Message message, SessionID sessionID)
{
session(sessionID).ToApp(message, sessionID);
}
IEnumerable<SecurityImmutable> IFixRouter.get_securities()
{
return _mdglobal.get_securities();
}
SecurityImmutable? IFixRouter.lookup(uint key)
{
return _mdglobal.lookup(key);
}
SecurityImmutable? IFixRouter.lookup(string desc)
{
if (_mdglobal.lookup_security_key_from_desc(desc, out uint key))
return _mdglobal.lookup(key);
else
return null;
}
}
}
+585
View File
@@ -0,0 +1,585 @@
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Web;
using QuickFix;
namespace bts.fix
{
public class HttpServer : IDisposable
{
private readonly HttpListener _httpListener;
private Thread _connectionThread;
private Boolean _running, _disposed;
private readonly SessionSettings _sessionSettings;
private StringBuilder _sbHtmlHeader;
public HttpServer(string prefix, SessionSettings settings)
{
if (!HttpListener.IsSupported)
{
// Requires at least a Windows XP with Service Pack 2
throw new NotSupportedException(
"The Http Server cannot run on this operating system.");
}
_httpListener = new HttpListener();
_httpListener.Prefixes.Add(prefix);
_sessionSettings = settings;
}
public void Start()
{
if (!_httpListener.IsListening)
{
_httpListener.Start();
_running = true;
// Use a thread to listen to the Http requests
_connectionThread = new Thread(ConnectionThreadStart);
_connectionThread.Start();
}
}
public void Stop()
{
if (_httpListener.IsListening)
{
_running = false;
_httpListener.Stop();
}
}
private void ConnectionThreadStart()
{
try
{
while (_running)
{
// Grab the context and pass it to the processor methods to handle it
HttpListenerContext context = _httpListener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
string responseString;
_sbHtmlHeader = new StringBuilder(@"<HEAD><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>");
_sbHtmlHeader.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>]&nbsp;[<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEADER><BODY>", request.Url.OriginalString);
switch (request.Url.AbsolutePath)
{
case "/":
responseString = ProcessRoot(request);
break;
case "/session":
responseString = SessionDetails(request);
break;
case "/resetSession":
responseString = ResetSession(request);
break;
case "/resetSessions":
responseString = ResetSessions(request);
break;
case "/refreshSession":
responseString = RefreshSession(request);
break;
case "/refreshSessions":
responseString = RefreshSessions(request);
break;
case "/enableSessions":
responseString = EnableSessions(request);
break;
case "/disableSessions":
responseString = DisableSessions(request);
break;
default:
responseString = ProcessRoot(request);
break;
}
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
}
}
catch (HttpListenerException)
{
Console.WriteLine("HTTP server was shut down.");
}
}
private string DisableSessions(HttpListenerRequest request)
{
bool confirm = false;
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sessionDetails.Logout();
}
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been disabled</h2></CENTER>", "/");
}
else
{
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to disable all sessions ?</h2></CENTER>");
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, disable sessions</A>]&nbsp;[<A HREF='/'>NO, do not disable sessions</A>]</CENTER>", request.Url.OriginalString);
}
return sbHtmlPageBody.ToString();
}
private string EnableSessions(HttpListenerRequest request)
{
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sessionDetails.Logon();
}
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been enabled</h2></CENTER>", "/");
}
else
{
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to enable all sessions ?</h2></CENTER>");
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, enable sessions</A>]&nbsp;[<A HREF='/'>NO, do not enable sessions</A>]</CENTER>", urlOriginalString);
}
return sbHtmlPageBody.ToString();
}
private string RefreshSession(HttpListenerRequest request)
{
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
Session sessionDetails = Session.LookupSession(sessionId);
if (sessionDetails == null) throw new Exception("Session not found");
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
string url = "/session?" + GetParameterList(urlOriginalString);
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
sessionDetails.Refresh();
url = RemoveQueryStringByKey(urlOriginalString, "confirm");
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL=/session?{0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", GetParameterList(urlOriginalString));
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>]&nbsp;[<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEAD><BODY>", urlOriginalString);
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='/session?{0}'>{1}</A> has been refreshed</h2></CENTER>", GetParameterList(url), sessionId);
}
else
{
sbHtmlPageBody.AppendFormat("<CENTER><h2>Are you sure you want to refresh session <A HREF='{0}'>{1}</A>?</h2></CENTER>", url, sessionId);
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}&confirm=1'>YES, refresh session</A>]&nbsp;[<A HREF='{0}'>NO, do not refresh session</A>]</CENTER>", url);
}
return sbHtmlPageBody.ToString();
}
private string RefreshSessions(HttpListenerRequest request)
{
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sessionDetails.Refresh();
}
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been refreshed</h2></CENTER>", "/");
}
else
{
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to refresh all sessions ?</h2></CENTER>");
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, refresh sessions</A>]&nbsp;[<A HREF='/'>NO, do not refresh sessions</A>]</CENTER>", urlOriginalString);
}
return sbHtmlPageBody.ToString();
}
private string ResetSessions(HttpListenerRequest request)
{
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sessionDetails.Reset("Reset from WebInterface");
}
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been reset</h2></CENTER>", "/");
}
else
{
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to reset all sessions ?</h2></CENTER>");
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, reset sessions</A>]&nbsp;[<A HREF='/'>NO, do not reset sessions</A>]</CENTER>", urlOriginalString);
}
return sbHtmlPageBody.ToString();
}
private string ResetSession(HttpListenerRequest request)
{
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
Session sessionDetails = Session.LookupSession(sessionId);
if (sessionDetails == null) throw new Exception("Session not found");
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
string url = "/session?" + GetParameterList(urlOriginalString);
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
sessionDetails.Reset("Reset from WebInterface");
url = RemoveQueryStringByKey(urlOriginalString, "confirm");
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL=/session?{0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", GetParameterList(urlOriginalString));
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>]&nbsp;[<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEAD><BODY>", urlOriginalString);
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='/session?{0}'>{1}</A> has been reset</h2></CENTER>", GetParameterList(url), sessionId);
}
else
{
sbHtmlPageBody.AppendFormat("<CENTER><h2>Are you sure you want to reset session <A HREF='{0}'>{1}</A>?</h2></CENTER>", url, sessionId);
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}&confirm=1'>YES, reset session</A>]&nbsp;[<A HREF='{0}'>NO, do not reset session</A>]</CENTER>", url);
}
return sbHtmlPageBody.ToString();
}
private string ProcessRoot(HttpListenerRequest request)
{
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
//Session count
sbHtmlPageBody.AppendFormat("<CENTER><b><i>{0} Sessions managed by QuickFIX</b></i></CENTER><HR/>", _sessionSettings.GetSessions().Count);
//session management links
sbHtmlPageBody.AppendFormat(@"<CENTER><A HREF='/resetSessions{0}'>RESET</A>&nbsp;<A HREF='/refreshSessions'>REFRESH</A>&nbsp;<A HREF='/enableSessions'>ENABLE</A>&nbsp;<A HREF='/disableSessions'>DISABLE</A></CENTER><HR/></HEADER><BODY>", GetParameterList(request.Url.OriginalString));
//Start the table generation
sbHtmlPageBody.Append("<table id=\"sessionlist\" style=\"border-width:1; padding:2; width:100%\">");
sbHtmlPageBody.Append("<tr>");
sbHtmlPageBody.Append(AddCell("Session", true));
sbHtmlPageBody.Append(AddCell("Type", true));
sbHtmlPageBody.Append(AddCell("Enabled", true));
sbHtmlPageBody.Append(AddCell("Session Time", true));
sbHtmlPageBody.Append(AddCell("Logged On", true));
sbHtmlPageBody.Append(AddCell("Next Incoming", true));
sbHtmlPageBody.Append(AddCell("Next Outgoing", true));
sbHtmlPageBody.Append("</tr>");
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sbHtmlPageBody.Append("<tr>");
sbHtmlPageBody.Append(AddCell(String.Format(
"<a href=\"session?BeginString={0}&SenderCompID={1}&TargetCompID={2}\">{0}:{1}->{2}</a>",
session.BeginString, session.SenderCompID, session.TargetCompID)));
sbHtmlPageBody.Append(AddCell(sessionDetails.IsInitiator ? "initiator" : "acceptor"));
sbHtmlPageBody.Append(AddCell(sessionDetails.IsEnabled ? "yes" : "no"));
sbHtmlPageBody.Append(AddCell(sessionDetails.IsSessionTime ? "yes" : "no"));
sbHtmlPageBody.Append(AddCell(sessionDetails.IsLoggedOn ? "yes" : "no"));
sbHtmlPageBody.Append(AddCell(sessionDetails.NextTargetMsgSeqNum.ToString()));
sbHtmlPageBody.Append(AddCell(sessionDetails.NextSenderMsgSeqNum.ToString()));
sbHtmlPageBody.Append("</tr>");
}
sbHtmlPageBody.Append("</table>");
return sbHtmlPageBody.ToString();
}
public string SessionDetails(HttpListenerRequest request)
{
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
Session sessionDetails = Session.LookupSession(sessionId);
if (sessionDetails == null) throw new Exception("Session not found");
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
string url = request.Url.OriginalString;
string urlOriginalString = request.Url.OriginalString;
if (request.QueryString["enabled"] != null)
{
if (!Convert.ToBoolean(request.QueryString["enabled"]))
sessionDetails.Logout();
else
sessionDetails.Logon();
url = RemoveQueryStringByKey(urlOriginalString, "Enabled");
}
if (request.QueryString["next incoming"] != null)
{
int value = Convert.ToInt16(request.QueryString["next incoming"]);
sessionDetails.NextTargetMsgSeqNum = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "next incoming");
}
if (request.QueryString["Next Outgoing"] != null)
{
int value = Convert.ToInt16(request.QueryString["Next Outgoing"]);
sessionDetails.NextSenderMsgSeqNum = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "Next Outgoing");
}
if (request.QueryString["SendRedundantResendRequests"] != null)
{
sessionDetails.SendRedundantResendRequests = Convert.ToBoolean(request.QueryString["SendRedundantResendRequests"]);
url = RemoveQueryStringByKey(urlOriginalString, "SendRedundantResendRequests");
}
if (request.QueryString["CheckCompId"] != null)
{
sessionDetails.CheckCompID = Convert.ToBoolean(request.QueryString["CheckCompId"]);
url = RemoveQueryStringByKey(urlOriginalString, "CheckCompId");
}
if (request.QueryString["CheckLatency"] != null)
{
sessionDetails.CheckLatency = Convert.ToBoolean(request.QueryString["CheckLatency"]);
url = RemoveQueryStringByKey(urlOriginalString, "CheckLatency");
}
if (request.QueryString["MaxLatency"] != null)
{
int value = Convert.ToInt16(request.QueryString["MaxLatency"]);
sessionDetails.MaxLatency = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "MaxLatency");
}
if (request.QueryString["LogonTimeout"] != null)
{
int value = Convert.ToInt16(request.QueryString["LogonTimeout"]);
sessionDetails.LogonTimeout = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "LogonTimeout");
}
if (request.QueryString["LogoutTimeout"] != null)
{
int value = Convert.ToInt16(request.QueryString["LogoutTimeout"]);
sessionDetails.LogoutTimeout = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "LogoutTimeout");
}
if (request.QueryString["ResetOnLogon"] != null)
{
sessionDetails.ResetOnLogon = Convert.ToBoolean(request.QueryString["ResetOnLogon"]);
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnLogon");
}
if (request.QueryString["ResetOnLogout"] != null)
{
sessionDetails.ResetOnLogout = Convert.ToBoolean(request.QueryString["ResetOnLogout"]);
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnLogout");
}
if (request.QueryString["ResetOnDisconnect"] != null)
{
sessionDetails.ResetOnDisconnect = Convert.ToBoolean(request.QueryString["ResetOnDisconnect"]);
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnDisconnect");
}
if (request.QueryString["RefreshOnLogon"] != null)
{
sessionDetails.RefreshOnLogon = Convert.ToBoolean(request.QueryString["RefreshOnLogon"]);
url = RemoveQueryStringByKey(urlOriginalString, "RefreshOnLogon");
}
if (request.QueryString["MillisecondsInTimestamp"] != null)
{
sessionDetails.MillisecondsInTimeStamp = Convert.ToBoolean(request.QueryString["MillisecondsInTimestamp"]);
url = RemoveQueryStringByKey(urlOriginalString, "MillisecondsInTimestamp");
}
if (request.QueryString["PersistMessages"] != null)
{
sessionDetails.PersistMessages = Convert.ToBoolean(request.QueryString["PersistMessages"]);
url = RemoveQueryStringByKey(urlOriginalString, "PersistMessages");
}
sbHtmlPageBody.AppendFormat(@"<CENTER>{0}</CENTER><HR/>", sessionId);
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/resetSession?{0}'>RESET</A>]&nbsp;[<A HREF='/refreshSession?{0}'>REFRESH</A>]</CENTER><HR/></HEADER><BODY>", GetParameterList(urlOriginalString));
sbHtmlPageBody.Append("<table id=\"session_details\" style=\"border-width:1; padding:2; width:100%\">");
sbHtmlPageBody.Append(AddRow("Enabled", sessionDetails.IsEnabled, url));
sbHtmlPageBody.Append(AddRow("ConnectionType", sessionDetails.IsInitiator ? "initiator" : "acceptor"));
sbHtmlPageBody.Append(AddRow("SessionTime", sessionDetails.IsSessionTime));
sbHtmlPageBody.Append(AddRow("LoggedOn", sessionDetails.IsLoggedOn));
sbHtmlPageBody.Append(AddRow("Next Incoming", sessionDetails.NextTargetMsgSeqNum, url));
sbHtmlPageBody.Append(AddRow("Next Outgoing", sessionDetails.NextSenderMsgSeqNum, url));
sbHtmlPageBody.Append(AddRow("SendRedundantResendRequests", sessionDetails.SendRedundantResendRequests, url));
sbHtmlPageBody.Append(AddRow("CheckCompId", sessionDetails.CheckCompID, url));
sbHtmlPageBody.Append(AddRow("CheckLatency", sessionDetails.CheckLatency, url));
sbHtmlPageBody.Append(AddRow("MaxLatency", sessionDetails.MaxLatency, url));
sbHtmlPageBody.Append(AddRow("LogonTimeout", sessionDetails.LogonTimeout, url));
sbHtmlPageBody.Append(AddRow("LogoutTimeout", sessionDetails.LogoutTimeout, url));
sbHtmlPageBody.Append(AddRow("ResetOnLogon", sessionDetails.ResetOnLogon, url));
sbHtmlPageBody.Append(AddRow("ResetOnLogout", sessionDetails.ResetOnLogout, url));
sbHtmlPageBody.Append(AddRow("ResetOnDisconnect", sessionDetails.ResetOnDisconnect, url));
sbHtmlPageBody.Append(AddRow("RefreshOnLogon", sessionDetails.RefreshOnLogon, url));
sbHtmlPageBody.Append(AddRow("MillisecondsInTimestamp", sessionDetails.MillisecondsInTimeStamp, url));
sbHtmlPageBody.Append(AddRow("PersistMessages", sessionDetails.PersistMessages, url));
sbHtmlPageBody.Append("</table>");
return sbHtmlPageBody.ToString();
}
public static string RemoveQueryStringByKey(string url, string key)
{
var uri = new Uri(url);
// this gets all the query string key value pairs as a collection
var newQueryString = HttpUtility.ParseQueryString(uri.Query);
// this removes the key if exists
newQueryString.Remove(key);
// this gets the page path from root without QueryString
string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path);
return newQueryString.Count > 0
? $"{pagePathWithoutQueryString}?{newQueryString}"
: pagePathWithoutQueryString;
}
public static string GetParameterList(string url)
{
return HttpUtility.ParseQueryString((new Uri(url).Query)).ToString();
}
private static string AddRow(string colName, bool value, string url = "")
{
string valueAsStr = value ? "yes" : "no";
string innerHtml = url.Length > 0
? $"<a href=\" {url}&{colName}={!value} \">toggle</a>"
: "";
return AddRow(colName, valueAsStr, innerHtml);
}
private static string AddRow(string colName, int value, string url = "")
{
string innerHtml = $"<a href=\" {url}&{colName}={value - 10} \"> << </a>" +
$"<a href=\" {url}&{colName}={value - 1} \"> < </a>" +
" | " +
$"<a href=\" {url}&{colName}={value + 1} \"> > </a>" +
$"<a href=\" {url}&{colName}={value + 10} \"> >> </a>";
return AddRow(colName, value.ToString(), innerHtml);
}
private static string AddRow(string colName, string value, string innerHtml = "")
{
StringBuilder row = new StringBuilder();
row.Append("<tr>");
row.Append(AddCell(colName));
row.Append(AddCell(value));
row.Append(AddCell(innerHtml));
row.Append("</tr>");
return row.ToString();
}
private static string AddCell(string cellContent, bool header = false)
{
string entryType = header ? "th" : "td";
return "<" + entryType + " align=\"left\">" + cellContent + "</" + entryType + ">";
}
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
if (_running)
{
Stop();
}
if (_connectionThread != null)
{
_connectionThread.Abort();
_connectionThread = null;
}
}
_disposed = true;
}
}
}
@@ -0,0 +1,2 @@
using System.Runtime.CompilerServices;
+2
View File
@@ -0,0 +1,2 @@
# BTS Spark FIX Adapter
@@ -0,0 +1,281 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Text;
using QuickFix;
using QuickFix.Fields;
using bts.api.core;
using bts.api.orderman;
using bts.api.user;
using bts.api.admin;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
namespace bts.fix
{
class DropCopySession : SessionBase, QuickFix.IApplication
{
protected IDisposable _fillsdisp;
protected IDisposable _ordersdisp;
protected readonly ConcurrentDictionary<uint, OrderRecordImmutable> _order_cache = new ConcurrentDictionary<uint, OrderRecordImmutable>();
protected readonly ConcurrentDictionary<uint, string> _fix_clorderids = new ConcurrentDictionary<uint, string>();
public DropCopySession(IFixRouter router, SessionID sess) : base(router, sess)
{
}
override public void OnLogon(SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
var msg = sessionID.ToString() + " Logged on";
log_info(msg);
subscribe_fills_and_orders(sessionID);
}
override public void OnLogout(SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
var msg = sessionID.ToString() + " Logged out";
log_info(msg);
if (_ordersdisp != null)
_ordersdisp.Dispose();
if (_fillsdisp != null)
_fillsdisp.Dispose();
}
override public void FromApp(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
MsgType type = Message.IdentifyType(message.ToString());
switch (type.getValue())
{
case MsgType.ORDER_STATUS_REQUEST:
case MsgType.SECURITYDEFINITIONREQUEST:
log_info("IN: " + message);
Crack(message, sessionID);
break;
default:
log_info("UNSUPPORTED MESSAGE IN: " + type.getValue());
throw new UnsupportedMessageType();
}
}
virtual public void OnMessage(QuickFix.FIX44.OrderStatusRequest req, SessionID s)
{
if (req.IsSetOrderID() && req.IsSetOrdStatusReqID())
{
uint oid = uint.Parse(req.OrderID.getValue());
if (_order_cache.TryGetValue(oid, out OrderRecordImmutable order))
{
var ostat = build_order_status(s, order);
ostat.OrdStatusReqID = new OrdStatusReqID(req.OrdStatusReqID.getValue());
send(ostat, s);
}
else
send_reject(s, "Can't find order " + oid, req);
}
else
send_reject(s, "clorderid null", req);
}
virtual public void OnMessage(QuickFix.FIX44.SecurityDefinitionRequest req, SessionID s)
{
if (req.IsSetSecurityReqID() && req.IsSetSecurityRequestType() &&
req.SecurityRequestType.getValue() == SecurityRequestType.REQUEST_LIST_SECURITIES)
{
var secs = _router.get_securities();
if (req.IsSetSymbol())
secs = secs.Where(x => x.Symbol.ToLower() == req.Symbol.getValue().ToLower());
if (req.IsSetSecurityType())
secs = secs.Where(x => security_types_equal(x.security_type(), req.SecurityType));
if (req.IsSetSecurityExchange())
secs = secs.Where(x => exchanges_equal(x.Exchange, req.SecurityExchange));
var def = new QuickFix.FIX44.SecurityDefinition(req.SecurityReqID,
new SecurityResponseID(Guid.NewGuid().ToString()),
new SecurityResponseType(SecurityResponseType.LIST_OF_SECURITIES_RETURNED_PER_REQUEST));
send(def, s);
}
else
send_reject(s, "Can't parse secdef request", req);
}
virtual protected void subscribe_fills_and_orders(SessionID sessionID)
{
var scheduler = TaskPoolScheduler.Default;
var interval = TimeSpan.FromMilliseconds(500);
var on_orders = _orderservice.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(_adminobservable).Table().Cache();
var on_fills = _orderservice.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(_adminobservable).Table().Cache();
_ordersdisp = on_orders.Subscribe(o =>
{
foreach (var order in o.added)
{
var ostat = build_order_status(sessionID, order);
_order_cache[order.Key] = order;
send(ostat, sessionID);
}
foreach (var order in o.changed)
{
var ostat = build_order_status(sessionID, order, true);
send(ostat, sessionID);
}
});
_fillsdisp = on_fills.Subscribe(f =>
{
foreach (var fill in f.added)
{
var fi = build_fill(sessionID, fill);
send(fi, sessionID);
}
foreach (var fill in f.changed)
{
var fi = build_fill(sessionID, fill);
send(fi, sessionID);
}
});
}
virtual protected void process_order_state(in OrderStateImmutable state, ref uint filled, ref uint leaves, ref double avgprx,
ref QuickFix.Fields.OrdStatus fstatus, ref NativeOrderStateImmutable? native)
{
filled = state.FilledQuantity;
leaves = state.Quantity - state.FilledQuantity;
avgprx = state.AvgPrice;
fstatus = translate_status(state, ref native);
}
protected bts.api.SecurityImmutable? resolve_execution_report_security(bts.api.SecurityImmutable? sec, QuickFix.FIX44.ExecutionReport report)
{
report.SecurityExchange = convert_exchange(sec.Value.Exchange);
var sectype = sec.Value.security_type();
report.SecurityType = convert_security_type(sectype);
if (sec.Value.HasGlbx)
report.SecurityDesc = new SecurityDesc(sec.Value.Glbx.SecurityDesc);
switch (sectype)
{
case bts.api.SecurityType.Future:
report.MaturityMonthYear = new MaturityMonthYear(bts.api.SecurityHelper.expiration_date(sec.Value.Future).ToString("yyyyMMdd"));
break;
case bts.api.SecurityType.Option:
throw new NotSupportedException("No options yet");
case bts.api.SecurityType.Spread:
//report.MaturityMonthYear = new MaturityMonthYear(bts.api.SecurityHelper.EarliestExpiration(sec.Value).ToString("yyyyMMdd"));
//TODO
//bug here need to kill MDCLIENT
break;
default:
report.MaturityMonthYear = new MaturityMonthYear(bts.api.SecurityHelper.EarliestExpiration(sec.Value).ToString("yyyyMMdd"));
break;
}
return sec;
}
virtual protected QuickFix.FIX44.ExecutionReport build_fill(SessionID sess, in FillImmutable fill, bool change = false)
{
var exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.FILL);
QuickFix.Fields.OrdStatus status = new QuickFix.Fields.OrdStatus();
uint filled = 0; uint leaves = 0; double prx = 0.0;
OrderRecordImmutable order; NativeOrderStateImmutable? native = null;
if (_order_cache.TryGetValue(fill.OrderKey, out order))
{
process_order_state(order.State, ref filled, ref leaves, ref prx, ref status, ref native);
if (leaves > 0)
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.PARTIAL_FILL);
}
if (change)
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.TRADE_CORRECT);
var sec = _router.lookup(fill.SecurityKey);
QuickFix.FIX44.ExecutionReport report = new QuickFix.FIX44.ExecutionReport(
new OrderID(fill.OrderKey.ToString()),
new ExecID(fill.FillKey.ToString()),
exec,
status,
new Symbol(sec?.Symbol ?? string.Empty),
new Side(fill.Side == bts.api.Side.Buy ? Side.BUY : Side.SELL),
new LeavesQty(leaves),
new CumQty(filled),
new AvgPx(Convert.ToDecimal(prx)));
report.Account = new Account(fill.Account);
report.LastQty = new LastQty(Convert.ToDecimal(fill.LastShares));
report.LastPx = new LastPx(Convert.ToDecimal(fill.LastPrice));
var ts = fill.HasTimestamp ? fill.Timestamp.ToDateTime() : DateTime.MinValue;
report.TransactTime = new TransactTime(ts, true);
report.TradeDate = new TradeDate(ts.ToString("yyyyMMdd"));
sec = resolve_execution_report_security(sec, report);
if (native.HasValue)
report.SecondaryOrderID = new SecondaryOrderID(native.Value.ExchangeOrderId);
if (sec.HasValue && sec.Value.security_type() == bts.api.SecurityType.Spread ||
sec.Value.security_type() == bts.api.SecurityType.Synthetic)
{
if (fill.FillKind == bts.api.orderman.Fill.Types.FillKind.Leg)
report.MultiLegReportingType = new MultiLegReportingType('2');
else
report.MultiLegReportingType = new MultiLegReportingType('3');
}
return report;
}
virtual protected QuickFix.FIX44.ExecutionReport build_order_status(SessionID sess, in OrderRecordImmutable order, bool change = false)
{
var exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.NEW);
if (_order_cache.TryGetValue(order.Key, out OrderRecordImmutable _))
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.ORDER_STATUS);
QuickFix.Fields.OrdStatus status = new QuickFix.Fields.OrdStatus();
uint filled = 0; uint leaves = 0; double prx = 0.0; NativeOrderStateImmutable? native = null;
process_order_state(order.State, ref filled, ref leaves, ref prx, ref status, ref native);
var sec = _router.lookup(order.Def.SecurityKey);
QuickFix.FIX44.ExecutionReport report = new QuickFix.FIX44.ExecutionReport(
new OrderID(order.Key.ToString()),
new ExecID("0"),
exec,
status,
new Symbol(sec?.Symbol ?? string.Empty),
new Side(order.Def.Side == bts.api.Side.Buy ? Side.BUY : Side.SELL),
new LeavesQty(leaves),
new CumQty(filled),
new AvgPx(Convert.ToDecimal(prx)));
report.Account = new Account(order.Def.Account);
report.OrderQty = new OrderQty(order.State.Quantity);
report.Price = new Price(Convert.ToDecimal(order.State.Price));
report.TimeInForce = new QuickFix.Fields.TimeInForce((char)order.Def.TimeInForce);
var ts = order.State.LastChange.ToDateTime();
report.TransactTime = new TransactTime(ts, true);
if(!string.IsNullOrEmpty(order.State.LastMsg))
report.Text = new Text(order.State.LastMsg);
sec = resolve_execution_report_security(sec, report);
if (native.HasValue)
{
report.SecondaryOrderID = new SecondaryOrderID(native.Value.ExchangeOrderId);
report.OrdType = new OrdType((char)native.Value.NativeType);
}
//TODO Big race here, we need a correlation id of some sort
if (_fix_clorderids.TryGetValue(order.Key, out string clorderid))
{
report.ClOrdID = new ClOrdID(clorderid);
}
return report;
}
}
}
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QuickFix;
using QuickFix.Fields;
using bts.api;
using bts.api.orderman;
namespace bts.fix
{
class OrderSession : DropCopySession, QuickFix.IApplication
{
public OrderSession(IFixRouter router, SessionID sess) : base(router, sess)
{
}
override public void FromApp(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
MsgType type = Message.IdentifyType(message.ToString());
switch (type.getValue())
{
case MsgType.ORDER_STATUS_REQUEST:
case MsgType.SECURITYDEFINITIONREQUEST:
case MsgType.NEWORDERSINGLE:
case MsgType.ORDERCANCELREPLACEREQUEST:
case MsgType.ORDERCANCELREQUEST:
log_info("IN: " + message);
Crack(message, sessionID);
break;
default:
log_info("UNSUPPORTED MESSAGE IN: " + type.getValue());
throw new UnsupportedMessageType();
}
}
virtual public void OnMessage(QuickFix.FIX44.NewOrderSingle req, SessionID s)
{
Venue ven = convert_exchange(req.SecurityExchange);
if(ven != Venue.Glbx)
throw new IncorrectTagValue(req.SecurityExchange.Tag);
var sec = _router.lookup(req.Symbol.getValue());
if(sec == null)
sec = _router.lookup(req.SecurityDesc.getValue());
if(!sec.HasValue)
throw new IncorrectTagValue(req.SecurityDesc.Tag);
OrderDefinition def = new OrderDefinition()
{
Account = req.Account.getValue(),
AccountId = lookup_account(req.Account.getValue()),
SecurityKey = sec.Value.Id,
Side = (api.Side)int.Parse(req.Side.getValue().ToString()),
TimeInForce = (api.orderman.TimeInForce)int.Parse(req.TimeInForce.getValue().ToString()),
User = _user_name,
UserId = lookup_user(_user_name),
ManualOrderIndicator = bts.api.orderman.ManualOrderIndicator.Yes,
};
set_ordertype(def, req, out bool price_req);
Create cr = new Create();
if(price_req)
cr.Price = Convert.ToDouble(req.Price.getValue());
cr.NewOrder = def;
cr.Quantity = Convert.ToUInt32(Math.Abs(req.OrderQty.getValue()));
_orderservice.create_order_async(cr);
}
private void set_ordertype(OrderDefinition def, QuickFix.FIX44.NewOrderSingle req, out bool price_req)
{
price_req = false;
switch (req.OrdType.getValue())
{
case '1':
def.CustomDef = new CustomOrderDefinition { Market = new MarketOrderDefinition() };
break;
case '2':
def.CustomDef = new CustomOrderDefinition { Limit = new LimitOrderDefinition() };
price_req = true;
break;
case '3':
def.CustomDef = new CustomOrderDefinition { Stop = new StopOrderDefinition() {StopPrice = Convert.ToDouble(req.StopPx.getValue()) } };
break;
case '4':
def.CustomDef = new CustomOrderDefinition { StopLimit = new StopLimitOrderDefinition() { StopPrice = Convert.ToDouble(req.StopPx.getValue()) } };
price_req = true;
break;
default:
throw new IncorrectTagValue(req.OrdType.Tag);
}
}
}
}
+359
View File
@@ -0,0 +1,359 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Text;
using QuickFix;
using QuickFix.Fields;
using bts.api.core;
using bts.api.orderman;
using bts.api.user;
using bts.api.admin;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
namespace bts.fix
{
abstract class SessionBase : QuickFix.MessageCracker, QuickFix.IApplication
{
protected static UserService _userservice = bts.api.Locator.locate<UserService>();
protected static OrderService _orderservice = bts.api.Locator.locate<OrderService>();
protected static AdminService _adminservice = bts.api.Locator.locate<AdminService>();
protected static bts.metrics.ShellLatencyService _latencyservice = bts.api.Locator.locate<bts.metrics.ShellLatencyService>();
protected string _user_name;
protected AuthToken _token;
protected IDisposable _admindisp;
protected IObservable<AdminModelWrapper> _adminobservable;
protected AdminDataImmutable? _admin_data;
protected readonly IFixRouter _router;
protected readonly SessionID _sess;
public SessionBase(IFixRouter router, SessionID sess)
{
_router = router;
_sess = sess;
_clockdisp = _latencyservice.subscribe_clock().Subscribe(x =>
{
var t = x.all.FirstOrDefault();
if(t != null)
_clock = t.Timestamp;
});
_adminobservable = _adminservice.subscribei_admin_data().Select(x =>
{
return new AdminModelWrapper(x.all.TryFirst(), _user_name);
});
_admindisp = _adminobservable.Subscribe(x => _admin_data = x.admin_data);
}
abstract public void FromApp(Message message, SessionID sessionID);
abstract public void OnLogon(SessionID sessionID); //subscribe to your stuff
abstract public void OnLogout(SessionID sessionID); //unsub
virtual public void ToApp(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
log_info("OUT: " + message);
}
readonly IDisposable _clockdisp; ulong _clock = 0;
virtual public void FromAdmin(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
MsgType type = Message.IdentifyType(message.ToString());
switch (type.getValue())
{
case MsgType.LOGON:
{
try
{
QuickFix.FIX44.Logon msg = (QuickFix.FIX44.Logon)message;
_user_name = msg.Username.getValue();
string pass = string.Empty;
if(msg.IsSetPassword())
pass = msg.Password.getValue();
var resp = _userservice.authenticate_login(new AuthenticateLoginRequest {
Login = msg.Username.getValue(), Password = pass});
if (resp != null && resp.IsValid)
_token = resp.Token;
else
throw new RejectLogon("Logon rejected " + resp?.Reason ?? string.Empty);
}
catch (RejectLogon)
{
throw;
}
catch (Exception ex)
{
throw new RejectLogon("Logon rejected " + ex.ToString());
}
}
break;
case MsgType.HEARTBEAT:
{
QuickFix.FIX44.Heartbeat hb = (QuickFix.FIX44.Heartbeat)message;
handle_heartbeat(hb, sessionID);
}
break;
default:
log_info("ADMININ: " + message);
break;
}
}
async protected void handle_heartbeat(QuickFix.FIX44.Heartbeat hb, SessionID sessionID)
{
var resp = await _latencyservice.report_async(new bts.metrics.Report { HostName = "BTSFIX_" + sessionID.ToString(),
Timestamp = _clock, User = _user_name });
if (resp.success)
send(hb, sessionID);
}
virtual public void ToAdmin(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
log_info("ADMINOUT: " + message);
}
virtual public void OnCreate(SessionID sessionID)
{
log_info("CREATE");
}
virtual public void OnMessage(QuickFix.FIX44.BusinessMessageReject n, SessionID s)
{
//Log.assert(false, "biz reject wtf " + n.EncodedText.getValue());
Log.FIX.warn(_sess.ToString() + " Business Reject " + n.BusinessRejectReason.getValue() + " " + n.Text.getValue());
}
virtual public void OnMessage(QuickFix.FIX44.News n, SessionID s)
{
log_info("news " + n.EncodedHeadline.getValue());
}
protected void log_info(string message, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0)
{
Log.FIX.info(_sess.ToString() + " " + message, file, line);
}
protected void log_error(string message, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0)
{
Log.FIX.error(_sess.ToString() + " " + message, file, line);
}
protected void send(Message mess, SessionID sess)
{
try
{
Session.SendToTarget(mess, sess);
}
catch (SessionNotFound ex)
{
log_error("==session not found exception!==");
log_error(ex.ToString());
Log.assert(false, "session not found " + sess);
}
catch (Exception ex)
{
log_error("==exception sending to " + sess + " " + ex.ToString());
}
}
protected void send_reject(SessionID sess, string text, Message refmessage = null)
{
var seq = new QuickFix.Fields.MsgSeqNum();
QuickFix.FIX44.Reject rej = null;
if (refmessage.Header.IsSetField(seq))
rej = new QuickFix.FIX44.Reject(new QuickFix.Fields.RefSeqNum(seq.getValue()));
else
rej = new QuickFix.FIX44.Reject();
rej.Text = new Text(text);
send(rej, sess);
}
protected bool security_types_equal(bts.api.SecurityType btstype, QuickFix.Fields.SecurityType fixtype)
{
return btstype == convert_security_type(fixtype);
}
protected QuickFix.Fields.SecurityType convert_security_type(bts.api.SecurityType btstype)
{
switch (btstype)
{
case bts.api.SecurityType.Future:
return new QuickFix.Fields.SecurityType("FUT");
case bts.api.SecurityType.Option:
return new QuickFix.Fields.SecurityType("OPT");
case bts.api.SecurityType.Spread:
return new QuickFix.Fields.SecurityType("MLEG");
case bts.api.SecurityType.Stock:
return new QuickFix.Fields.SecurityType("CS");
case bts.api.SecurityType.Synthetic:
return new QuickFix.Fields.SecurityType("SYNTH");
default:
return new QuickFix.Fields.SecurityType();
}
}
protected bts.api.SecurityType convert_security_type(QuickFix.Fields.SecurityType fixtype)
{
switch (fixtype.getValue())
{
case "FUT":
return bts.api.SecurityType.Future;
case "OPT":
return bts.api.SecurityType.Option;
case "MLEG":
return bts.api.SecurityType.Spread;
case "CS":
return bts.api.SecurityType.Stock;
case "SYNTH":
return bts.api.SecurityType.Synthetic;
default:
return bts.api.SecurityType.Unset;
}
}
protected bool exchanges_equal(bts.api.Venue btstype, QuickFix.Fields.SecurityExchange fixtype)
{
return btstype == convert_exchange(fixtype);
}
protected QuickFix.Fields.SecurityExchange convert_exchange(bts.api.Venue btstype)
{
switch (btstype)
{
case bts.api.Venue.Bts:
return new QuickFix.Fields.SecurityExchange("BTS");
case bts.api.Venue.Cboe:
return new QuickFix.Fields.SecurityExchange("XCBO");
case bts.api.Venue.Cboecfe:
return new QuickFix.Fields.SecurityExchange("XCBF");
case bts.api.Venue.Glbx:
return new QuickFix.Fields.SecurityExchange("XCME");
case bts.api.Venue.Ice:
return new QuickFix.Fields.SecurityExchange("IEPA");
case bts.api.Venue.Sim:
return new QuickFix.Fields.SecurityExchange("SIM");
default:
return new QuickFix.Fields.SecurityExchange();
}
}
protected bts.api.Venue convert_exchange(QuickFix.Fields.SecurityExchange fixtype)
{
switch (fixtype.getValue())
{
case "BTS":
return bts.api.Venue.Bts;
case "XCBO":
return bts.api.Venue.Cboe;
case "XCBF":
return bts.api.Venue.Cboecfe;
case "XCME":
case "XCBT":
case "XCEC":
case "XNYM":
return bts.api.Venue.Glbx;
case "IEPA":
case "IFUS":
case "IFEU":
case "IFLL":
return bts.api.Venue.Ice;
case "SIM":
return bts.api.Venue.Sim;
default:
return bts.api.Venue.Unset;
}
}
protected QuickFix.Fields.OrdStatus translate_status(in bts.api.orderman.OrderStateImmutable state,
ref bts.api.orderman.NativeOrderStateImmutable? native)
{
native = null;
if (state.HasMarket)
{
native = state.Market.NativeState;
return new OrdStatus((char)native?.NativeStatus);
}
else if (state.HasLimit)
{
native = state.Limit.NativeState;
return new OrdStatus((char)native?.NativeStatus);
}
else if (state.HasStop)
{
native = state.Stop.NativeState;
return new OrdStatus((char)native?.NativeStatus);
}
else if (state.HasStopLimit)
{
native = state.StopLimit.NativeState;
return new OrdStatus((char)native?.NativeStatus);
}
else
{
switch (state.Status)
{
case OrderStatus.Canceled:
return new OrdStatus(OrdStatus.CANCELED);
case OrderStatus.Completed:
return new OrdStatus(OrdStatus.FILLED);
case OrderStatus.Disconnected:
return new OrdStatus(OrdStatus.STOPPED);
case OrderStatus.Error:
return new OrdStatus(OrdStatus.REJECTED);
case OrderStatus.Inactive:
return new OrdStatus(OrdStatus.PENDING_NEW);
case OrderStatus.PendingWorking:
return new OrdStatus(OrdStatus.PENDING_NEW);
case OrderStatus.RejectedBySpark:
return new OrdStatus(OrdStatus.REJECTED);
case OrderStatus.Working:
{
if (state.FilledQuantity < state.Quantity)
return new OrdStatus(OrdStatus.PARTIALLY_FILLED);
else
return new OrdStatus(OrdStatus.NEW);
}
default:
return new OrdStatus();
}
}
}
protected uint lookup_account(string acct)
{
if (_admin_data.HasValue)
{
var ac = _admin_data.Value.Accounts.Where(x => x.Name == acct).TryFirst();
if(ac.HasValue)
return ac.Value.Id;
}
throw new IncorrectTagValue(new Account().Tag);
}
protected uint lookup_user(string user)
{
if (_admin_data.HasValue)
{
var ac = _admin_data.Value.Users.Where(x => x.Name == user).TryFirst();
if (ac.HasValue)
return ac.Value.Id;
}
throw new IncorrectTagValue(new Username().Tag);
}
}
}
+42
View File
@@ -0,0 +1,42 @@
#http://www.quickfixengine.org/quickfix/doc/html/configuration.html
#https://www.onixs.biz/fix-dictionary.html
[DEFAULT]
ConnectionType=acceptor
SocketAcceptPort=5001
SocketReuseAddress=Y
StartTime=00:00:00
EndTime=00:00:00
FileLogPath=log
UseDataDictionary=Y
ResetOnLogon=Y
ResetOnLogout=Y
ResetOnDisconnect=Y
HeartBtInt=30
LogonTimeout=30
SSLEnable=N
# SSLEnable=Y
# # It is recommended to install the certificate and refer to it by name instead of using filename + password
# SSLCertificate=QuickFixn-TestServer.pfx
# SSLCertificatePassword=QuickFixn-TestServer
# # For production refer to certificate by name instead: SSLCertificate=CN=QuickFixn-TestServer
# SSLCACertificate=QuickFixn-TestCA.cer
# SSLCheckCertificateRevocation=N
[SESSION]
BeginString=FIX.4.4
SenderCompID=SPARK
TargetCompID=CLIENT1
FileStorePath=store
DataDictionary=./spec/fix/FIX44.xml
[SESSION]
BeginString=FIX.4.4
SenderCompID=SPARK
TargetCompID=CLIENT2
FileStorePath=store
DataDictionary=./spec/fix/FIX44.xml
+41
View File
@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<Authors>Blue Trading Systems</Authors>
<Company>Blue Trading Systems</Company>
<Product>Spark</Product>
<Description />
<Platforms>x64</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
<LangVersion>7.2</LangVersion>
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="QuickFix.Net.NETCore" Version="1.8.1" />
<PackageReference Include="QuickFix.Net.NETCore.FIX44" Version="1.8.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../spark_api/spark_api.csproj"/>
<ProjectReference Include="../spark_core/spark_core.csproj"/>
</ItemGroup>
<Target Name="copy_fix" AfterTargets="PostBuildEvent" >
<Copy SourceFiles="./spark_fix.cfg" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
<Copy SourceFiles="./spec/fix/FIX44.xml" DestinationFolder="$(OutputPath)/spec/fix/" SkipUnchangedFiles="true" />
</Target>
<Target Name="copy_fix471" AfterTargets="PostBuildEvent" >
<Copy SourceFiles="./spark_fix.cfg" DestinationFolder="$(OutputPath)/../net471" SkipUnchangedFiles="true" />
<Copy SourceFiles="./spec/fix/FIX44.xml" DestinationFolder="$(OutputPath)/../net471/spec/fix/" SkipUnchangedFiles="true" />
</Target>
</Project>
+828
View File
@@ -0,0 +1,828 @@
<fix major="4" minor="0">
<header>
<field name="BeginString" required="Y"/>
<field name="BodyLength" required="Y"/>
<field name="MsgType" required="Y"/>
<field name="SenderCompID" required="Y"/>
<field name="TargetCompID" required="Y"/>
<field name="OnBehalfOfCompID" required="N"/>
<field name="DeliverToCompID" required="N"/>
<field name="SecureDataLen" required="N"/>
<field name="SecureData" required="N"/>
<field name="MsgSeqNum" required="Y"/>
<field name="SenderSubID" required="N"/>
<field name="TargetSubID" required="N"/>
<field name="OnBehalfOfSubID" required="N"/>
<field name="DeliverToSubID" required="N"/>
<field name="PossDupFlag" required="N"/>
<field name="PossResend" required="N"/>
<field name="SendingTime" required="Y"/>
<field name="OrigSendingTime" required="N"/>
</header>
<trailer>
<field name="SignatureLength" required="N"/>
<field name="Signature" required="N"/>
<field name="CheckSum" required="Y"/>
</trailer>
<messages>
<message name="Heartbeat" msgtype="0" msgcat="admin">
<field name="TestReqID" required="N"/>
</message>
<message name="Logon" msgtype="A" msgcat="admin">
<field name="EncryptMethod" required="Y"/>
<field name="HeartBtInt" required="Y"/>
<field name="RawDataLength" required="N"/>
<field name="RawData" required="N"/>
</message>
<message name="TestRequest" msgtype="1" msgcat="admin">
<field name="TestReqID" required="Y"/>
</message>
<message name="ResendRequest" msgtype="2" msgcat="admin">
<field name="BeginSeqNo" required="Y"/>
<field name="EndSeqNo" required="Y"/>
</message>
<message name="Reject" msgtype="3" msgcat="admin">
<field name="RefSeqNum" required="Y"/>
<field name="Text" required="N"/>
</message>
<message name="SequenceReset" msgtype="4" msgcat="admin">
<field name="GapFillFlag" required="N"/>
<field name="NewSeqNo" required="Y"/>
</message>
<message name="Logout" msgtype="5" msgcat="admin">
<field name="Text" required="N"/>
</message>
<message name="Advertisement" msgtype="7" msgcat="app">
<field name="AdvId" required="Y"/>
<field name="AdvTransType" required="Y"/>
<field name="AdvRefID" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="AdvSide" required="Y"/>
<field name="Shares" required="Y"/>
<field name="Price" required="N"/>
<field name="Currency" required="N"/>
<field name="TransactTime" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="IndicationofInterest" msgtype="6" msgcat="app">
<field name="IOIid" required="Y"/>
<field name="IOITransType" required="Y"/>
<field name="IOIRefID" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
<field name="IOIShares" required="Y"/>
<field name="Price" required="N"/>
<field name="Currency" required="N"/>
<field name="ValidUntilTime" required="N"/>
<field name="IOIQltyInd" required="N"/>
<field name="IOIOthSvc" required="N"/>
<field name="IOINaturalFlag" required="N"/>
<field name="IOIQualifier" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="News" msgtype="B" msgcat="app">
<field name="OrigTime" required="N"/>
<field name="Urgency" required="N"/>
<field name="RelatdSym" required="N" repeating="Y"/>
<group name="LinesOfText" required="Y">
<field name="Text" required="Y"/>
</group>
<field name="RawDataLength" required="N"/>
<field name="RawData" required="N"/>
</message>
<message name="Email" msgtype="C" msgcat="app">
<field name="EmailType" required="Y"/>
<field name="OrigTime" required="N"/>
<field name="RelatdSym" required="N" repeating="Y"/>
<field name="OrderID" required="N"/>
<field name="ClOrdID" required="N"/>
<group name="LinesOfText" required="Y">
<field name="Text" required="Y"/>
</group>
<field name="RawDataLength" required="N"/>
<field name="RawData" required="N"/>
</message>
<message name="QuoteRequest" msgtype="R" msgcat="app">
<field name="QuoteReqID" required="Y"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="PrevClosePx" required="N"/>
<field name="Side" required="N"/>
<field name="OrderQty" required="N"/>
</message>
<message name="Quote" msgtype="S" msgcat="app">
<field name="QuoteReqID" required="N"/>
<field name="QuoteID" required="Y"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="BidPx" required="Y"/>
<field name="OfferPx" required="N"/>
<field name="BidSize" required="N"/>
<field name="OfferSize" required="N"/>
<field name="ValidUntilTime" required="N"/>
</message>
<message name="NewOrderSingle" msgtype="D" msgcat="app">
<field name="ClOrdID" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="Account" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="HandlInst" required="Y"/>
<field name="ExecInst" required="N"/>
<field name="MinQty" required="N"/>
<field name="MaxFloor" required="N"/>
<field name="ExDestination" required="N"/>
<field name="ProcessCode" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="PrevClosePx" required="N"/>
<field name="Side" required="Y"/>
<field name="LocateReqd" required="N"/>
<field name="OrderQty" required="Y"/>
<field name="OrdType" required="Y"/>
<field name="Price" required="N"/>
<field name="StopPx" required="N"/>
<field name="Currency" required="N"/>
<field name="IOIid" required="N"/>
<field name="QuoteID" required="N"/>
<field name="TimeInForce" required="N"/>
<field name="ExpireTime" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<field name="Rule80A" required="N"/>
<field name="ForexReq" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="ExecutionReport" msgtype="8" msgcat="app">
<field name="OrderID" required="Y"/>
<field name="ClOrdID" required="N"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="ListID" required="N"/>
<field name="ExecID" required="Y"/>
<field name="ExecTransType" required="Y"/>
<field name="ExecRefID" required="N"/>
<field name="OrdStatus" required="Y"/>
<field name="OrdRejReason" required="N"/>
<field name="Account" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
<field name="OrderQty" required="Y"/>
<field name="OrdType" required="N"/>
<field name="Price" required="N"/>
<field name="StopPx" required="N"/>
<field name="Currency" required="N"/>
<field name="TimeInForce" required="N"/>
<field name="ExpireTime" required="N"/>
<field name="ExecInst" required="N"/>
<field name="Rule80A" required="N"/>
<field name="LastShares" required="Y"/>
<field name="LastPx" required="Y"/>
<field name="LastMkt" required="N"/>
<field name="LastCapacity" required="N"/>
<field name="CumQty" required="Y"/>
<field name="AvgPx" required="Y"/>
<field name="TradeDate" required="N"/>
<field name="TransactTime" required="N"/>
<field name="ReportToExch" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<group name="NoMiscFees" required="N">
<field name="MiscFeeAmt" required="N"/>
<field name="MiscFeeCurr" required="N"/>
<field name="MiscFeeType" required="N"/>
</group>
<field name="NetMoney" required="N"/>
<field name="SettlCurrAmt" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="DontKnowTrade" msgtype="Q" msgcat="app">
<field name="OrderID" required="N"/>
<field name="ExecID" required="N"/>
<field name="DKReason" required="Y"/>
<field name="Symbol" required="Y"/>
<field name="Side" required="Y"/>
<field name="OrderQty" required="Y"/>
<field name="LastShares" required="Y"/>
<field name="LastPx" required="Y"/>
<field name="Text" required="N"/>
</message>
<message name="OrderCancelReplaceRequest" msgtype="G" msgcat="app">
<field name="OrderID" required="N"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="OrigClOrdID" required="Y"/>
<field name="ClOrdID" required="Y"/>
<field name="ListID" required="N"/>
<field name="Account" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="HandlInst" required="Y"/>
<field name="ExecInst" required="N"/>
<field name="MinQty" required="N"/>
<field name="MaxFloor" required="N"/>
<field name="ExDestination" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
<field name="OrderQty" required="Y"/>
<field name="OrdType" required="Y"/>
<field name="Price" required="N"/>
<field name="StopPx" required="N"/>
<field name="Currency" required="N"/>
<field name="TimeInForce" required="N"/>
<field name="ExpireTime" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<field name="Rule80A" required="N"/>
<field name="ForexReq" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="OrderCancelRequest" msgtype="F" msgcat="app">
<field name="OrigClOrdID" required="Y"/>
<field name="OrderID" required="N"/>
<field name="ClOrdID" required="Y"/>
<field name="ListID" required="N"/>
<field name="CxlType" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
<field name="OrderQty" required="Y"/>
<field name="Text" required="N"/>
</message>
<message name="OrderCancelReject" msgtype="9" msgcat="app">
<field name="OrderID" required="Y"/>
<field name="ClOrdID" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="ListID" required="N"/>
<field name="CxlRejReason" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="OrderStatusRequest" msgtype="H" msgcat="app">
<field name="OrderID" required="N"/>
<field name="ClOrdID" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
</message>
<message name="Allocation" msgtype="J" msgcat="app">
<field name="AllocID" required="Y"/>
<field name="AllocTransType" required="Y"/>
<field name="RefAllocID" required="N"/>
<group name="NoOrders" required="Y">
<field name="ClOrdID" required="Y"/>
<field name="OrderID" required="N"/>
<field name="ListID" required="N"/>
<field name="WaveNo" required="N"/>
</group>
<group name="NoExecs" required="N">
<field name="ExecID" required="N"/>
<field name="LastShares" required="N"/>
<field name="LastPx" required="N"/>
<field name="LastMkt" required="N"/>
</group>
<field name="Side" required="Y"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Shares" required="Y"/>
<field name="AvgPx" required="Y"/>
<field name="Currency" required="N"/>
<field name="AvgPrxPrecision" required="N"/>
<field name="TradeDate" required="Y"/>
<field name="TransactTime" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="NetMoney" required="N"/>
<group name="NoMiscFees" required="N">
<field name="MiscFeeAmt" required="N"/>
<field name="MiscFeeCurr" required="N"/>
<field name="MiscFeeType" required="N"/>
</group>
<field name="SettlCurrAmt" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="OpenClose" required="N"/>
<field name="Text" required="N"/>
<group name="NoAllocs" required="Y">
<field name="AllocAccount" required="Y"/>
<field name="AllocShares" required="Y"/>
<field name="ProcessCode" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="ClientID" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<group name="NoDlvyInst" required="N">
<field name="BrokerOfCredit" required="N"/>
<field name="DlvyInst" required="N"/>
</group>
</group>
</message>
<message name="AllocationACK" msgtype="P" msgcat="app">
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="AllocID" required="Y"/>
<field name="TradeDate" required="Y"/>
<field name="TransactTime" required="N"/>
<field name="AllocStatus" required="Y"/>
<field name="AllocRejCode" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="NewOrderList" msgtype="E" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="ListSeqNo" required="Y"/>
<field name="ListNoOrds" required="Y"/>
<field name="ListExecInst" required="N"/>
<field name="ClOrdID" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="Account" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="HandlInst" required="Y"/>
<field name="ExecInst" required="N"/>
<field name="MinQty" required="N"/>
<field name="MaxFloor" required="N"/>
<field name="ExDestination" required="N"/>
<field name="ProcessCode" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="PrevClosePx" required="N"/>
<field name="Side" required="Y"/>
<field name="LocateReqd" required="N"/>
<field name="OrderQty" required="Y"/>
<field name="OrdType" required="Y"/>
<field name="Price" required="N"/>
<field name="StopPx" required="N"/>
<field name="Currency" required="N"/>
<field name="TimeInForce" required="N"/>
<field name="ExpireTime" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<field name="Rule80A" required="N"/>
<field name="ForexReq" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="ListStatus" msgtype="N" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="NoRpts" required="Y"/>
<field name="RptSeq" required="Y"/>
<group name="NoOrders" required="Y">
<field name="ClOrdID" required="Y"/>
<field name="CumQty" required="Y"/>
<field name="CxlQty" required="Y"/>
<field name="AvgPx" required="Y"/>
</group>
</message>
<message name="ListExecute" msgtype="L" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="ListCancelRequest" msgtype="K" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="ListStatusRequest" msgtype="M" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="Text" required="N"/>
</message>
</messages>
<fields>
<field number="1" name="Account" type="STRING"/>
<field number="2" name="AdvId" type="INT"/>
<field number="3" name="AdvRefID" type="INT"/>
<field number="4" name="AdvSide" type="STRING">
<value enum="B" description="BUY"/>
<value enum="S" description="SELL"/>
<value enum="X" description="CROSS"/>
<value enum="T" description="TRADE"/>
</field>
<field number="5" name="AdvTransType" type="STRING">
<value enum="N" description="NEW"/>
<value enum="C" description="CANCEL"/>
<value enum="R" description="REPLACE"/>
</field>
<field number="6" name="AvgPx" type="FLOAT"/>
<field number="7" name="BeginSeqNo" type="INT"/>
<field number="8" name="BeginString" type="STRING"/>
<field number="9" name="BodyLength" type="INT"/>
<field number="10" name="CheckSum" type="STRING"/>
<field number="11" name="ClOrdID" type="STRING"/>
<field number="12" name="Commission" type="FLOAT"/>
<field number="13" name="CommType" type="STRING">
<value enum="1" description="PER_SHARE"/>
<value enum="2" description="PERCENTAGE"/>
<value enum="3" description="ABSOLUTE"/>
</field>
<field number="14" name="CumQty" type="INT"/>
<field number="15" name="Currency" type="STRING"/>
<field number="16" name="EndSeqNo" type="INT"/>
<field number="17" name="ExecID" type="INT"/>
<field number="18" name="ExecInst" type="MULTIPLEVALUESTRING">
<value enum="1" description="NOT_HELD"/>
<value enum="2" description="WORK"/>
<value enum="3" description="GO_ALONG"/>
<value enum="4" description="OVER_THE_DAY"/>
<value enum="5" description="HELD"/>
<value enum="6" description="PARTICIPATE_DONT_INITIATE"/>
<value enum="7" description="STRICT_SCALE"/>
<value enum="8" description="TRY_TO_SCALE"/>
<value enum="9" description="STAY_ON_BIDSIDE"/>
<value enum="0" description="STAY_ON_OFFERSIDE"/>
<value enum="A" description="NO_CROSS"/>
<value enum="B" description="OK_TO_CROSS"/>
<value enum="C" description="CALL_FIRST"/>
<value enum="D" description="PERCENT_OF_VOLUME"/>
<value enum="E" description="DO_NOT_INCREASE_DNI"/>
<value enum="F" description="DO_NOT_REDUCE_DNR"/>
<value enum="G" description="ALL_OR_NONE_AON"/>
<value enum="I" description="INSTITUTIONS_ONLY"/>
<value enum="L" description="LAST_PEG"/>
<value enum="M" description="MIDPRICE_PEG"/>
<value enum="N" description="NONNEGOTIABLE"/>
<value enum="O" description="OPENING_PEG"/>
<value enum="P" description="MARKET_PEG"/>
<value enum="R" description="PRIMARY_PEG"/>
<value enum="S" description="SUSPEND"/>
</field>
<field number="19" name="ExecRefID" type="INT"/>
<field number="20" name="ExecTransType" type="STRING">
<value enum="0" description="NEW"/>
<value enum="1" description="CANCEL"/>
<value enum="2" description="CORRECT"/>
<value enum="3" description="STATUS"/>
</field>
<field number="21" name="HandlInst" type="STRING">
<value enum="1" description="AUTOMATED_EXECUTION_ORDER_PRIVATE_NO_BROKER_INTERVENTION"/>
<value enum="2" description="AUTOMATED_EXECUTION_ORDER_PUBLIC_BROKER_INTERVENTION_OK"/>
<value enum="3" description="MANUAL_ORDER_BEST_EXECUTION"/>
</field>
<field number="22" name="IDSource" type="STRING">
<value enum="1" description="CUSIP"/>
<value enum="2" description="SEDOL"/>
<value enum="3" description="QUIK"/>
<value enum="4" description="ISIN_NUMBER"/>
<value enum="5" description="RIC_CODE"/>
</field>
<field number="23" name="IOIid" type="INT"/>
<field number="24" name="IOIOthSvc" type="STRING"/>
<field number="25" name="IOIQltyInd" type="STRING">
<value enum="L" description="LOW"/>
<value enum="M" description="MEDIUM"/>
<value enum="H" description="HIGH"/>
</field>
<field number="26" name="IOIRefID" type="INT"/>
<field number="27" name="IOIShares" type="STRING"/>
<field number="28" name="IOITransType" type="STRING">
<value enum="N" description="NEW"/>
<value enum="C" description="CANCEL"/>
<value enum="R" description="REPLACE"/>
</field>
<field number="29" name="LastCapacity" type="STRING">
<value enum="1" description="AGENT"/>
<value enum="2" description="CROSS_AS_AGENT"/>
<value enum="3" description="CROSS_AS_PRINCIPAL"/>
<value enum="4" description="PRINCIPAL"/>
</field>
<field number="30" name="LastMkt" type="STRING"/>
<field number="31" name="LastPx" type="FLOAT"/>
<field number="32" name="LastShares" type="INT"/>
<field number="33" name="LinesOfText" type="INT"/>
<field number="34" name="MsgSeqNum" type="INT"/>
<field number="35" name="MsgType" type="STRING"/>
<field number="36" name="NewSeqNo" type="INT"/>
<field number="37" name="OrderID" type="STRING"/>
<field number="38" name="OrderQty" type="INT"/>
<field number="39" name="OrdStatus" type="STRING">
<value enum="0" description="NEW"/>
<value enum="1" description="PARTIALLY_FILLED"/>
<value enum="2" description="FILLED"/>
<value enum="3" description="DONE_FOR_DAY"/>
<value enum="4" description="CANCELED"/>
<value enum="5" description="REPLACED"/>
<value enum="6" description="PENDING_CANCELREPLACE"/>
<value enum="7" description="STOPPED"/>
<value enum="8" description="REJECTED"/>
<value enum="9" description="SUSPENDED"/>
<value enum="A" description="PENDING_NEW"/>
<value enum="B" description="CALCULATED"/>
<value enum="C" description="EXPIRED"/>
</field>
<field number="40" name="OrdType" type="STRING">
<value enum="1" description="MARKET"/>
<value enum="2" description="LIMIT"/>
<value enum="3" description="STOP"/>
<value enum="4" description="STOP_LIMIT"/>
<value enum="5" description="MARKET_ON_CLOSE"/>
<value enum="6" description="WITH_OR_WITHOUT"/>
<value enum="7" description="LIMIT_OR_BETTER"/>
<value enum="8" description="LIMIT_WITH_OR_WITHOUT"/>
<value enum="9" description="ON_BASIS"/>
<value enum="A" description="ON_CLOSE"/>
<value enum="B" description="LIMIT_ON_CLOSE"/>
<value enum="C" description="FOREX"/>
<value enum="D" description="PREVIOUSLY_QUOTED"/>
<value enum="E" description="PREVIOUSLY_INDICATED"/>
<value enum="P" description="PEGGED"/>
</field>
<field number="41" name="OrigClOrdID" type="STRING"/>
<field number="42" name="OrigTime" type="TIME"/>
<field number="43" name="PossDupFlag" type="STRING">
<value enum="Y" description="POSSIBLE_DUPLICATE"/>
<value enum="N" description="ORIGINAL_TRANSMISSION"/>
</field>
<field number="44" name="Price" type="FLOAT"/>
<field number="45" name="RefSeqNum" type="INT"/>
<field number="46" name="RelatdSym" type="STRING"/>
<field number="47" name="Rule80A" type="STRING">
<value enum="A" description="AGENCY_SINGLE_ORDER"/>
<value enum="D" description="PROGRAM_ORDER_INDEX_ARB_FOR_MEMBER_FIRMORG"/>
<value enum="C" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_MEMBER_FIRMORG"/>
<value enum="J" description="PROGRAM_ORDER_INDEX_ARB_FOR_INDIVIDUAL_CUSTOMER"/>
<value enum="K" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_INDIVIDUAL_CUSTOMER"/>
<value enum="U" description="PROGRAM_ORDER_INDEX_ARB_FOR_OTHER_AGENCY"/>
<value enum="Y" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_OTHER_AGENCY"/>
<value enum="M" description="PROGRAM_ORDER_INDEX_ARB_FOR_OTHER_MEMBER"/>
<value enum="N" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_OTHER_MEMBER"/>
<value enum="W" description="ALL_OTHER_ORDERS_AS_AGENT_FOR_OTHER_MEMBER"/>
</field>
<field number="48" name="SecurityID" type="STRING"/>
<field number="49" name="SenderCompID" type="STRING"/>
<field number="50" name="SenderSubID" type="STRING"/>
<field number="52" name="SendingTime" type="TIME"/>
<field number="53" name="Shares" type="INT"/>
<field number="54" name="Side" type="STRING">
<value enum="1" description="BUY"/>
<value enum="2" description="SELL"/>
<value enum="3" description="BUY_MINUS"/>
<value enum="4" description="SELL_PLUS"/>
<value enum="5" description="SELL_SHORT"/>
<value enum="6" description="SELL_SHORT_EXEMPT"/>
</field>
<field number="55" name="Symbol" type="STRING"/>
<field number="56" name="TargetCompID" type="STRING"/>
<field number="57" name="TargetSubID" type="STRING"/>
<field number="58" name="Text" type="STRING"/>
<field number="59" name="TimeInForce" type="STRING">
<value enum="0" description="DAY"/>
<value enum="1" description="GOOD_TILL_CANCEL"/>
<value enum="2" description="AT_THE_OPENING"/>
<value enum="3" description="IMMEDIATE_OR_CANCEL"/>
<value enum="4" description="FILL_OR_KILL"/>
<value enum="5" description="GOOD_TILL_CROSSING"/>
<value enum="6" description="GOOD_TILL_DATE"/>
</field>
<field number="60" name="TransactTime" type="TIME"/>
<field number="61" name="Urgency" type="STRING">
<value enum="0" description="NORMAL"/>
<value enum="1" description="FLASH"/>
<value enum="2" description="BACKGROUND"/>
</field>
<field number="62" name="ValidUntilTime" type="TIME"/>
<field number="63" name="SettlmntTyp" type="STRING">
<value enum="0" description="REGULAR"/>
<value enum="1" description="CASH"/>
<value enum="2" description="NEXT_DAY"/>
<value enum="3" description="TPLUS2"/>
<value enum="4" description="TPLUS3"/>
<value enum="5" description="TPLUS4"/>
<value enum="6" description="FUTURE"/>
<value enum="7" description="WHEN_ISSUED"/>
<value enum="8" description="SELLERS_OPTION"/>
<value enum="9" description="TPLUS5"/>
</field>
<field number="64" name="FutSettDate" type="DATE"/>
<field number="65" name="SymbolSfx" type="STRING"/>
<field number="66" name="ListID" type="STRING"/>
<field number="67" name="ListSeqNo" type="INT"/>
<field number="68" name="ListNoOrds" type="INT"/>
<field number="69" name="ListExecInst" type="STRING"/>
<field number="70" name="AllocID" type="INT"/>
<field number="71" name="AllocTransType" type="STRING">
<value enum="0" description="NEW"/>
<value enum="1" description="REPLACE"/>
<value enum="2" description="CANCEL"/>
</field>
<field number="72" name="RefAllocID" type="INT"/>
<field number="73" name="NoOrders" type="INT"/>
<field number="74" name="AvgPrxPrecision" type="INT"/>
<field number="75" name="TradeDate" type="DATE"/>
<field number="76" name="ExecBroker" type="STRING"/>
<field number="77" name="OpenClose" type="STRING"/>
<field number="78" name="NoAllocs" type="INT"/>
<field number="79" name="AllocAccount" type="STRING"/>
<field number="80" name="AllocShares" type="INT"/>
<field number="81" name="ProcessCode" type="STRING">
<value enum="0" description="REGULAR"/>
<value enum="1" description="SOFT_DOLLAR"/>
<value enum="2" description="STEPIN"/>
<value enum="3" description="STEPOUT"/>
<value enum="4" description="SOFTDOLLAR_STEPIN"/>
<value enum="5" description="SOFTDOLLAR_STEPOUT"/>
<value enum="6" description="PLAN_SPONSOR"/>
</field>
<field number="82" name="NoRpts" type="INT"/>
<field number="83" name="RptSeq" type="INT"/>
<field number="84" name="CxlQty" type="INT"/>
<field number="85" name="NoDlvyInst" type="INT"/>
<field number="86" name="DlvyInst" type="STRING"/>
<field number="87" name="AllocStatus" type="INT">
<value enum="0" description="ACCEPTED"/>
<value enum="1" description="REJECTED"/>
<value enum="2" description="PARTIAL_ACCEPT"/>
<value enum="3" description="RECEIVED"/>
</field>
<field number="88" name="AllocRejCode" type="INT">
<value enum="0" description="UNKNOWN_ACCOUNT"/>
<value enum="1" description="INCORRECT_QUANTITY"/>
<value enum="2" description="INCORRECT_AVERAGE_PRICE"/>
<value enum="3" description="UNKNOWN_EXECUTING_BROKER_MNEMONIC"/>
<value enum="4" description="COMMISSION_DIFFERENCE"/>
<value enum="5" description="UNKNOWN_ORDERID"/>
<value enum="6" description="UNKNOWN_LISTID"/>
<value enum="7" description="OTHER"/>
</field>
<field number="89" name="Signature" type="DATA"/>
<field number="90" name="SecureDataLen" type="INT"/>
<field number="91" name="SecureData" type="DATA"/>
<field number="92" name="BrokerOfCredit" type="STRING"/>
<field number="93" name="SignatureLength" type="INT"/>
<field number="94" name="EmailType" type="STRING">
<value enum="0" description="NEW"/>
<value enum="1" description="REPLY"/>
<value enum="2" description="ADMIN_REPLY"/>
</field>
<field number="95" name="RawDataLength" type="INT"/>
<field number="96" name="RawData" type="DATA"/>
<field number="97" name="PossResend" type="STRING"/>
<field number="98" name="EncryptMethod" type="INT">
<value enum="0" description="NONE_OTHER"/>
<value enum="1" description="PKCS"/>
<value enum="2" description="DES"/>
<value enum="3" description="PKCSDES"/>
<value enum="4" description="PGPDES"/>
<value enum="5" description="PGPDESMD5"/>
<value enum="6" description="PEMDESMD5"/>
</field>
<field number="99" name="StopPx" type="FLOAT"/>
<field number="100" name="ExDestination" type="STRING">
<value enum="0" description="NONE"/>
<value enum="4" description="POSIT"/>
</field>
<field number="102" name="CxlRejReason" type="INT">
<value enum="0" description="TOO_LATE_TO_CANCEL"/>
<value enum="1" description="UNKNOWN_ORDER"/>
</field>
<field number="103" name="OrdRejReason" type="INT">
<value enum="0" description="BROKER_OPTION"/>
<value enum="1" description="UNKNOWN_SYMBOL"/>
<value enum="2" description="EXCHANGE_CLOSED"/>
<value enum="3" description="ORDER_EXCEEDS_LIMIT"/>
<value enum="4" description="TOO_LATE_TO_ENTER"/>
</field>
<field number="104" name="IOIQualifier" type="STRING">
<value enum="X" description="CROSSING_OPPORTUNITY"/>
<value enum="O" description="AT_THE_OPEN"/>
<value enum="M" description="MORE_BEHIND"/>
<value enum="P" description="TAKING_A_POSITION"/>
<value enum="V" description="VERSUS"/>
<value enum="Q" description="CURRENT_QUOTE"/>
<value enum="C" description="AT_THE_CLOSE"/>
<value enum="S" description="PORTFOLIO_SHOWN"/>
<value enum="I" description="IN_TOUCH_WITH"/>
<value enum="W" description="INDICATION_WORKING_AWAY"/>
<value enum="A" description="ALL_OR_NONE"/>
<value enum="L" description="LIMIT"/>
<value enum="T" description="THROUGH_THE_DAY"/>
</field>
<field number="105" name="WaveNo" type="STRING"/>
<field number="106" name="Issuer" type="STRING"/>
<field number="107" name="SecurityDesc" type="STRING"/>
<field number="108" name="HeartBtInt" type="INT"/>
<field number="109" name="ClientID" type="STRING"/>
<field number="110" name="MinQty" type="INT"/>
<field number="111" name="MaxFloor" type="INT"/>
<field number="112" name="TestReqID" type="STRING"/>
<field number="113" name="ReportToExch" type="STRING">
<value enum="Y" description="YES"/>
<value enum="N" description="NO"/>
</field>
<field number="114" name="LocateReqd" type="STRING">
<value enum="Y" description="YES"/>
<value enum="N" description="NO"/>
</field>
<field number="115" name="OnBehalfOfCompID" type="STRING"/>
<field number="116" name="OnBehalfOfSubID" type="STRING"/>
<field number="117" name="QuoteID" type="STRING"/>
<field number="118" name="NetMoney" type="FLOAT"/>
<field number="119" name="SettlCurrAmt" type="FLOAT"/>
<field number="120" name="SettlCurrency" type="STRING"/>
<field number="121" name="ForexReq" type="STRING">
<value enum="Y" description="YES"/>
<value enum="N" description="NO"/>
</field>
<field number="122" name="OrigSendingTime" type="TIME"/>
<field number="123" name="GapFillFlag" type="STRING">
<value enum="Y" description="GAP_FILL_MESSAGE_MSGSEQNUM_FIELD_VALID"/>
<value enum="N" description="SEQUENCE_RESET_IGNORE_MSGSEQNUM"/>
</field>
<field number="124" name="NoExecs" type="INT"/>
<field number="125" name="CxlType" type="STRING">
<value enum="P" description="PARTIAL_CANCEL"/>
<value enum="F" description="FULL_REMAINING_QUANTITY"/>
</field>
<field number="126" name="ExpireTime" type="TIME"/>
<field number="127" name="DKReason" type="STRING">
<value enum="A" description="UNKNOWN_SYMBOL"/>
<value enum="B" description="WRONG_SIDE"/>
<value enum="C" description="QUANTITY_EXCEEDS_ORDER"/>
<value enum="D" description="NO_MATCHING_ORDER"/>
<value enum="E" description="PRICE_EXCEEDS_LIMIT"/>
<value enum="Z" description="OTHER" />
</field>
<field number="128" name="DeliverToCompID" type="STRING"/>
<field number="129" name="DeliverToSubID" type="STRING"/>
<field number="130" name="IOINaturalFlag" type="STRING">
<value enum="Y" description="NATURAL"/>
<value enum="N" description="NOT_NATURAL"/>
</field>
<field number="131" name="QuoteReqID" type="STRING"/>
<field number="132" name="BidPx" type="FLOAT"/>
<field number="133" name="OfferPx" type="FLOAT"/>
<field number="134" name="BidSize" type="INT"/>
<field number="135" name="OfferSize" type="INT"/>
<field number="136" name="NoMiscFees" type="INT"/>
<field number="137" name="MiscFeeAmt" type="FLOAT"/>
<field number="138" name="MiscFeeCurr" type="STRING"/>
<field number="139" name="MiscFeeType" type="STRING">
<value enum="1" description="REGULATORY"/>
<value enum="2" description="TAX"/>
<value enum="3" description="LOCAL_COMMISSION"/>
<value enum="4" description="EXCHANGE_FEES"/>
<value enum="5" description="STAMP"/>
<value enum="6" description="LEVY"/>
<value enum="7" description="OTHER"/>
</field>
<field number="140" name="PrevClosePx" type="FLOAT"/>
</fields>
</fix>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+313
View File
@@ -0,0 +1,313 @@
<?xml version='1.0' encoding='UTF-8'?>
<fix type='FIXT' major='1' minor='1' servicepack='0'>
<header>
<field name='BeginString' required='Y'/>
<field name='BodyLength' required='Y'/>
<field name='MsgType' required='Y'/>
<field name='SenderCompID' required='Y'/>
<field name='TargetCompID' required='Y'/>
<field name='OnBehalfOfCompID' required='N'/>
<field name='DeliverToCompID' required='N'/>
<field name='SecureDataLen' required='N'/>
<field name='SecureData' required='N'/>
<field name='MsgSeqNum' required='Y'/>
<field name='SenderSubID' required='N'/>
<field name='SenderLocationID' required='N'/>
<field name='TargetSubID' required='N'/>
<field name='TargetLocationID' required='N'/>
<field name='OnBehalfOfSubID' required='N'/>
<field name='OnBehalfOfLocationID' required='N'/>
<field name='DeliverToSubID' required='N'/>
<field name='DeliverToLocationID' required='N'/>
<field name='PossDupFlag' required='N'/>
<field name='PossResend' required='N'/>
<field name='SendingTime' required='Y'/>
<field name='OrigSendingTime' required='N'/>
<field name='XmlDataLen' required='N'/>
<field name='XmlData' required='N'/>
<field name='MessageEncoding' required='N'/>
<field name='LastMsgSeqNumProcessed' required='N'/>
<component name='HopGrp' required='N'/>
<field name='ApplVerID' required='N'/>
<field name='CstmApplVerID' required='N'/>
</header>
<trailer>
<field name='SignatureLength' required='N'/>
<field name='Signature' required='N'/>
<field name='CheckSum' required='Y'/>
</trailer>
<messages>
<message msgcat='admin' msgtype='0' name='Heartbeat'>
<field name='TestReqID' required='N'/>
</message>
<message msgcat='admin' msgtype='1' name='TestRequest'>
<field name='TestReqID' required='Y'/>
</message>
<message msgcat='admin' msgtype='2' name='ResendRequest'>
<field name='BeginSeqNo' required='Y'/>
<field name='EndSeqNo' required='Y'/>
</message>
<message msgcat='admin' msgtype='3' name='Reject'>
<field name='RefSeqNum' required='Y'/>
<field name='RefTagID' required='N'/>
<field name='RefMsgType' required='N'/>
<field name='SessionRejectReason' required='N'/>
<field name='Text' required='N'/>
<field name='EncodedTextLen' required='N'/>
<field name='EncodedText' required='N'/>
</message>
<message msgcat='admin' msgtype='4' name='SequenceReset'>
<field name='GapFillFlag' required='N'/>
<field name='NewSeqNo' required='Y'/>
</message>
<message msgcat='admin' msgtype='5' name='Logout'>
<field name='Text' required='N'/>
<field name='EncodedTextLen' required='N'/>
<field name='EncodedText' required='N'/>
</message>
<message msgcat='admin' msgtype='A' name='Logon'>
<field name='EncryptMethod' required='Y'/>
<field name='HeartBtInt' required='Y'/>
<field name='RawDataLength' required='N'/>
<field name='RawData' required='N'/>
<field name='ResetSeqNumFlag' required='N'/>
<field name='NextExpectedMsgSeqNum' required='N'/>
<field name='MaxMessageSize' required='N'/>
<field name='TestMessageIndicator' required='N'/>
<field name='Username' required='N'/>
<field name='Password' required='N'/>
<field name='DefaultApplVerID' required='Y'/>
<component name='MsgTypeGrp' required='N'/>
</message>
</messages>
<components>
<component name='HopGrp'>
<group name='NoHops' required='N'>
<field name='HopCompID' required='N'/>
<field name='HopSendingTime' required='N'/>
<field name='HopRefID' required='N'/>
</group>
</component>
<component name='MsgTypeGrp'>
<group name='NoMsgTypes' required='N'>
<field name='RefMsgType' required='N'/>
<field name='MsgDirection' required='N'/>
<field name='RefApplVerID' required='N'/>
<field name='RefCstmApplVerID' required='N'/>
</group>
</component>
</components>
<fields>
<field name='BeginSeqNo' number='7' type='SEQNUM'/>
<field name='BeginString' number='8' type='STRING'/>
<field name='BodyLength' number='9' type='LENGTH'/>
<field name='CheckSum' number='10' type='STRING'/>
<field name='EndSeqNo' number='16' type='SEQNUM'/>
<field name='MsgSeqNum' number='34' type='SEQNUM'/>
<field number='35' name='MsgType' type='STRING'>
<value enum='0' description='HEARTBEAT'/>
<value enum='1' description='TEST_REQUEST'/>
<value enum='2' description='RESEND_REQUEST'/>
<value enum='3' description='REJECT'/>
<value enum='4' description='SEQUENCE_RESET'/>
<value enum='5' description='LOGOUT'/>
<value enum='6' description='INDICATION_OF_INTEREST'/>
<value enum='7' description='ADVERTISEMENT'/>
<value enum='8' description='EXECUTION_REPORT'/>
<value enum='9' description='ORDER_CANCEL_REJECT'/>
<value enum='A' description='LOGON'/>
<value enum='B' description='NEWS'/>
<value enum='C' description='EMAIL'/>
<value enum='D' description='ORDER_SINGLE'/>
<value enum='E' description='ORDER_LIST'/>
<value enum='F' description='ORDER_CANCEL_REQUEST'/>
<value enum='G' description='ORDER_CANCEL_REPLACE_REQUEST'/>
<value enum='H' description='ORDER_STATUS_REQUEST'/>
<value enum='J' description='ALLOCATION_INSTRUCTION'/>
<value enum='K' description='LIST_CANCEL_REQUEST'/>
<value enum='L' description='LIST_EXECUTE'/>
<value enum='M' description='LIST_STATUS_REQUEST'/>
<value enum='N' description='LIST_STATUS'/>
<value enum='P' description='ALLOCATION_INSTRUCTION_ACK'/>
<value enum='Q' description='DONT_KNOW_TRADE'/>
<value enum='R' description='QUOTE_REQUEST'/>
<value enum='S' description='QUOTE'/>
<value enum='T' description='SETTLEMENT_INSTRUCTIONS'/>
<value enum='V' description='MARKET_DATA_REQUEST'/>
<value enum='W' description='MARKET_DATA_SNAPSHOT_FULL_REFRESH'/>
<value enum='X' description='MARKET_DATA_INCREMENTAL_REFRESH'/>
<value enum='Y' description='MARKET_DATA_REQUEST_REJECT'/>
<value enum='Z' description='QUOTE_CANCEL'/>
<value enum='a' description='QUOTE_STATUS_REQUEST'/>
<value enum='b' description='MASS_QUOTE_ACKNOWLEDGEMENT'/>
<value enum='c' description='SECURITY_DEFINITION_REQUEST'/>
<value enum='d' description='SECURITY_DEFINITION'/>
<value enum='e' description='SECURITY_STATUS_REQUEST'/>
<value enum='f' description='SECURITY_STATUS'/>
<value enum='g' description='TRADING_SESSION_STATUS_REQUEST'/>
<value enum='h' description='TRADING_SESSION_STATUS'/>
<value enum='i' description='MASS_QUOTE'/>
<value enum='j' description='BUSINESS_MESSAGE_REJECT'/>
<value enum='k' description='BID_REQUEST'/>
<value enum='l' description='BID_RESPONSE'/>
<value enum='m' description='LIST_STRIKE_PRICE'/>
<value enum='n' description='XML_MESSAGE'/>
<value enum='o' description='REGISTRATION_INSTRUCTIONS'/>
<value enum='p' description='REGISTRATION_INSTRUCTIONS_RESPONSE'/>
<value enum='q' description='ORDER_MASS_CANCEL_REQUEST'/>
<value enum='r' description='ORDER_MASS_CANCEL_REPORT'/>
<value enum='s' description='NEW_ORDER_CROSS'/>
<value enum='t' description='CROSS_ORDER_CANCEL_REPLACE_REQUEST'/>
<value enum='u' description='CROSS_ORDER_CANCEL_REQUEST'/>
<value enum='v' description='SECURITY_TYPE_REQUEST'/>
<value enum='w' description='SECURITY_TYPES'/>
<value enum='x' description='SECURITY_LIST_REQUEST'/>
<value enum='y' description='SECURITY_LIST'/>
<value enum='z' description='DERIVATIVE_SECURITY_LIST_REQUEST'/>
<value enum='AA' description='DERIVATIVE_SECURITY_LIST'/>
<value enum='AB' description='NEW_ORDER_MULTILEG'/>
<value enum='AC' description='MULTILEG_ORDER_CANCEL_REPLACE'/>
<value enum='AD' description='TRADE_CAPTURE_REPORT_REQUEST'/>
<value enum='AE' description='TRADE_CAPTURE_REPORT'/>
<value enum='AF' description='ORDER_MASS_STATUS_REQUEST'/>
<value enum='AG' description='QUOTE_REQUEST_REJECT'/>
<value enum='AH' description='RFQ_REQUEST'/>
<value enum='AI' description='QUOTE_STATUS_REPORT'/>
<value enum='AJ' description='QUOTE_RESPONSE'/>
<value enum='AK' description='CONFIRMATION'/>
<value enum='AL' description='POSITION_MAINTENANCE_REQUEST'/>
<value enum='AM' description='POSITION_MAINTENANCE_REPORT'/>
<value enum='AN' description='REQUEST_FOR_POSITIONS'/>
<value enum='AO' description='REQUEST_FOR_POSITIONS_ACK'/>
<value enum='AP' description='POSITION_REPORT'/>
<value enum='AQ' description='TRADE_CAPTURE_REPORT_REQUEST_ACK'/>
<value enum='AR' description='TRADE_CAPTURE_REPORT_ACK'/>
<value enum='AS' description='ALLOCATION_REPORT'/>
<value enum='AT' description='ALLOCATION_REPORT_ACK'/>
<value enum='AU' description='CONFIRMATION_ACK'/>
<value enum='AV' description='SETTLEMENT_INSTRUCTION_REQUEST'/>
<value enum='AW' description='ASSIGNMENT_REPORT'/>
<value enum='AX' description='COLLATERAL_REQUEST'/>
<value enum='AY' description='COLLATERAL_ASSIGNMENT'/>
<value enum='AZ' description='COLLATERAL_RESPONSE'/>
<value enum='BA' description='COLLATERAL_REPORT'/>
<value enum='BB' description='COLLATERAL_INQUIRY'/>
<value enum='BC' description='NETWORK_STATUS_REQUEST'/>
<value enum='BD' description='NETWORK_STATUS_RESPONSE'/>
<value enum='BE' description='USER_REQUEST'/>
<value enum='BF' description='USER_RESPONSE'/>
<value enum='BG' description='COLLATERAL_INQUIRY_ACK'/>
<value enum='BH' description='CONFIRMATION_REQUEST'/>
<value enum='BI' description='TRADING_SESSION_LIST_REQUEST'/>
<value enum='BJ' description='TRADING_SESSION_LIST'/>
<value enum='BK' description='SECURITY_LIST_UPDATE_REPORT'/>
<value enum='BL' description='ADJUSTED_POSITION_REPORT'/>
<value enum='BM' description='ALLOCATION_INSTRUCTION_ALERT'/>
<value enum='BN' description='EXECUTION_ACKNOWLEDGEMENT'/>
<value enum='BO' description='CONTRARY_INTENTION_REPORT'/>
<value enum='BP' description='SECURITY_DEFINITION_UPDATE_REPORT'/>
</field>
<field name='NewSeqNo' number='36' type='SEQNUM'/>
<field name='PossDupFlag' number='43' type='BOOLEAN'/>
<field name='RefSeqNum' number='45' type='SEQNUM'/>
<field name='SenderCompID' number='49' type='STRING'/>
<field name='SenderSubID' number='50' type='STRING'/>
<field name='SendingTime' number='52' type='UTCTIMESTAMP'/>
<field name='TargetCompID' number='56' type='STRING'/>
<field name='TargetSubID' number='57' type='STRING'/>
<field name='Text' number='58' type='STRING'/>
<field name='Signature' number='89' type='DATA'/>
<field name='SecureDataLen' number='90' type='LENGTH'/>
<field name='SecureData' number='91' type='DATA'/>
<field name='SignatureLength' number='93' type='LENGTH'/>
<field name='RawDataLength' number='95' type='LENGTH'/>
<field name='RawData' number='96' type='DATA'/>
<field name='PossResend' number='97' type='BOOLEAN'/>
<field name='EncryptMethod' number='98' type='INT'>
<value description='NONE_OTHER' enum='0'/>
<value description='PKCS' enum='1'/>
<value description='DES' enum='2'/>
<value description='PKCS_DES' enum='3'/>
<value description='PGP_DES' enum='4'/>
<value description='PGP_DES_MD5' enum='5'/>
<value description='PEM_DES_MD5' enum='6'/>
</field>
<field name='HeartBtInt' number='108' type='INT'/>
<field name='TestReqID' number='112' type='STRING'/>
<field name='OnBehalfOfCompID' number='115' type='STRING'/>
<field name='OnBehalfOfSubID' number='116' type='STRING'/>
<field name='OrigSendingTime' number='122' type='UTCTIMESTAMP'/>
<field name='GapFillFlag' number='123' type='BOOLEAN'/>
<field name='DeliverToCompID' number='128' type='STRING'/>
<field name='DeliverToSubID' number='129' type='STRING'/>
<field name='ResetSeqNumFlag' number='141' type='BOOLEAN'/>
<field name='SenderLocationID' number='142' type='STRING'/>
<field name='TargetLocationID' number='143' type='STRING'/>
<field name='OnBehalfOfLocationID' number='144' type='STRING'/>
<field name='DeliverToLocationID' number='145' type='STRING'/>
<field name='XmlDataLen' number='212' type='LENGTH'/>
<field name='XmlData' number='213' type='DATA'/>
<field number='347' name='MessageEncoding' type='STRING'>
<value enum='ISO-2022-JP' description='ISO_2022_JP'/>
<value enum='EUC-JP' description='EUC_JP'/>
<value enum='SHIFT_JIS' description='SHIFT_JIS'/>
<value enum='UTF-8' description='UTF_8'/>
</field>
<field name='EncodedTextLen' number='354' type='LENGTH'/>
<field name='EncodedText' number='355' type='DATA'/>
<field name='LastMsgSeqNumProcessed' number='369' type='SEQNUM'/>
<field name='RefTagID' number='371' type='INT'/>
<field name='RefMsgType' number='372' type='STRING'/>
<field name='SessionRejectReason' number='373' type='INT'>
<value description='INVALID_TAG_NUMBER' enum='0'/>
<value description='REQUIRED_TAG_MISSING' enum='1'/>
<value description='SENDINGTIME_ACCURACY_PROBLEM' enum='10'/>
<value description='INVALID_MSGTYPE' enum='11'/>
<value description='XML_VALIDATION_ERROR' enum='12'/>
<value description='TAG_APPEARS_MORE_THAN_ONCE' enum='13'/>
<value description='TAG_SPECIFIED_OUT_OF_REQUIRED_ORDER' enum='14'/>
<value description='REPEATING_GROUP_FIELDS_OUT_OF_ORDER' enum='15'/>
<value description='INCORRECT_NUMINGROUP_COUNT_FOR_REPEATING_GROUP' enum='16'/>
<value description='NON_DATA_VALUE_INCLUDES_FIELD_DELIMITER' enum='17'/>
<value description='TAG_NOT_DEFINED_FOR_THIS_MESSAGE_TYPE' enum='2'/>
<value description='UNDEFINED_TAG' enum='3'/>
<value description='TAG_SPECIFIED_WITHOUT_A_VALUE' enum='4'/>
<value description='VALUE_IS_INCORRECT' enum='5'/>
<value description='INCORRECT_DATA_FORMAT_FOR_VALUE' enum='6'/>
<value description='DECRYPTION_PROBLEM' enum='7'/>
<value description='SIGNATURE_PROBLEM' enum='8'/>
<value description='COMPID_PROBLEM' enum='9'/>
<value description='OTHER' enum='99'/>
</field>
<field name='MaxMessageSize' number='383' type='LENGTH'/>
<field name='NoMsgTypes' number='384' type='NUMINGROUP'/>
<field name='MsgDirection' number='385' type='CHAR'>
<value description='RECEIVE' enum='R'/>
<value description='SEND' enum='S'/>
</field>
<field name='TestMessageIndicator' number='464' type='BOOLEAN'/>
<field name='Username' number='553' type='STRING'/>
<field name='Password' number='554' type='STRING'/>
<field name='NoHops' number='627' type='NUMINGROUP'/>
<field name='HopCompID' number='628' type='STRING'/>
<field name='HopSendingTime' number='629' type='UTCTIMESTAMP'/>
<field name='HopRefID' number='630' type='SEQNUM'/>
<field name='NextExpectedMsgSeqNum' number='789' type='SEQNUM'/>
<field name='ApplVerID' number='1128' type='STRING'>
<value description='FIX27' enum='0'/>
<value description='FIX30' enum='1'/>
<value description='FIX40' enum='2'/>
<value description='FIX41' enum='3'/>
<value description='FIX42' enum='4'/>
<value description='FIX43' enum='5'/>
<value description='FIX44' enum='6'/>
<value description='FIX50' enum='7'/>
<value description='FIX50SP1' enum='8'/>
<value description='FIX50SP2' enum='9'/>
</field>
<field name='CstmApplVerID' number='1129' type='STRING'/>
<field name='RefApplVerID' number='1130' type='STRING'/>
<field name='RefCstmApplVerID' number='1131' type='STRING'/>
<field name='DefaultApplVerID' number='1137' type='STRING'/>
</fields>
</fix>
+88
View File
@@ -0,0 +1,88 @@
# BTS Spark Client SDK
#### Getting Started
## Git
* First clone the spark_client_sdk repo
```
> git clone https://github.com/BlueTradingSystems/spark_client_sdk.git
```
## Build
* Use msbuild to target windows / .net 4.71
* Install studio here if you don't have it: https://visualstudio.microsoft.com/downloads/
* Use VSCode for editing, it's better: https://code.visualstudio.com/
```
> cd spark_client_sdk\src\spark_sdk
spark_client_sdk\src\spark_sdk> msbuild /t:restore
spark_client_sdk\src\spark_sdk> msbuild
```
* spark_sdk binaries will be produced in the bin\Debug directory
## Connecting to Spark Server
* Start your exe using the -ConfigServer parameter to point at a running Spark Server
```
> spark_sdk.exe -ConfigServer http://location:8000/config
```
Or create a file called spark.conf at %APPDATA%\bts\conf\ that contains:
```
include: "http://location:8000/config/"
```
## Examples
* Make sure you can connect to a running server
* run spark_sdk.exe with no parameters to see help
```
spark_sdk\bin\Debug\net471> spark_sdk.exe
BTS Spark SDK
Usages: spark_sdk [command] [arguments]
Commands:
md <exchange> <security_type> <symbol> [<security_desc>]
ui <exchange> <security_type> <symbol> <security_desc> <poll_interval_in_ms>
order <exchange> <security_type> <symbol> <security_desc> <signed_quantity> [<price>]
Arguments:
--pump=<pump_interval_in_ms=100 default>
```
* Check out sample code
* [Market Data](src/spark_sdk/samples/MarketDataSample.cs)
* [Orders](src/spark_sdk/samples/OrderSample.cs)
* [Driver](src/spark_sdk/samples/SDKMain.cs)
## Excel
```
spark_sdk\bin\Debug\net471> start spark_excel-addin64-packed.xll
```
* This will start excel and load the bts addin
* Check the [```excel```](excel/) directory for an example spreadsheet
## Notes
* Our client side API is based on Google's protocol buffers; the message types are all protos. https://developers.google.com/protocol-buffers/
* We extended the protoc compiler to generate c#, c++, excel and js bindings to the services provided by our server process (https://github.com/BlueTradingSystems/spark_sdk)
* ### Dotnet core
* Right now we only support c# Windows 10 clients, but the package targets both net471 and .net core 2.1.6, so in theory one could run a .net core or c++ client on linux with a little work.
* https://dotnet.microsoft.com/
```
spark_client_sdk\src\spark_sdk> dotnet build
...> dotnet spark_sdk.dll
```
* Send us an email for help: api_support@bluetradesys.com
+3 -10
View File
@@ -26,16 +26,9 @@ namespace bts.sdk
_md = client;
}
readonly static Lazy<OrderService> _orders = new Lazy<OrderService>(() => locate<OrderService>(), true);
readonly static Lazy<UserService> _users = new Lazy<UserService>(() => locate<UserService>(), true);
readonly static Lazy<AdminService> _admin = new Lazy<AdminService>(() => locate<AdminService>(), true);
static T locate<T>() where T : class, IApiClient
{
using (var ctx = new api.core.ServiceContext())
{
return ctx.locate<T>();
}
}
readonly static Lazy<OrderService> _orders = new Lazy<OrderService>(() => Locator.locate<OrderService>(), true);
readonly static Lazy<UserService> _users = new Lazy<UserService>(() => Locator.locate<UserService>(), true);
readonly static Lazy<AdminService> _admin = new Lazy<AdminService>(() => Locator.locate<AdminService>(), true);
void print_update<T>(T update)
{
+55 -38
View File
@@ -13,56 +13,73 @@ namespace bts.sdk
public class SDKMain
{
private static void user_test()
{
var ctx = new ServiceContext();
var s = ctx.locate<api.user.UserService>();
var r = new api.user.GetUsersRequest();
var resp = s.get_users(r);
Console.WriteLine("{0}", resp);
}
public static void Main(string[] args)
{
MarketDataClient client = new MarketDataClient();
CompositeDisposable disp = new CompositeDisposable(client);
if (args.FirstOrDefault() == "ui")
{
if (args.Length >= 6)
{
using (var ui = new ui.UISample(client))
{
ui.simple_price_ui(args[1], args[2], args[3], args[4], args[5]);
}
}
else
ui_help();
}
else if (args.Length > 0 && args.FirstOrDefault().Contains("md"))
Log.ClientSdk.info("Welcome to the BTS Client Side SDK example");
if (args.FirstOrDefault() == "users")
{
var s = new MarketDataSample(client);
if (args.FirstOrDefault() == "md")
user_test();
}
else
{
MarketDataClient client = new MarketDataClient();
CompositeDisposable disp = new CompositeDisposable(client);
if (args.FirstOrDefault() == "ui")
{
if (args.Length >= 5)
if (args.Length >= 6)
{
s.print_security(args[1], args[2], args[3], args[4]);
using (var ui = new ui.UISample(client))
{
ui.simple_price_ui(args[1], args[2], args[3], args[4], args[5]);
}
}
else if (args.Length >= 4)
else
ui_help();
}
else if (args.Length > 0 && args.FirstOrDefault().Contains("md"))
{
var s = new MarketDataSample(client);
if (args.FirstOrDefault() == "md")
{
s.print_channel(args[1], args[2], args[3]);
if (args.Length >= 5)
{
s.print_security(args[1], args[2], args[3], args[4]);
}
else if (args.Length >= 4)
{
s.print_channel(args[1], args[2], args[3]);
}
else
md_help();
}
else
md_help();
}
}
else if (args.FirstOrDefault() == "order")
{
OrderSample os = new OrderSample(client);
disp.Add(os);
order_sample(os, args);
}
else
md_help();
}
else if (args.FirstOrDefault() == "order")
{
OrderSample os = new OrderSample(client);
disp.Add(os);
order_sample(os, args);
}
else
help();
help();
int ms = 100;
string p = args.FirstOrDefault(x => x.Contains("--pump="));
if (p != null)
ms = int.Parse(p.Replace("--pump=", string.Empty));
client.run_pump(TimeSpan.FromMilliseconds(ms));
wait_for_stop(disp);
int ms = 100;
string p = args.FirstOrDefault(x => x.Contains("--pump="));
if (p != null)
ms = int.Parse(p.Replace("--pump=", string.Empty));
client.run_pump(TimeSpan.FromMilliseconds(ms));
wait_for_stop(disp);
}
}
static async void order_sample(OrderSample os, string[] args)
+53 -8
View File
@@ -1,9 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net471</TargetFrameworks>
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks>
<OutputType>Exe</OutputType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GeneratePackageOnBuild Condition="$(Configuration) == 'Release' or $(Configuration) == 'Release'">true</GeneratePackageOnBuild>
<PackageOutputPath>$(OutputPath)\net471</PackageOutputPath>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
@@ -41,17 +40,63 @@
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<!-- <PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
-->
<!-- $(SparkRoot) is something we define on our build at BTS, if it's empty, this project file is in the wild-->
<ItemGroup Condition= " '$(SparkRoot)' != '' ">
<ProjectReference Include="../spark_api/spark_api.csproj"/>
</ItemGroup>
<ItemGroup Condition= " '$(SparkRoot)' != '' and '$(TargetFramework)' == 'net471' ">
<!-- This is so we can ship excel in the nupkg -->
<ProjectReference Include="../spark_excel/spark_excel.csproj"/>
</ItemGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup Condition= " '$(SparkRoot)' != '' ">
<None Include="README.md" pack="true" PackagePath="." />
<None Include="$(OutputPath)\net471\spark_excel_example_2018.xlsm" pack="true" PackagePath=".\excel\spark_excel_example_2018.xlsm" />
<None Include="$(OutputPath)\net471\spark_excel-addin64-packed.xll" pack="true" PackagePath=".\excel\spark_excel-addin64-packed.xll" />
<None Include="$(OutputPath)\net471\spark_excel-addin64.dna" pack="true" PackagePath=".\excel\spark_excel-addin64.dna" />
</ItemGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences" Condition= " '$(SparkRoot)' != '' ">
<ItemGroup>
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
<BuildOutputInPackage Include="$(OutputPath)..\net471\libspark.dll"/>
</ItemGroup>
</Target>
<Target Name="copy_scripts" AfterTargets="PostBuildEvent" Condition= " '$(SparkRoot)' != '' " >
<Copy SourceFiles="../../sdk/update_github.cmd" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
</Target>
<!-- deployed sdk build net471 -->
<ItemGroup Condition= " '$(SparkRoot)' == '' and '$(TargetFramework)' == 'net471'">
<Reference Include="spark_api">
<HintPath>..\..\lib\net471\spark_api.dll</HintPath>
</Reference>
</ItemGroup>
<!-- deployed sdk build netcoreapp2.1 -->
<ItemGroup Condition= " '$(SparkRoot)' == '' and '$(TargetFramework)' == 'netcoreapp2.1'">
<Reference Include="spark_api">
<HintPath>..\..\lib\netcoreapp2.1\spark_api.dll</HintPath>
</Reference>
</ItemGroup>
<Target Name="copy_lib_deployed" AfterTargets="PostBuildEvent" Condition= " '$(SparkRoot)' == '' " >
<Copy SourceFiles="..\..\lib\net471\libspark.dll" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
</Target>
<Target Name="copy_lib_deployed471" AfterTargets="PostBuildEvent" Condition= " '$(SparkRoot)' == '' and '$(TargetFramework)' == 'net471'" >
<Copy SourceFiles="..\..\lib\net471\spark_excel.dll" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
<Copy SourceFiles="..\..\excel\spark_excel_example_2018.xlsm" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
<Copy SourceFiles="..\..\excel\spark_excel-addin64-packed.xll" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
<Copy SourceFiles="..\..\excel\spark_excel-addin64.dna" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
</Target>
</Project>
+2 -1
View File
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<Platform>x64</Platform>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
@@ -1,4 +1,4 @@
using ManyConsole;
using ManyConsole.CommandLineUtils;
using System.Linq;
namespace bts {
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ManyConsole.CommandLineUtils {
public static class ConsoleCommandEx {
public static string get_argument(this ConsoleCommand _, string arg_name) {
var arg = Environment.GetCommandLineArgs().FirstOrDefault(str => str.StartsWith("--"+arg_name));
if (arg != null) {
var parts = arg.Split(new[] { '=' }, 2);
if (parts.Length > 1) {
return parts[1];
}
}
return "";
}
}
}
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Linq;
using ManyConsole.CommandLineUtils;
using bts.fix;
namespace bts
{
public class FIXCommand : ConsoleCommand
{
bool _initiator, _orders, _dropcopy;
string _config, _http;
public FIXCommand()
{
IsCommand("fix", "start a fix acceptor or initiator session");
HasRequiredOption<string>("f|configfile=", "Set the config file", h => _config = h);
HasOption<string>("i|initiator", "Start a FIX Initiator", h => _initiator = true);
HasOption<string>("w|http=", "Start http server {address}", h => _http = h);
HasOption<string>("o|order_session", "Start a FIX Acceptor session for orders", h => _orders = true);
HasOption<string>("d|dropcopy_only", "Start a FIX Acceptor session for dropcopy", h => _dropcopy = true);
//HasAdditionalArguments(null, " [other params]");
}
public override int Run(string[] remaining_args)
{
if (_orders)
{
Console.WriteLine("Starting FIX ORDERS Acceptor using " + _config);
if(!string.IsNullOrEmpty(_http))
Console.WriteLine(" Http server " + _http);
return FixInit.start_acceptor(_config, true, (string.IsNullOrEmpty(_http) ? null : _http));
}
else if (_dropcopy)
{
Console.WriteLine("Starting FIX DROP Acceptor using " + _config);
if (!string.IsNullOrEmpty(_http))
Console.WriteLine(" Http server " + _http);
return FixInit.start_acceptor(_config, false, (string.IsNullOrEmpty(_http) ? null : _http));
}
else if (_initiator)
{
Console.WriteLine("Initiator not supported yet");
}
Console.WriteLine("Must select either orders or dropcopy session");
return -1;
}
}
}
@@ -6,7 +6,7 @@ using System.Reflection;
using System.Threading.Tasks;
using System.Reactive.Linq;
using System.Reactive;
using ManyConsole;
using ManyConsole.CommandLineUtils;
using System.Linq;
namespace sparkcmd {
+78
View File
@@ -0,0 +1,78 @@
using bts.api.core;
using bts.test;
using ManyConsole.CommandLineUtils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bts {
class TailService : ConsoleCommand {
static bool _all = false;
static bool _timestamp = false;
public TailService() {
void ignore(string _) { }
IsCommand("tailservice", "Tail updates to named service api. Ex: tail --method=SyntheticSecurityService.metadata");
HasRequiredOption("method", "Service.method to tail", ignore);
HasOption("time", "Include timestamp", _ => _timestamp = true);
// The following are not implemented. If you need them get to work.
HasOption("path", "Path inside the row to use for display. Ex: 'Id' or 'Name' ", ignore);
HasOption("all", "Invlude 'all' rows on every print", _ => _all = true);
}
public override int Run(string[] remainingArguments) {
var arg = this.get_argument("method");
var split = arg.Split('.');
if (split.Length != 2) {
Console.WriteLine("Try --method=[ServiceName].[Method]");
return -1;
}
tail(split[0], split[1]);
return 0;
}
class SubscriptionLogger<T> : IObserver<T> {
public void OnCompleted() {
ConsoleEx.WriteLine("Completed.", ConsoleColor.Yellow);
}
public void OnError(Exception error) {
ConsoleEx.WriteLine($"Error. '{error}'", ConsoleColor.Red);
}
public void OnNext(T value) {
if (_timestamp) {
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
Console.Write(" ");
}
if (_all) { }
Console.WriteLine(value);
}
}
private static void tail(string service_name, string method_name) {
var spark_api = typeof(ServiceContext).Assembly;
var service_type = spark_api.GetExportedTypes().FirstOrDefault(t => t.Name == service_name);
if (service_type == null) {
Console.WriteLine($"Could not find service type {service_name} in spark_api.dll");
return;
}
var subscribe_method = service_type.GetMethod("subscribei_" + method_name);
using (var ctx = new ServiceContext()) {
var locate = typeof(ServiceContext).GetMethod("locate").MakeGenericMethod(service_type);
var service = locate.Invoke(ctx, new[] { "" });
// Subscribe with null qualifier
object observable = subscribe_method.Invoke(service, new object[] { null });
Type update_type = observable.GetType().GetGenericArguments().First(arg => arg.Name.StartsWith("Update"));
Type observer_type = typeof(SubscriptionLogger<>).MakeGenericType(update_type);
object observer = Activator.CreateInstance(observer_type);
var subscribe = observable.GetType().GetMethod("Subscribe", new[] { observer_type });
var dispose = subscribe.Invoke(observable, new[] { observer });
while (true) {
System.Threading.Thread.Sleep(1000);
}
}
}
}
}
@@ -1,8 +1,10 @@
using bts.api;
using bts.api.core;
using bts.api.synthetic;
using ManyConsole;
using bts.test;
using ManyConsole.CommandLineUtils;
using System;
using System.Linq;
namespace bts {
public class ServiceTest : ConsoleCommand {
@@ -28,6 +30,8 @@ namespace bts {
}
return 0;
}
private static void synthetic_test() {
using (var ctx = new ServiceContext()) {
var security = new Security {
@@ -1,4 +1,4 @@
using ManyConsole;
using ManyConsole.CommandLineUtils;
using System.Reflection;
namespace bts {
+1 -1
View File
@@ -1,6 +1,6 @@
using bts.test;
using ManyConsole;
using ManyConsole.CommandLineUtils;
using System;
using mbts;
using System.Collections.Generic;
+1 -1
View File
@@ -2,7 +2,7 @@
using bts.api.core;
using bts.api.synthetic;
using bts.api.test;
using ManyConsole;
using ManyConsole.CommandLineUtils;
using System;
using System.Linq;
+8 -6
View File
@@ -2,9 +2,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net471</TargetFramework>
<!--<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>-->
<RuntimeIdentifiers>win7-x64;ubuntu.16.04-x64</RuntimeIdentifiers>
<!-- <TargetFramework>net471</TargetFramework> -->
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
<RuntimeIdentifiers>win10-x64;ubuntu.18.04-x64</RuntimeIdentifiers>
<Authors>Blue Trading Systems</Authors>
<Company>Blue Trading Systems</Company>
<Product>Spark</Product>
@@ -18,11 +18,13 @@
<ProjectReference Include="../spark_api/spark_api.csproj" />
<!--<ProjectReference Include="..\spark\Spark.csproj" />-->
<ProjectReference Include="..\spark_core\spark_core.csproj" />
<ProjectReference Include="..\spark_test\spark_test.csproj" Condition="'$(Configuration)'=='Debug'" />
<ProjectReference Include="..\spark_test\spark_test.csproj" Condition="'$(Configuration)'=='Debug' " />
<ProjectReference Include="../spark_fix/spark_fix.csproj" />
<!--<Proto3 Include="**/*.proto" />-->
</ItemGroup>
<ItemGroup>
<PackageReference Include="ManyConsole" Version="1.0.0.4" />
<!-- <PackageReference Include="ManyConsole" Version="1.0.0.4" /> -->
<PackageReference Include="ManyConsole.CommandLineUtils" Version="1.0.3-alpha" />
</ItemGroup>
</Project>
+1 -7
View File
@@ -1,12 +1,6 @@
Session ID Access ID Secret Key Creation Date Expiration Date Environment
1U7 i9pLY5mVhWLXs7FWNb9X 8esbwiaHaqmR2rXZMkJRHt00fW6l_-ppCGoheWWuBZQ 2018-04-05 2019-04-05 CERT
1U7 ozXmORijPtJriTg6Wwny A1KOwrhH8GojNotjkCfVwHRSi_OS280xihaRZwJLFKo 2018-04-03 2018-05-03 CERT
ANS 6ogfYoF54g695tqaHsFC CtDkFnuDuStB1S05nC_pWvANb6FWsV2IMNKkmM3EcP4 2018-04-05 2019-04-05 NEWRELEASE
KTK IrUp518ekr8SzbfJGdPZ QmilC-cKgqlIiIBs_BV59fowbPfuBWn0QjeEc1dYj38 2018-04-05 2019-04-05 NEWRELEASE
M5Y bUFtyvZgPhZFCYa434ap rfZ2XLLwtHt26szWwMxYRw9uPb7jE4evEWaKuKf2uyU 2018-04-05 2019-04-05 CERT
MBX tbna9JLxT4Jlrzj0wtSS EcYqrLmR4ysSIT_NCBeC2AjDo7IAHsVZ0rOZ0cpAh_o 2018-04-05 2019-04-05 CERT
MC1 0N10hKkE2B2KUt86ewd8 dJ5I52JNA-6aZZ8hnO0byVKcyqA8Of6kkstfIF89AAM 2018-04-05 2019-04-05 CERT
MC5 e4uBmhQ1ebaXAeWHa7HK PNOM86eofsiCwzzKUsB5yAmrNoyZypnYD72pT2A8zgc 2018-04-05 2019-04-05 CERT
MC7 S6KfCnay5UJ67P55ibHd 5lSs1TbX7N0p6Duu63_JDMghPUqBAWacvkM91MUv1uM 2018-04-05 2019-04-05 CERT
MC8 AbEEtBn63WIAuItPDz1c n3ndjYb03Hx6wBO3me35HoONyDgO5TZKWk0tDSaX9KA 2018-04-05 2019-04-05 CERT
T39 UWggBgHWAe07aqW3ps2I tbb3it0rUQSSTa0TC9QcRwkrHEMDLWhaJH4VQ0FqGB4 2018-04-05 2019-04-05 NEWRELEASE
V06 WOC3GVA6l8ffrcOKUT9P Az1TJc2XHJyZ4C0S1bG_p5eWPQlVYUYhfjmdPqSGJ8o 2018-11-26 2019-11-26 NEWRELEASE
+1 -1
View File
@@ -1 +1 @@
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/thread_manager.cpp)
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/thread_manager.cpp)
+20 -4
View File
@@ -54,11 +54,27 @@ if(NOT old_config_proto_str STREQUAL config_proto_str)
file(WRITE "${config_proto_file}" "${config_proto_str}")
endif()
# copy defaults to output directory
# generate static defaults
set(config_defaults_file ${CMAKE_CURRENT_BINARY_DIR}/config_defaults.h)
# Defaults cannot be a single string because the compiler complains about a string longer than 65k
set(config_defaults_str "const char * config_static_defaults[] = { ")
foreach (cfg_file_path ${Conf_Defaults})
get_filename_component(cfg_file_name ${cfg_file_path} NAME)
configure_file(${cfg_file_path} ${CMAKE_BINARY_DIR}/src/server/sparkd/defaults/${cfg_file_name})
file(READ ${cfg_file_path} cfg_file_content)
string(REPLACE "\"" "\\\"" cfg_file_content_escaped ${cfg_file_content})
string(REPLACE "\n" ";" cfg_lines ${cfg_file_content_escaped})
foreach(line ${cfg_lines})
string(APPEND config_defaults_str "\n \"${line}\",")
endforeach(line)
endforeach(cfg_file_path)
string(APPEND config_defaults_str "};")
set(old_defaults_str "")
if(EXISTS ${config_defaults_file})
file(READ ${config_defaults_file} old_defaults_str)
endif()
if(NOT config_defaults_str STREQUAL old_defaults_str)
file(WRITE "${config_defaults_file}" "${config_defaults_str}")
endif()
# lib
if (WIN32)
@@ -125,4 +141,4 @@ if (WIN32)
include(${CMAKE_SOURCE_DIR}/tools/vs_filters.cmake)
endif()
+1
View File
@@ -10,6 +10,7 @@ message GatewayLoginResult {
bool is_valid = 1;
string reason = 2;
string token = 3;
bool can_create = 4;
}
message GatewayCallHeader {
int32 request_id = 1;
@@ -25,14 +25,9 @@ namespace {{package}}
// {{comment}}
public partial class {{name}}_Excel : IDisposable
{
readonly static Lazy<{{name}}> _service = new Lazy<{{name}}>(() => locate<{{name}}>(), true);
static T locate<T>() where T : class, IApiClient
{
using (var ctx = new api.core.ServiceContext())
{
return ctx.locate<T>();
}
}
readonly static object _lock = new object();
readonly static Lazy<{{name}}> _service = new Lazy<{{name}}>(() => bts.api.Locator.locate<{{name}}>(), true);
public void Dispose()
{
@@ -80,6 +75,54 @@ public void Dispose()
{{#unless skip_excel_generation}}
[ExcelFunction(Category = "BTS", Description = "Call BTS Spark {{../lname}} API Method {{lname}} with parameter {{input_type_full}}")]
async public static void spark_{{../lname}}_{{lname}}_async (object[,] parameters, ExcelAsyncHandle handle, bool show_headers = false, bool display_vertically = false)
{
try
{
Google.Protobuf.IMessage req = null;
{{input_type_full}} casted_req = null;
try
{
if(parameters.Rank == 2 && parameters.GetLength(0) == 1 && parameters.GetLength(1) == 1)
{
if(parameters[0,0] == ExcelMissing.Value)
req = (Google.Protobuf.IMessage) Activator.CreateInstance(typeof({{input_type_full}}));
else if(parameters[0,0] is string json)
req = APIDispatch.generate_imessage(typeof({{input_type_full}}), json);
}
if(req == null)
{
string json = JsonFormatter.parse_json(typeof({{input_type_full}}), parameters);
req = APIDispatch.generate_imessage(typeof({{input_type_full}}), json);
}
casted_req = ({{input_type_full}}) req;
}
catch
{
var members = bts.excel.extensions.ReflectionExtensions
.get_fields_or_properties(typeof({{input_type_full}}))
.Where(x => !DataParser.excluded_member(x));
handle.SetResult(
"Error in calling method {{lname}}( {{input_type_full}} ), input members are: " +
string.Join(",", members.Select(x => x.Name).ToArray())
);
}
var res = await _service.Value.{{name}}_async( casted_req );
object res_res = res.value;
object[,] scattered = new JsonFormatter(res_res).format(show_headers);
if(display_vertically)
scattered = bts.excel.Utilities.transpose_keep_dimension(scattered);
handle.SetResult(scattered);
}
catch(Exception ex)
{
Trace.TraceError(ex.ToString());
handle.SetException(ex);
}
}
[ExcelFunction(Category = "BTS", Description = "Call BTS Spark {{../lname}} API Method {{lname}} with parameter {{input_type_full}}")]
public static object spark_{{../lname}}_{{lname}} (object[,] parameters, bool show_headers = false, bool display_vertically = false)
{
@@ -113,7 +156,7 @@ public void Dispose()
.Where(x => !DataParser.excluded_member(x));
return "Error in calling method {{lname}}( {{input_type_full}} ), input members are: " +
string.Join(",", members.Select(x => x.Name).ToArray());
}
}
{{/unless}}
{{/if}}
@@ -846,12 +846,35 @@ void AdminServiceUtils::find_user_trading_accounts (const bts::api::admin::Admin
const bts::api::admin::UserAccountPermissions* user_perm_ptr = get_user_permissions (admin_data, user_id);
if (user_perm_ptr != nullptr)
for(auto it=user_perm_ptr->account_permissions().begin(); it!=user_perm_ptr->account_permissions().end(); it++)
if (it->second.has_trading_permission()){
if (it->second.has_trading_permission() && it->second.trading_permission().has_permissions()){
const api::admin::BTSAccount* account_ptr = find_account(admin_data, it->first);
if (account_ptr != nullptr)
associated_accounts.push_back(account_ptr);
}
}
// for a given account, fills in the associated_users ref arg
// with the users that can trade into the account
void AdminServiceUtils::find_users_for_trading_account (const AdminData& admin_data,
uint32_t account_id,
std::vector<const BTSUser*>& associated_users)
{
for(const auto& u : AdminServiceUtils::users(admin_data, false))
{
const auto user_perm_ptr = get_user_permissions (admin_data, u->id());
if (user_perm_ptr)
{
const auto& permission_unit = user_perm_ptr->account_permissions().find(account_id);
if(permission_unit != user_perm_ptr->account_permissions().end() && permission_unit->second.has_trading_permission() &&
permission_unit->second.trading_permission().has_permissions())
{
associated_users.push_back(u);
}
}
}
}
void AdminServiceUtils::validate_user_update (const bts::api::admin::AdminData& admin_data,
const bts::api::admin::BTSUser& updated_user,
bts::api::admin::AdminServiceOperationResult& result){
@@ -891,7 +914,7 @@ void AdminServiceUtils::get_md_entitlement_report(const AdminData& admin_data,
admin_data);
if(cme_data!=nullptr){
for (const auto& cme_mic_data : cme_data->md_permissions()){
MDEntitlementReportUnit rv=get_cme_md_entitlement_unit(user.name(),
MDEntitlementReportUnit rv=get_cme_md_entitlement_unit(user,
cme_mic_data);
entitlements.push_back(rv);
}
@@ -899,10 +922,11 @@ void AdminServiceUtils::get_md_entitlement_report(const AdminData& admin_data,
}
}
MDEntitlementReportUnit AdminServiceUtils::get_cme_md_entitlement_unit(
const std::string& user,
const BTSUser& user,
const CMEMicData& cme_mic_data){
MDEntitlementReportUnit rv;
rv.set_user(user);
rv.set_user(user.name());
rv.set_user_id(user.id());
rv.set_exchange("GLBX");
switch(cme_mic_data.mic())
{
@@ -974,6 +998,8 @@ bts::api::admin::BTSUser* AdminServiceUtils::create_user (bts::api::admin::Admin
uint32_t parent_group_id,
const std::string& name,
const std::string& password,
const std::string& full_name,
const std::string& company,
bool is_active,
const bts::api::admin::LicenseData& license_data,
const bts::api::admin::LicenseEntitlements& license_entitlements,
@@ -999,6 +1025,8 @@ bts::api::admin::BTSUser* AdminServiceUtils::create_user (bts::api::admin::Admin
new_user_ptr->set_parent_group_id(parent_group_id);
new_user_ptr->set_name(name);
new_user_ptr->set_password (password);
new_user_ptr->set_full_name (full_name);
new_user_ptr->set_company (company);
new_user_ptr->set_is_active(is_active);
new_user_ptr->mutable_license_data()->CopyFrom(license_data);
new_user_ptr->mutable_license_entitlements()->CopyFrom(license_entitlements);
@@ -1268,13 +1296,14 @@ void AdminServiceUtils::bootstrap_data (bts::api::admin::AdminData& data){
auto def_group = create_group(data, 0, std::string("default"), result);
// adding users to the group
bts::api::admin::LicenseData license_data;
bts::api::admin::LicenseEntitlements license_entitlements;
auto user_1 = create_user(data, def_group->id(), std::string("bts"), std::string(""), false, license_data, license_entitlements, user_result); user_1->set_is_active(true);
auto user_2 = create_user(data, def_group->id(), std::string("bts_2"), std::string("bts_2"), false, license_data, license_entitlements, user_result); user_2->set_is_active(true);
auto user_3 = create_user(data, def_group->id(), std::string("bts_3"), std::string("bts_3"), false, license_data, license_entitlements, user_result); user_3->set_is_active(true);
bts::api::admin::LicenseEntitlements license_entitlements; std::string empty("");
auto user_1 = create_user(data, def_group->id(), std::string("bts"), std::string(""), empty, empty, false, license_data, license_entitlements, user_result); user_1->set_is_active(true);
auto user_2 = create_user(data, def_group->id(), std::string("bts_2"), std::string("bts_2"), empty, empty, false, license_data, license_entitlements, user_result); user_2->set_is_active(true);
auto user_3 = create_user(data, def_group->id(), std::string("bts_3"), std::string("bts_3"), empty, empty, false, license_data, license_entitlements, user_result); user_3->set_is_active(true);
//associate users
bts::api::admin::UserAccountPermissionUnit permission_unit;
permission_unit.mutable_trading_permission()->set_submit_market_orders(true);
permission_unit.mutable_trading_permission()->set_has_permissions(true);
permission_unit.mutable_admin_permission()->set_has_permissions(true);
bts::api::admin::UserAccountPermissions account_permissions;
account_permissions.mutable_account_permissions()->insert(google::protobuf::MapPair<uint32_t, bts::api::admin::UserAccountPermissionUnit>(parent_account->id(), permission_unit));
@@ -217,6 +217,12 @@ public:
const AdminData& admin_data,
uint32_t user_id,
std::vector<const BTSAccount*>& associated_accounts);
// for a given account, fills in the associated_users ref arg
// with the users that can trade into the account
static void find_users_for_trading_account (
const AdminData& admin_data,
uint32_t account_id,
std::vector<const BTSUser*>& associated_users);
// validates that changes in user data satisfy all the
// constraints
static void validate_user_update (const AdminData& admin_data,
@@ -230,7 +236,7 @@ public:
std::vector<MDEntitlementReportUnit>& entitlements);
// Gets a reporting unit out of CMEMicData
static MDEntitlementReportUnit get_cme_md_entitlement_unit(
const std::string& user,
const BTSUser& user,
const CMEMicData& cme_mic_data);
static void get_user_symbol_permissions(
const AdminData& data,
@@ -243,6 +249,8 @@ public:
uint32_t parent_group_id,
const std::string& name,
const std::string& password,
const std::string& full_name,
const std::string& company,
bool is_active,
const LicenseData& license_data,
const LicenseEntitlements& license_entitlements,
+40 -9
View File
@@ -54,9 +54,12 @@ message BTSUser{
uint32 parent_group_id = 20;
string name = 30;
string password = 40;
string full_name = 42;
string company = 44;
bool is_active = 50;
LicenseData license_data = 60;
LicenseEntitlements license_entitlements = 70;
bool can_create = 55;
LicenseData license_data = 60; //unused
LicenseEntitlements license_entitlements = 70; //unused
BTSUserDerivableData user_data = 80;
}
@@ -89,21 +92,23 @@ message LicenseEntitlements{
}
message AlgoTradingPermissionsUnit{
bool submit_iceberg_orders = 10;
bool submit_time_box_orders = 20;
bool submit_spreader_orders = 30;
bool submit_iceberg_orders = 10; //unused
bool submit_time_box_orders = 20; //unused
bool submit_spreader_orders = 30; //unused
}
message TradingPermissionUnit{
bool submit_market_orders = 10;
bool submit_gtc_orders = 20;
AlgoTradingPermissionsUnit algo_trading_permission = 30;
bool has_permissions = 5;
bool submit_market_orders = 10; //unused
bool submit_gtc_orders = 20; //unused
AlgoTradingPermissionsUnit algo_trading_permission = 30; //unused
}
message AdminPermissionUnit{
// TODO: needs to be more ellaborate (account writing,
// reading etc, routing etc)
bool has_permissions = 10;
bool has_md_permissions = 20;
}
// this is the functionality permissions
@@ -113,6 +118,7 @@ message UserAccountPermissionUnit{
}
message UserAccountPermissions{
//uint32 = account_id
map<uint32, UserAccountPermissionUnit> account_permissions = 10;
}
@@ -283,6 +289,18 @@ message SIMAccountData {
string clearing_account = 40;
}
enum ProfessionalDesignation
{
PROFESSIONALDESIGNATION_PRO = 0;
PROFESSIONALDESIGNATION_NONPRO = 1;
}
enum DisplayDesignation
{
DISPLAYDESIGNATION_DISPLAY = 0;
DISPLAYDESIGNATION_NONDISPLAY = 1;
}
message CMEUserData{
string operator_id = 10;
string ats_operator_id = 20;
@@ -293,7 +311,11 @@ message CMEUserData{
string smp_instruction = 70;
string max_order_rate_per_connection = 80;
string location = 90;
// add suff here
ProfessionalDesignation pro_nonpro = 100;
DisplayDesignation display_nondisplay = 110;
enum AdminDisplay { All = 0; MD = 1; Routing = 2; None = 100; }
AdminDisplay admin_display_flag = 120;
repeated CMEMicData md_permissions = 1000;
}
@@ -377,6 +399,7 @@ message OrderCancelRequest{
message MDEntitlementReportUnit{
string record_id = 1 [(bts.api.key)=true];
string user = 10;
uint32 user_id=11;
string exchange = 20;
string mic = 30;
bool enabled = 40;
@@ -400,6 +423,7 @@ message AccountCreateAPIArgument{
uint32 parent_account_id = 10;
string account_name = 20;
string description = 30;
string creator_user = 40;
}
message AccountUpdateAPIArgument{
BTSAccount complete_data=10;
@@ -408,6 +432,8 @@ message UserCreateAPIArgument{
uint32 parent_group_id = 20;
string name = 30;
string password = 40;
string full_name = 42;
string company = 44;
LicenseData license_data = 50;
LicenseEntitlements license_entitlements = 70;
}
@@ -433,6 +459,10 @@ message AdminServiceCreateAccountResult {
uint32 new_id = 30;
}
message FilteredAdminDataRequest {
string token = 1;
}
service AdminService {
option (bts.api.service_description) = "Provides a management interface to the user model";
// account
@@ -446,6 +476,7 @@ service AdminService {
// subscription
rpc admin_data(bts.api.VoidMessage) returns (stream AdminData);
rpc get_admin_data(bts.api.VoidMessage) returns (AdminData);
rpc get_filtered_admin_data(FilteredAdminDataRequest) returns (AdminData);
// orders
rpc get_order_summary(bts.api.VoidMessage) returns (OrdersSummary);
rpc cancel_orders(OrderCancelRequest) returns (bts.api.VoidMessage);
+1
View File
@@ -47,6 +47,7 @@ message AuthenticateLoginResponse {
string reason = 2;
AuthToken token = 3;
uint32 user_id = 4;
bool can_create = 5;
}
message AuthenticateTokenRequest {AuthToken token = 1;}
message AuthenticateTokenResponse {bool is_valid = 1; string reason = 2; User user = 3;}
+11 -16
View File
@@ -38,6 +38,7 @@
#endif
#include "lib/config_proto_files.h"
#include "lib/config_defaults.h"
using namespace std;
@@ -77,12 +78,6 @@ ConfigurationState::~ConfigurationState(void)
{
}
std::string ConfigurationState::defaults_path()
{
std::string appdir = os::get_install_path();
return appdir + "/defaults/";
}
std::string get_command_argument(const char* key)
{
std::vector<std::string> cmd_line = os::get_command_line();
@@ -127,18 +122,18 @@ void ConfigurationState::initialize_defaults(IConfigLog& log)
std::shared_ptr<proto::Message> new_message(_prototype->New());
ErrorsOnlyLog elog(log);
load_message(defaults_path(), *new_message.get(), elog, *_deferred_queue.get());
std::string defaults;
for (const char* str : config_static_defaults) {
defaults += str;
defaults += "\n";
}
proto::io::ArrayInputStream input_stream(defaults.c_str(), (int)defaults.length());
parse_stream("static_defaults.conf", input_stream, *new_message.get(), elog, *_deferred_queue.get());
_defaults = new_message;
// If defaults has an include process that now
// FEATURE: Unlike settings loaded from ~/sparkdata/ these includes
// take precedence over the settings in the file which includes them.
// While this seems backwards it is always the behavior you want
std::string include = get_include(*_defaults.get());
if (!include.empty())
{
load(include, "", log);
}
// do not process 'include' for static defaults
_is_initialized = true;
}
-1
View File
@@ -81,7 +81,6 @@ namespace internal {
void generate_all_config_proto(IConfigLog&);
const proto::Message* try_get_value(const std::string& field_name, const proto::Message& message) const;
std::string defaults_path();
std::string settings_path();
bool _is_initialized;
-13
View File
@@ -1,13 +0,0 @@
ExecutorCfg
{
value
{
# executor for latency sensitive code
executor { affinity: 0 yield_cpu: false }
# normal executor
executor { affinity: -1 yield_cpu: true }
}
}
+4
View File
@@ -80,6 +80,10 @@ inline Price round_to_quote_price(api::Security const&sec, Price in, api::Side s
else
return Price(raw_round_to_quote_price(p,sec.contract_spec().low_tick(),side));
}
// Looking to round md::IntPrice? Look in IntPrice.h
// computes the difference between 2 prices in ticks.
// the result is an a signed int so the order of args
// matter, its second price - first price
+5 -1
View File
@@ -53,7 +53,11 @@ enum CMEMics {
}
enum ICEMics{
ICE_FUTURES_CANADA=0;
ICE_UNSET=0;
ICE_US = 1;
ICE_EU = 2;
ICE_LIFFE = 3;
ICE_CA = 4;
}
enum Side {
+1 -1
View File
@@ -140,7 +140,7 @@ public:
~Logger()
{
#ifndef WIN32
#ifdef false //This breaks .net core and windows
assert(!_active);
assert(_wcursor == 0);
#endif
+5 -2
View File
@@ -193,7 +193,7 @@ LoggingConfigPoller::~LoggingConfigPoller ()
_poller.stop();
}
#ifdef _WIN32
namespace interop {
const char* intern(const char* str) {
@@ -205,7 +205,11 @@ const char* intern(const char* str) {
return *iter;
}
else {
#ifdef _WIN32
const char* v = _strdup(str);
#else
const char* v = strdup(str);
#endif
set.insert(v);
return v;
}
@@ -218,7 +222,6 @@ extern "C" BTS_EXPORT void Logger_log (int level, int subsystem, const char *mes
}
}
}
#endif
}}
+3 -1
View File
@@ -42,7 +42,9 @@ constexpr log::Subsystem Mercury {ExSim.ordinal+1 };
constexpr log::Subsystem Testing {Mercury.ordinal+1 };
constexpr log::Subsystem Latency {Testing.ordinal+1 };
constexpr log::Subsystem DataFlow {Latency.ordinal+1 };
constexpr log::Subsystem Plugin {DataFlow.ordinal+1 };
constexpr log::Subsystem Plugin {DataFlow.ordinal+1 };
constexpr log::Subsystem ClientSdk {Plugin.ordinal+1 };
constexpr log::Subsystem FIX {ClientSdk.ordinal+1 };
struct LoggerHandle {
LoggerHandle(LoggerHandle const&) = delete;
Loaded 100 of 170 files, more files were not shown because too many files have changed in this diff. Show more