Compare commits

..
1689 changed files with 58627 additions and 114247 deletions

No files matched your search

-18
View File
@@ -7,24 +7,6 @@
"commands": [
"dotnet-format"
]
},
"fable": {
"version": "4.0.0-theta-018",
"commands": [
"fable"
]
},
"fantomas": {
"version": "5.2.2",
"commands": [
"fantomas"
]
},
"fake-cli": {
"version": "6.0.0",
"commands": [
"fake"
]
}
}
}
+3 -3
View File
@@ -1,6 +1,5 @@
nohup.out
build/**
install/**
build/
.build_proxy/
cmake-build-debug/
.cache/
@@ -41,6 +40,8 @@ trace_data/
.ionide/*
/.ignore
*.rej
/build
/build.*/*
.clangd/
*CMakeDeps.cmake
/sniper.ipynb
@@ -50,4 +51,3 @@ trace_data/
*.tmp/*
/.venv*/*
.cache/
src/web/api/tsconfig.*.g.json
+3
View File
@@ -7,3 +7,6 @@
[submodule "docker/windows"]
path = docker/windows
url = git@bitbucket.org:bluetradesys/windows_docker.git
[submodule "src/deribit-crypto-connector-demo-application"]
path = src/deribit-crypto-connector-demo-application
url = git@gitlab.com:cryptoexchanges1/deribit-crypto-connector-demo-application.git
+3 -22
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.5)
project(spark)
if(WIN32)
@@ -7,18 +7,12 @@ else()
set(platform "linux")
endif()
set(
EXTERNALS_ROOT
"${CMAKE_SOURCE_DIR}/externals/${platform}"
CACHE
STRING
"External dependencies root folder (should contain folders: lib, bin, include")
set(EXTERNALS_ROOT "${CMAKE_SOURCE_DIR}/externals/${platform}")
set(SPARK_PYTHON "${EXTERNALS_ROOT}/python")
set(SPARK_BIN "${EXTERNALS_ROOT}/bin")
set(SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
set(SPARK_LIB "${EXTERNALS_ROOT}/lib/${CMAKE_BUILD_TYPE}")
set(BUILD_SDK OFF CACHE BOOL "Build the sdk")
set(BUILD_WEB ON CACHE BOOL "Build the web server")
include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
link_directories(${SPARK_LIB})
@@ -53,12 +47,6 @@ else()
# make sparkd look for libspark.so in its directory
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ".")
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install/${CMAKE_BUILD_TYPE}/sparkd")
add_compile_definitions(SPARK_ENABLE_CCAPI)
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_FTX)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
add_compile_definitions(CCAPI_ENABLE_LOG_WARN)
# flags
set(
@@ -79,9 +67,7 @@ else()
# subdirs
add_subdirectory(src/server/sparkd)
add_subdirectory(src/lib)
if (BUILD_WEB)
add_subdirectory(src/web)
endif()
add_subdirectory(src/web)
add_subdirectory(doc/user)
if(BUILD_SDK)
add_subdirectory(src/sdk)
@@ -105,9 +91,4 @@ else()
DEPENDS sparkd data_files_dir)
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
install(DIRECTORY "${CMAKE_BINARY_DIR}/server/plugins" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libACE-5.7.5.so" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/src/emumba/sparkd.lic" DESTINATION bin)
endif()
+1 -3
View File
@@ -46,7 +46,6 @@ function setup_build
assert_dir $repo_root
assert_dir $build_root
set compiler "$argv[1]"
set c_compiler (which cc)
set build_type "$argv[2]"
if test \( -z "$build_type" \) -o \( \( "$build_type" != debug \) -a \( "$build_type" != release \) \)
@@ -60,7 +59,6 @@ function setup_build
else
# set compiler_arg "/usr/lib/ccache/clang++"
set compiler_arg "clang++"
set c_compiler (which clang)
end
else if test "$build_type" = release
set build_type_arg Release
@@ -87,7 +85,7 @@ function setup_build
rm -rf "$build_dest"
mkdir -p "$build_dest"
cd "$build_dest"
cmake -G Ninja -DCMAKE_BUILD_TYPE=$build_type_arg -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_C_COMPILER=$c_compiler -DCMAKE_CXX_COMPILER=$compiler_arg $repo_root
cmake -G Ninja -DCMAKE_BUILD_TYPE=$build_type_arg -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_CXX_COMPILER=$compiler_arg $repo_root
end
# The while loop below removes arguments (via shift) as it parses them.
File renamed without changes.
@@ -1,941 +0,0 @@
{
"$schema": "vscode://schemas/color-theme",
"type": "dark",
"colors": {
"activityBar.background": "#111111",
"activityBar.border": "#111111",
"activityBar.foreground": "#888888",
"activityBarBadge.background": "#88cc44",
"activityBarBadge.foreground": "#222222",
"badge.background": "#88cc44",
"badge.foreground": "#222222",
"button.background": "#333333",
"button.foreground": "#888888",
"button.hoverBackground": "#222222",
"debugExceptionWidget.background": "#333333",
"debugExceptionWidget.border": "#222222",
"debugToolBar.background": "#111111",
"diffEditor.insertedTextBackground": "#ffdd440c",
"diffEditor.insertedTextBorder": "#ffdd44",
"diffEditor.removedTextBackground": "#88bbff0c",
"diffEditor.removedTextBorder": "#88bbff",
"dropdown.background": "#222222",
"dropdown.border": "#222222",
"dropdown.foreground": "#888888",
"editor.background": "#222222",
"editor.findMatchBackground": "#44446655",
"editor.findMatchHighlightBackground": "#444466aa",
"editor.foreground": "#ffffff",
"editor.hoverHighlightBackground": "#44446655",
"editor.inactiveSelectionBackground": "#44446655",
"editor.lineHighlightBackground": "#00000066",
"editor.lineHighlightBorder": "#00000000",
"editor.rangeHighlightBackground": "#333333",
"editor.selectionBackground": "#444466",
"editor.selectionHighlightBackground": "#44446655",
"editor.wordHighlightBackground": "#44446655",
"editor.wordHighlightStrongBackground": "#44446655",
"editorBracketMatch.background": "#222222",
"editorBracketMatch.border": "#666666",
"editorCodeLens.foreground": "#666666",
"editorCursor.foreground": "#ffffff",
"editorError.foreground": "#ee5500",
"editorGroup.border": "#111111",
"editorGroup.dropBackground": "#111111bb",
"editorGroupHeader.noTabsBackground": "#111111",
"editorGroupHeader.tabsBackground": "#111111",
"editorGroupHeader.tabsBorder": "#222222",
"editorGutter.addedBackground": "#ffdd44",
"editorGutter.background": "#222222",
"editorGutter.deletedBackground": "#88bbff",
"editorGutter.modifiedBackground": "#88bbff",
"editorHoverWidget.background": "#333333",
"editorHoverWidget.border": "#222222",
"editorIndentGuide.background": "#333333",
"editorLineNumber.foreground": "#555555",
"editorLink.activeForeground": "#bba0ff",
"editorMarkerNavigation.background": "#333333",
"editorMarkerNavigationError.background": "#88bbff",
"editorMarkerNavigationWarning.background": "#88bbff",
"editorOverviewRuler.addedForeground": "#ffdd44",
"editorOverviewRuler.border": "#222222",
"editorOverviewRuler.currentContentForeground": "#333333",
"editorOverviewRuler.deletedForeground": "#88bbff",
"editorOverviewRuler.errorForeground": "#88bbff",
"editorOverviewRuler.findMatchForeground": "#555555",
"editorOverviewRuler.incomingContentForeground": "#333333",
"editorOverviewRuler.infoForeground": "#bba0ff",
"editorOverviewRuler.modifiedForeground": "#88bbff",
"editorOverviewRuler.rangeHighlightForeground": "#333333",
"editorOverviewRuler.selectionHighlightForeground": "#555555",
"editorOverviewRuler.warningForeground": "#88bbff",
"editorOverviewRuler.wordHighlightForeground": "#333333",
"editorOverviewRuler.wordHighlightStrongForeground": "#555555",
"editorRuler.foreground": "#ffffff",
"editorSuggestWidget.background": "#333333",
"editorSuggestWidget.border": "#333333",
"editorSuggestWidget.foreground": "#888888",
"editorSuggestWidget.highlightForeground": "#ffffff",
"editorSuggestWidget.selectedBackground": "#666666",
"editorWarning.border": "#00000000",
"editorWarning.foreground": "#88bbff",
"editorWhitespace.foreground": "#333333",
"editorWidget.background": "#333333",
"editorWidget.border": "#333333",
"errorForeground": "#88bbff",
"extensionButton.prominentBackground": "#333333",
"extensionButton.prominentForeground": "#ffffff",
"extensionButton.prominentHoverBackground": "#555555",
"focusBorder": "#666666",
"foreground": "#ffffff",
"gitDecoration.modifiedResourceForeground": "#cccccc",
"gitDecoration.untrackedResourceForeground": "#cccccc",
"input.background": "#333333",
"input.border": "#333333",
"input.foreground": "#ffffff",
"input.placeholderForeground": "#555555",
"inputOption.activeBorder": "#555555",
"inputValidation.errorBackground": "#333333",
"inputValidation.errorBorder": "#88bbff",
"inputValidation.infoBackground": "#333333",
"inputValidation.infoBorder": "#bba0ff",
"inputValidation.warningBackground": "#333333",
"inputValidation.warningBorder": "#88bbff",
"list.activeSelectionBackground": "#111111",
"list.activeSelectionForeground": "#ffffff",
"list.dropBackground": "#111111bf",
"list.focusBackground": "#222222",
"list.focusForeground": "#ffffff",
"list.highlightForeground": "#ffffff",
"list.hoverBackground": "#333333",
"list.hoverForeground": "#ffffff",
"list.inactiveSelectionBackground": "#000000",
"list.inactiveSelectionForeground": "#ffffff",
"menu.background": "#333333",
"menu.foreground": "#ffffff",
"merge.border": "#222222",
"merge.currentContentBackground": "#333333",
"merge.currentHeaderBackground": "#555555",
"merge.incomingContentBackground": "#555555",
"merge.incomingHeaderBackground": "#666666",
"panel.background": "#111111",
"panel.border": "#111111",
"panelTitle.activeBorder": "#ffffff",
"panelTitle.activeForeground": "#ffffff",
"panelTitle.inactiveForeground": "#888888",
"peekView.border": "#222222",
"peekViewEditor.background": "#333333",
"peekViewEditor.matchHighlightBackground": "#555555",
"peekViewEditorGutter.background": "#333333",
"peekViewResult.background": "#333333",
"peekViewResult.fileForeground": "#888888",
"peekViewResult.lineForeground": "#888888",
"peekViewResult.matchHighlightBackground": "#555555",
"peekViewResult.selectionBackground": "#333333",
"peekViewResult.selectionForeground": "#ffffff",
"peekViewTitle.background": "#333333",
"peekViewTitleDescription.foreground": "#888888",
"peekViewTitleLabel.foreground": "#ffffff",
"pickerGroup.border": "#222222",
"pickerGroup.foreground": "#555555",
"progressBar.background": "#333333",
"scrollbar.shadow": "#191919",
"scrollbarSlider.activeBackground": "#ffffff12",
"scrollbarSlider.background": "#ffffff12",
"scrollbarSlider.hoverBackground": "#ffffff12",
"selection.background": "#2255aa",
"sideBar.background": "#111111",
"sideBar.border": "#111111",
"sideBar.foreground": "#888888",
"sideBarSectionHeader.background": "#111111",
"sideBarSectionHeader.foreground": "#666666",
"sideBarTitle.foreground": "#555555",
"statusBar.background": "#111111",
"statusBar.border": "#111111",
"statusBar.debuggingBackground": "#666666",
"statusBar.debuggingForeground": "#ffffff",
"statusBar.foreground": "#666666",
"statusBar.noFolderBackground": "#111111",
"statusBar.noFolderForeground": "#666666",
"statusBarItem.activeBackground": "#222222",
"statusBarItem.hoverBackground": "#222222",
"statusBarItem.prominentBackground": "#333333",
"statusBarItem.prominentHoverBackground": "#444444",
"tab.activeBackground": "#222222",
"tab.border": "#111111",
"tab.inactiveBackground": "#111111",
"tab.inactiveForeground": "#888888",
"tab.unfocusedActiveBorder": "#888888",
"tab.unfocusedActiveForeground": "#888888",
"tab.unfocusedInactiveForeground": "#888888",
"terminal.ansiBlack": "#333333",
"terminal.ansiBlue": "#88bbff",
"terminal.ansiBrightBlack": "#666666",
"terminal.ansiBrightBlue": "#88bbff",
"terminal.ansiBrightCyan": "#bba0ff",
"terminal.ansiBrightGreen": "#ffdd44",
"terminal.ansiBrightMagenta": "#88bbff",
"terminal.ansiBrightRed": "#88bbff",
"terminal.ansiBrightWhite": "#ffffff",
"terminal.ansiBrightYellow": "#88cc44",
"terminal.ansiCyan": "#bba0ff",
"terminal.ansiGreen": "#ffdd44",
"terminal.ansiMagenta": "#88bbff",
"terminal.ansiRed": "#88bbff",
"terminal.ansiWhite": "#ffffff",
"terminal.ansiYellow": "#88cc44",
"terminal.background": "#151515",
"terminal.foreground": "#ffffff",
"titleBar.activeBackground": "#111111",
"titleBar.activeForeground": "#888888",
"titleBar.inactiveBackground": "#111111",
"titleBar.inactiveForeground": "#555555",
"walkThrough.embeddedEditorBackground": "#111111",
"widget.shadow": "#111111",
//"activityBar.activeBorder": "#888888",
//"activityBar.dropBorder": "#888888",
//"activityBar.inactiveForeground": "#88888866",
//"banner.background": "#111111",
//"banner.foreground": "#ffffff",
//"banner.iconForeground": "#3794ff",
//"bookmarks.lineBackground": "#00000000",
//"bookmarks.lineBorder": "#00000000",
//"bookmarks.overviewRuler": "#157efb88",
//"breadcrumb.activeSelectionForeground": "#ffffff",
//"breadcrumb.background": "#222222",
//"breadcrumb.focusForeground": "#ffffff",
//"breadcrumb.foreground": "#ffffffcc",
//"breadcrumbPicker.background": "#333333",
//"button.secondaryBackground": "#3a3d41",
//"button.secondaryForeground": "#ffffff",
//"button.secondaryHoverBackground": "#45494e",
//"charts.blue": "#3794ff",
//"charts.foreground": "#ffffff",
//"charts.green": "#89d185",
//"charts.lines": "#ffffff80",
//"charts.orange": "#d18616",
//"charts.purple": "#b180d7",
//"charts.red": "#ee5500",
//"charts.yellow": "#88bbff",
//"checkbox.background": "#222222",
//"checkbox.border": "#222222",
//"checkbox.foreground": "#888888",
//"debugConsole.errorForeground": "#88bbff",
//"debugConsole.infoForeground": "#3794ff",
//"debugConsole.sourceForeground": "#ffffff",
//"debugConsole.warningForeground": "#88bbff",
//"debugConsoleInputIcon.foreground": "#ffffff",
//"debugIcon.breakpointCurrentStackframeForeground": "#ffcc00",
//"debugIcon.breakpointDisabledForeground": "#848484",
//"debugIcon.breakpointForeground": "#e51400",
//"debugIcon.breakpointStackframeForeground": "#89d185",
//"debugIcon.breakpointUnverifiedForeground": "#848484",
//"debugIcon.continueForeground": "#75beff",
//"debugIcon.disconnectForeground": "#f48771",
//"debugIcon.pauseForeground": "#75beff",
//"debugIcon.restartForeground": "#89d185",
//"debugIcon.startForeground": "#89d185",
//"debugIcon.stepBackForeground": "#75beff",
//"debugIcon.stepIntoForeground": "#75beff",
//"debugIcon.stepOutForeground": "#75beff",
//"debugIcon.stepOverForeground": "#75beff",
//"debugIcon.stopForeground": "#f48771",
//"debugTokenExpression.boolean": "#4e94ce",
//"debugTokenExpression.error": "#f48771",
//"debugTokenExpression.name": "#c586c0",
//"debugTokenExpression.number": "#b5cea8",
//"debugTokenExpression.string": "#ce9178",
//"debugTokenExpression.value": "#cccccc99",
//"debugView.exceptionLabelBackground": "#6c2022",
//"debugView.exceptionLabelForeground": "#ffffff",
//"debugView.stateLabelBackground": "#88888844",
//"debugView.stateLabelForeground": "#ffffff",
//"debugView.valueChangedHighlight": "#569cd6",
//"descriptionForeground": "#ffffffb3",
//"diffEditor.diagonalFill": "#cccccc33",
//"editor.findRangeHighlightBackground": "#3a3d4166",
//"editor.focusedStackFrameHighlightBackground": "#7abd7a4d",
//"editor.foldBackground": "#4444664d",
//"editor.inlineValuesBackground": "#ffc80033",
//"editor.inlineValuesForeground": "#ffffff80",
//"editor.linkedEditingBackground": "#ff00004d",
//"editor.snippetFinalTabstopHighlightBorder": "#525252",
//"editor.snippetTabstopHighlightBackground": "#7c7c7c4d",
//"editor.stackFrameHighlightBackground": "#ffff0033",
//"editor.symbolHighlightBackground": "#444466aa",
//"editorActiveLineNumber.foreground": "#c6c6c6",
//"editorBracketHighlight.foreground1": "#ffd700",
//"editorBracketHighlight.foreground2": "#da70d6",
//"editorBracketHighlight.foreground3": "#179fff",
//"editorBracketHighlight.foreground4": "#00000000",
//"editorBracketHighlight.foreground5": "#00000000",
//"editorBracketHighlight.foreground6": "#00000000",
//"editorBracketHighlight.unexpectedBracket.foreground": "#ff1212cc",
//"editorBracketPairGuide.activeBackground1": "#00000000",
//"editorBracketPairGuide.activeBackground2": "#00000000",
//"editorBracketPairGuide.activeBackground3": "#00000000",
//"editorBracketPairGuide.activeBackground4": "#00000000",
//"editorBracketPairGuide.activeBackground5": "#00000000",
//"editorBracketPairGuide.activeBackground6": "#00000000",
//"editorBracketPairGuide.background1": "#00000000",
//"editorBracketPairGuide.background2": "#00000000",
//"editorBracketPairGuide.background3": "#00000000",
//"editorBracketPairGuide.background4": "#00000000",
//"editorBracketPairGuide.background5": "#00000000",
//"editorBracketPairGuide.background6": "#00000000",
//"editorGhostText.foreground": "#ffffff56",
//"editorGutter.commentRangeForeground": "#c5c5c5",
//"editorGutter.foldingControlForeground": "#c5c5c5",
//"editorHint.foreground": "#eeeeeeb3",
//"editorHoverWidget.foreground": "#ffffff",
//"editorHoverWidget.highlightForeground": "#ffffff",
//"editorHoverWidget.statusBarBackground": "#3d3d3d",
//"editorIndentGuide.activeBackground": "#333333",
//"editorInfo.foreground": "#3794ff",
//"editorInlayHint.background": "#88cc4499",
//"editorInlayHint.foreground": "#222222cc",
//"editorInlayHint.parameterBackground": "#88cc4499",
//"editorInlayHint.parameterForeground": "#222222cc",
//"editorInlayHint.typeBackground": "#88cc4499",
//"editorInlayHint.typeForeground": "#222222cc",
//"editorLightBulb.foreground": "#ffcc00",
//"editorLightBulbAutoFix.foreground": "#75beff",
//"editorLineNumber.activeForeground": "#c6c6c6",
//"editorMarkerNavigationError.headerBackground": "#88bbff1a",
//"editorMarkerNavigationInfo.background": "#3794ff",
//"editorMarkerNavigationInfo.headerBackground": "#3794ff1a",
//"editorMarkerNavigationWarning.headerBackground": "#88bbff1a",
//"editorOverviewRuler.bracketMatchForeground": "#a0a0a0",
//"editorOverviewRuler.commonContentForeground": "#60606066",
//"editorOverviewRuler.unicodeForeground": "#d186167e",
//"editorPane.background": "#222222",
//"editorSuggestWidget.focusHighlightForeground": "#ffffff",
//"editorSuggestWidget.selectedForeground": "#ffffff",
//"editorSuggestWidgetStatus.foreground": "#88888880",
//"editorUnicodeHighlight.border": "#bd9b03",
//"editorUnnecessaryCode.opacity": "#000000aa",
//"editorWidget.foreground": "#ffffff",
//"extensionBadge.remoteBackground": "#88cc44",
//"extensionBadge.remoteForeground": "#222222",
//"extensionIcon.preReleaseForeground": "#1d9271",
//"extensionIcon.starForeground": "#ff8e00",
//"extensionIcon.verifiedForeground": "#3794ff",
//"gitDecoration.addedResourceForeground": "#81b88b",
//"gitDecoration.conflictingResourceForeground": "#e4676b",
//"gitDecoration.deletedResourceForeground": "#c74e39",
//"gitDecoration.ignoredResourceForeground": "#8c8c8c",
//"gitDecoration.renamedResourceForeground": "#73c991",
//"gitDecoration.stageDeletedResourceForeground": "#c74e39",
//"gitDecoration.stageModifiedResourceForeground": "#e2c08d",
//"gitDecoration.submoduleResourceForeground": "#8db9e2",
//"gitlens.closedAutolinkedIssueIconColor": "#a371f7",
//"gitlens.closedPullRequestIconColor": "#f85149",
//"gitlens.decorations.addedForegroundColor": "#81b88b",
//"gitlens.decorations.branchAheadForegroundColor": "#35b15e",
//"gitlens.decorations.branchBehindForegroundColor": "#b15e35",
//"gitlens.decorations.branchDivergedForegroundColor": "#d8af1b",
//"gitlens.decorations.branchMissingUpstreamForegroundColor": "#c74e39",
//"gitlens.decorations.branchUnpublishedForegroundColor": "#35b15e",
//"gitlens.decorations.branchUpToDateForegroundColor": "#888888",
//"gitlens.decorations.copiedForegroundColor": "#73c991",
//"gitlens.decorations.deletedForegroundColor": "#c74e39",
//"gitlens.decorations.ignoredForegroundColor": "#8c8c8c",
//"gitlens.decorations.modifiedForegroundColor": "#cccccc",
//"gitlens.decorations.renamedForegroundColor": "#73c991",
//"gitlens.decorations.untrackedForegroundColor": "#cccccc",
//"gitlens.gutterBackgroundColor": "#ffffff13",
//"gitlens.gutterForegroundColor": "#bebebe",
//"gitlens.gutterUncommittedForegroundColor": "#00bcf299",
//"gitlens.lineHighlightBackgroundColor": "#00bcf233",
//"gitlens.lineHighlightOverviewRulerColor": "#00bcf299",
//"gitlens.mergedPullRequestIconColor": "#a371f7",
//"gitlens.openAutolinkedIssueIconColor": "#3fb950",
//"gitlens.openPullRequestIconColor": "#3fb950",
//"gitlens.trailingLineBackgroundColor": "#00000000",
//"gitlens.trailingLineForegroundColor": "#99999959",
//"gitlens.unpublishedCommitIconColor": "#35b15e",
//"gitlens.unpulledChangesIconColor": "#b15e35",
//"gitlens.unpushlishedChangesIconColor": "#35b15e",
//"icon.foreground": "#c5c5c5",
//"inputOption.activeBackground": "#66666666",
//"inputOption.activeForeground": "#ffffff",
//"inputOption.hoverBackground": "#5a5d5e80",
//"interactive.activeCodeBorder": "#222222",
//"interactive.inactiveCodeBorder": "#000000",
//"keybindingLabel.background": "#8080802b",
//"keybindingLabel.border": "#33333399",
//"keybindingLabel.bottomBorder": "#44444499",
//"keybindingLabel.foreground": "#cccccc",
//"keybindingTable.headerBackground": "#ffffff0a",
//"keybindingTable.rowsBackground": "#ffffff0a",
//"list.deemphasizedForeground": "#8c8c8c",
//"list.errorForeground": "#f88070",
//"list.filterMatchBackground": "#444466aa",
//"list.focusHighlightForeground": "#ffffff",
//"list.focusOutline": "#666666",
//"list.invalidItemForeground": "#b89500",
//"list.warningForeground": "#cca700",
//"listFilterWidget.background": "#653723",
//"listFilterWidget.noMatchesOutline": "#be1100",
//"listFilterWidget.outline": "#00000000",
//"menu.selectionBackground": "#111111",
//"menu.selectionForeground": "#ffffff",
//"menu.separatorBackground": "#bbbbbb",
//"menubar.selectionBackground": "#ffffff1a",
//"menubar.selectionForeground": "#888888",
//"merge.commonContentBackground": "#60606029",
//"merge.commonHeaderBackground": "#60606066",
//"minimap.errorHighlight": "#ff1212b3",
//"minimap.findMatchHighlight": "#d18616",
//"minimap.foregroundOpacity": "#000000",
//"minimap.selectionHighlight": "#264f78",
//"minimap.selectionOccurrenceHighlight": "#676767",
//"minimap.unicodeHighlight": "#d18616",
//"minimap.warningHighlight": "#88bbff",
//"minimapGutter.addedBackground": "#587c0c",
//"minimapGutter.deletedBackground": "#94151b",
//"minimapGutter.modifiedBackground": "#0c7d9d",
//"minimapSlider.activeBackground": "#ffffff09",
//"minimapSlider.background": "#ffffff09",
//"minimapSlider.hoverBackground": "#ffffff09",
//"notebook.cellBorderColor": "#000000",
//"notebook.cellEditorBackground": "#111111",
//"notebook.cellInsertionIndicator": "#666666",
//"notebook.cellStatusBarItemHoverBackground": "#ffffff26",
//"notebook.cellToolbarSeparator": "#80808059",
//"notebook.focusedCellBorder": "#666666",
//"notebook.focusedEditorBorder": "#666666",
//"notebook.inactiveFocusedCellBorder": "#000000",
//"notebook.selectedCellBackground": "#000000",
//"notebook.selectedCellBorder": "#000000",
//"notebook.symbolHighlightBackground": "#ffffff0b",
//"notebookScrollbarSlider.activeBackground": "#ffffff12",
//"notebookScrollbarSlider.background": "#ffffff12",
//"notebookScrollbarSlider.hoverBackground": "#ffffff12",
//"notebookStatusErrorIcon.foreground": "#88bbff",
//"notebookStatusRunningIcon.foreground": "#ffffff",
//"notebookStatusSuccessIcon.foreground": "#89d185",
//"notificationCenterHeader.background": "#424242",
//"notificationLink.foreground": "#3794ff",
//"notifications.background": "#333333",
//"notifications.border": "#424242",
//"notifications.foreground": "#ffffff",
//"notificationsErrorIcon.foreground": "#ee5500",
//"notificationsInfoIcon.foreground": "#3794ff",
//"notificationsWarningIcon.foreground": "#88bbff",
//"panel.dropBorder": "#ffffff",
//"panelSection.border": "#111111",
//"panelSection.dropBackground": "#111111bb",
//"panelSectionHeader.background": "#80808033",
//"ports.iconRunningProcessForeground": "#88cc44",
//"problemsErrorIcon.foreground": "#ee5500",
//"problemsInfoIcon.foreground": "#3794ff",
//"problemsWarningIcon.foreground": "#88bbff",
//"quickInput.background": "#333333",
//"quickInput.foreground": "#ffffff",
//"quickInputList.focusBackground": "#111111",
//"quickInputList.focusForeground": "#ffffff",
//"quickInputTitle.background": "#ffffff1b",
//"sash.hoverBorder": "#666666",
//"scm.providerBorder": "#454545",
//"searchEditor.findMatchBackground": "#44446670",
//"searchEditor.textInputBorder": "#333333",
//"settings.checkboxBackground": "#222222",
//"settings.checkboxBorder": "#222222",
//"settings.checkboxForeground": "#888888",
//"settings.dropdownBackground": "#222222",
//"settings.dropdownBorder": "#222222",
//"settings.dropdownForeground": "#888888",
//"settings.dropdownListBorder": "#333333",
//"settings.focusedRowBackground": "#80808024",
//"settings.focusedRowBorder": "#ffffff1f",
//"settings.headerForeground": "#e7e7e7",
//"settings.modifiedItemIndicator": "#0c7d9d",
//"settings.numberInputBackground": "#333333",
//"settings.numberInputBorder": "#333333",
//"settings.numberInputForeground": "#ffffff",
//"settings.rowHoverBackground": "#80808012",
//"settings.textInputBackground": "#333333",
//"settings.textInputBorder": "#333333",
//"settings.textInputForeground": "#ffffff",
//"sideBar.dropBackground": "#111111bb",
//"sideBySideEditor.horizontalBorder": "#111111",
//"sideBySideEditor.verticalBorder": "#111111",
//"statusBar.debuggingBorder": "#111111",
//"statusBar.noFolderBorder": "#111111",
//"statusBarItem.compactHoverBackground": "#ffffff33",
//"statusBarItem.errorBackground": "#0066eb",
//"statusBarItem.errorForeground": "#ffffff",
//"statusBarItem.prominentForeground": "#666666",
//"statusBarItem.remoteBackground": "#88cc44",
//"statusBarItem.remoteForeground": "#222222",
//"statusBarItem.warningBackground": "#0066eb",
//"statusBarItem.warningForeground": "#ffffff",
//"symbolIcon.arrayForeground": "#ffffff",
//"symbolIcon.booleanForeground": "#ffffff",
//"symbolIcon.classForeground": "#ee9d28",
//"symbolIcon.colorForeground": "#ffffff",
//"symbolIcon.constantForeground": "#ffffff",
//"symbolIcon.constructorForeground": "#b180d7",
//"symbolIcon.enumeratorForeground": "#ee9d28",
//"symbolIcon.enumeratorMemberForeground": "#75beff",
//"symbolIcon.eventForeground": "#ee9d28",
//"symbolIcon.fieldForeground": "#75beff",
//"symbolIcon.fileForeground": "#ffffff",
//"symbolIcon.folderForeground": "#ffffff",
//"symbolIcon.functionForeground": "#b180d7",
//"symbolIcon.interfaceForeground": "#75beff",
//"symbolIcon.keyForeground": "#ffffff",
//"symbolIcon.keywordForeground": "#ffffff",
//"symbolIcon.methodForeground": "#b180d7",
//"symbolIcon.moduleForeground": "#ffffff",
//"symbolIcon.namespaceForeground": "#ffffff",
//"symbolIcon.nullForeground": "#ffffff",
//"symbolIcon.numberForeground": "#ffffff",
//"symbolIcon.objectForeground": "#ffffff",
//"symbolIcon.operatorForeground": "#ffffff",
//"symbolIcon.packageForeground": "#ffffff",
//"symbolIcon.propertyForeground": "#ffffff",
//"symbolIcon.referenceForeground": "#ffffff",
//"symbolIcon.snippetForeground": "#ffffff",
//"symbolIcon.stringForeground": "#ffffff",
//"symbolIcon.structForeground": "#ffffff",
//"symbolIcon.textForeground": "#ffffff",
//"symbolIcon.typeParameterForeground": "#ffffff",
//"symbolIcon.unitForeground": "#ffffff",
//"symbolIcon.variableForeground": "#75beff",
//"tab.activeForeground": "#ffffff",
//"tab.activeModifiedBorder": "#3399cc",
//"tab.inactiveModifiedBorder": "#3399cc80",
//"tab.lastPinnedBorder": "#585858",
//"tab.unfocusedActiveBackground": "#222222",
//"tab.unfocusedActiveModifiedBorder": "#3399cc80",
//"tab.unfocusedInactiveBackground": "#111111",
//"tab.unfocusedInactiveModifiedBorder": "#3399cc40",
//"terminal.border": "#111111",
//"terminal.dropBackground": "#111111bb",
//"terminal.selectionBackground": "#ffffff40",
//"testing.iconErrored": "#f14c4c",
//"testing.iconFailed": "#f14c4c",
//"testing.iconPassed": "#73c991",
//"testing.iconQueued": "#cca700",
//"testing.iconSkipped": "#848484",
//"testing.iconUnset": "#848484",
//"testing.message.error.decorationForeground": "#ee5500",
//"testing.message.error.lineBackground": "#ff000033",
//"testing.message.info.decorationForeground": "#ffffff80",
//"testing.peekBorder": "#ee5500",
//"testing.peekHeaderBackground": "#ee55001a",
//"testing.runAction": "#73c991",
//"textBlockQuote.background": "#7f7f7f1a",
//"textBlockQuote.border": "#007acc80",
//"textCodeBlock.background": "#0a0a0a66",
//"textLink.activeForeground": "#3794ff",
//"textLink.foreground": "#3794ff",
//"textPreformat.foreground": "#d7ba7d",
//"textSeparator.foreground": "#ffffff2e",
//"toolbar.activeBackground": "#63666750",
//"toolbar.hoverBackground": "#5a5d5e50",
//"tree.indentGuidesStroke": "#585858",
//"tree.tableColumnsBorder": "#cccccc20",
//"tree.tableOddRowsBackground": "#ffffff0a",
//"welcomePage.progress.background": "#333333",
//"welcomePage.progress.foreground": "#3794ff",
//"welcomePage.tileBackground": "#333333",
//"welcomePage.tileHoverBackground": "#3d3d3d",
//"welcomePage.tileShadow": "#111111",
//"activityBar.activeBackground": null,
//"activityBar.activeFocusBorder": null,
//"button.border": null,
//"contrastActiveBorder": null,
//"contrastBorder": null,
//"debugToolBar.border": null,
//"diffEditor.border": null,
//"dropdown.listBackground": null,
//"editor.findMatchBorder": null,
//"editor.findMatchHighlightBorder": null,
//"editor.findRangeHighlightBorder": null,
//"editor.rangeHighlightBorder": null,
//"editor.selectionForeground": null,
//"editor.selectionHighlightBorder": null,
//"editor.snippetFinalTabstopHighlightBackground": null,
//"editor.snippetTabstopHighlightBorder": null,
//"editor.symbolHighlightBorder": null,
//"editor.wordHighlightBorder": null,
//"editor.wordHighlightStrongBorder": null,
//"editorCursor.background": null,
//"editorError.background": null,
//"editorError.border": null,
//"editorGhostText.background": null,
//"editorGhostText.border": null,
//"editorGroup.emptyBackground": null,
//"editorGroup.focusedEmptyBorder": null,
//"editorGroupHeader.border": null,
//"editorHint.border": null,
//"editorInfo.background": null,
//"editorInfo.border": null,
//"editorOverviewRuler.background": null,
//"editorSuggestWidget.selectedIconForeground": null,
//"editorUnnecessaryCode.border": null,
//"editorWarning.background": null,
//"editorWidget.resizeBorder": null,
//"inputValidation.errorForeground": null,
//"inputValidation.infoForeground": null,
//"inputValidation.warningForeground": null,
//"list.activeSelectionIconForeground": null,
//"list.filterMatchBorder": null,
//"list.inactiveFocusBackground": null,
//"list.inactiveFocusOutline": null,
//"list.inactiveSelectionIconForeground": null,
//"menu.border": null,
//"menu.selectionBorder": null,
//"menubar.selectionBorder": null,
//"minimap.background": null,
//"notebook.cellHoverBackground": null,
//"notebook.focusedCellBackground": null,
//"notebook.inactiveSelectedCellBorder": null,
//"notebook.outputContainerBackgroundColor": null,
//"notebook.outputContainerBorderColor": null,
//"notificationCenter.border": null,
//"notificationCenterHeader.foreground": null,
//"notificationToast.border": null,
//"panelInput.border": null,
//"panelSectionHeader.border": null,
//"panelSectionHeader.foreground": null,
//"peekViewEditor.matchHighlightBorder": null,
//"quickInput.list.focusBackground": null,
//"quickInputList.focusIconForeground": null,
//"searchEditor.findMatchBorder": null,
//"sideBarSectionHeader.border": null,
//"tab.activeBorder": null,
//"tab.activeBorderTop": null,
//"tab.hoverBackground": null,
//"tab.hoverBorder": null,
//"tab.hoverForeground": null,
//"tab.unfocusedActiveBorderTop": null,
//"tab.unfocusedHoverBackground": null,
//"tab.unfocusedHoverBorder": null,
//"tab.unfocusedHoverForeground": null,
//"terminal.tab.activeBorder": null,
//"terminalCursor.background": null,
//"terminalCursor.foreground": null,
//"testing.message.info.lineBackground": null,
//"titleBar.border": null,
//"toolbar.hoverOutline": null,
//"welcomePage.background": null,
//"window.activeBorder": null,
//"window.inactiveBorder": null
},
"tokenColors": [
{
"scope": [
"variable.language.this"
],
"settings": {
"fontStyle": ""
}
},
{
"scope": "markup.bold",
"settings": {
"fontStyle": "bold"
}
},
{
"scope": [
"invalid",
"markup.italic",
"markup.quote"
],
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "markup.underline",
"settings": {
"fontStyle": "underline"
}
},
{
"scope": [
"source.js.jsx punctuation.section.embedded",
"entity.name.tag",
"string.other.link.description.title",
"support.constant",
"support.variable",
"support.type",
"variable.other.class",
"support.class.builtin.js",
"support.class.console.js",
"meta.property-value",
"variable.parameter.keyframe-list",
"meta.at-rule.keyframes entity.name.function",
"expression.embedded.vue punctuation.definition"
],
"settings": {
"foreground": "#BBA0FF",
"fontStyle": ""
}
},
{
"scope": [
"entity.other.attribute-name.pseudo-class.css",
"entity.name.type.object.console",
"variable.language",
"variable.parameter.function.language.special.self.python",
"storage.type",
"entity.other.attribute-name.pseudo-element.css"
],
"settings": {
"foreground": "#BBA0FF",
"fontStyle": "italic"
}
},
{
"scope": [
"punctuation.definition.string.template"
],
"settings": {
"foreground": "#BBA0FF",
"fontStyle": "bold"
}
},
{
"scope": [
"entity.other.attribute-name.class.css",
"entity.other.attribute-name.parent-selector-suffix.css",
"entity.other.attribute-name.parent-selector-suffix.css punctuation.definition.entity.css",
"meta.definition.function",
"markup.underline.link",
"markup.inserted",
"meta.function-call",
"meta.instance.constructor",
"meta.attribute-with-value.class string",
"string.other.ref",
"string.other.restructuredtext.ref",
"variable.function",
"entity.name.type",
"entity.name.function",
"variable.other.object.property",
"punctuation.separator.key-value",
"meta.definition.function.js",
"support.class.component.js",
"entity.other.inherited-class",
"support.class",
"support.function.misc",
"support.function",
"entity.other.attribute-name.id.css",
"entity.other.attribute-name.id.css punctuation",
"entity.other.attribute-name.class.css punctuation",
"meta.function-call support.function.construct",
"entity.other.attribute-name"
],
"settings": {
"foreground": "#FFDD44"
}
},
{
"scope": "storage.type.function.arrow",
"settings": {
"foreground": "#FFDD44",
"fontStyle": ""
}
},
{
"scope": [
"entity.name.section",
"constant.numeric.line-number.match.find-in-files",
"markup.heading",
"markup.changed",
"meta.attribute-with-value.id string",
"string",
"punctuation.definition.string.begin",
"punctuation.definition.string.end",
"meta.attribute-selector"
],
"settings": {
"foreground": "#88CC44"
}
},
{
"scope": [
"keyword",
"keyword.control",
"constant",
"constant.numeric",
"constant.language",
"constant.other.property",
"constant.character.escape",
"comment keyword.codetag.notation",
"keyword.other.template.begin",
"keyword.other.template.end",
"markup.raw",
"markup.deleted",
"meta.property-list.css variable.other",
"entity.name.constant.preprocessor",
"meta.preprocessor",
"punctuation.section.embedded",
"punctuation.template-string.element.begin",
"punctuation.template-string.element.end",
"string.quoted.template punctuation.definition.string.begin",
"string.quoted.template punctuation.definition.string.end",
"meta.paragraph.markdown meta.dummy.line-break",
"meta.paragraph.markdown meta.hard-line-break.markdown",
"storage",
"storage.class.restructuredtext.ref",
"string.other.link.title",
"string.other.link.description",
"string.regexp punctuation.definition.string.begin",
"string.regexp punctuation.definition.string.end",
"variable.language.arguments",
"storage.modifier",
"keyword.operator.new",
"keyword.operator",
"keyword.operator.spread",
"punctuation.section.embedded.end.php source.php",
"support.function.construct",
"meta.at-rule.keyframes",
"meta.at-rule.keyframes entity.other.attribute-name"
],
"settings": {
"foreground": "#88BBFF"
}
},
{
"scope": [
"variable.other.substitution"
],
"settings": {
"foreground": "#88BBFF",
"fontStyle": "bold"
}
},
{
"scope": [
"meta.function-call meta.function-call.arguments",
"meta.function-call meta.arguments",
"meta.function-call meta.group",
"source.json meta.structure.dictionary",
"meta.object.member",
"punctuation.definition.group",
"string.unquoted.label",
"variable.other.member",
"entity.name.class",
"entity.name.namespace",
"constant.other",
"variable",
"variable.other",
"variable.other.constant.js",
"text.find-in-files",
"source",
"entity.name.type.module",
"source.sql support.function",
"support.other.namespace.php"
],
"settings": {
"foreground": "#FFFFFF",
"fontStyle": ""
}
},
{
"scope": [
"variable.parameter",
"parameters variable.function"
],
"settings": {
"foreground": "#FFFFFF",
"fontStyle": "italic"
}
},
{
"scope": [
"variable.other.property",
"support.variable.property",
"punctuation.separator.key-value",
"meta.object-literal.key",
"variable.other.object.property",
"variable.object.property",
"text.html.basic",
"text.html.vue-html",
"text.html.php",
"text.html.derivative",
"punctuation.definition.variable",
"punctuation.definition.keyword.scss",
"meta.jsx.children.js",
"meta.tag.attributes keyword.operator.assignment",
"support.type.vendored.property-name.css",
"support.type.property-name.json",
"variable.other.constant.property"
],
"settings": {
"foreground": "#BBBBCC"
}
},
{
"scope": "meta.property-name support.type",
"settings": {
"foreground": "#BBBBCC",
"fontStyle": ""
}
},
{
"scope": [
"keyword.other.substitution",
"keyword.operator.heading.restructuredtext",
"keyword.operator.table.row.restructuredtext",
"keyword.operator.table.data.restructuredtext",
"punctuation",
"markup.ignored",
"markup.untracked",
"meta.brace.round",
"meta.brace.square",
"meta.brace.curly",
"meta.delimiter.comma.js",
"meta.function-call.without-arguments.js",
"meta.function-call.method.without-arguments.js",
"source.scss",
"string source punctuation.section.embedded",
"punctuation.definition.tag.begin source",
"source.php"
],
"settings": {
"foreground": "#9999AA"
}
},
{
"scope": [
"comment",
"comment punctuation"
],
"settings": {
"foreground": "#888899",
"fontStyle": "italic"
}
},
{
"scope": "token.info-token",
"settings": {
"foreground": "#6796E6"
}
},
{
"scope": "token.warn-token",
"settings": {
"foreground": "#CD9731"
}
},
{
"scope": "token.error-token",
"settings": {
"foreground": "#F44747"
}
},
{
"scope": "token.debug-token",
"settings": {
"foreground": "#B267E6"
}
}
]
}
File renamed without changes.
File renamed without changes.
-41
View File
@@ -1,41 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Spark Client",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/build/Debug/bin/net6.0-windows/Spark.exe",
"cwd": "${workspaceFolder}/build/Debug/bin/net6.0-windows",
"enableStepFiltering": false,
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Spark Test",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/build/Debug/bin/net6.0/sparkcmd.exe",
"args": ["test", "${input:test}"],
"cwd": "${workspaceFolder}/build/Debug/bin/net6.0-windows",
"enableStepFiltering": false,
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Attach (.NET Core)",
"type": "coreclr",
"request": "attach"
}
],
"inputs": [
{
"id": "test",
"description": "The test(s) to run",
"type": "promptString"
}
]
}
-37
View File
@@ -1,37 +0,0 @@
{
"cmake.configureOnOpen": false,
"workbench.colorTheme": "Default Dark+",
"C_Cpp.intelliSenseEngine": "Disabled",
"files.associations": {
"*.js": "javascript",
"*.conf": "jsonc",
"array": "cpp",
"xtree": "cpp",
"chrono": "cpp",
"*.ipp": "cpp",
"optional": "cpp",
"regex": "cpp",
"system_error": "cpp",
"format": "cpp",
"future": "cpp",
"istream": "cpp",
"memory": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"variant": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xutility": "cpp",
"xstring": "cpp",
"string": "cpp",
"cstdint": "cpp"
},
"files.exclude": {
"**/.g.cs": true,
"**/.g.i.cs": true
}
}
-44
View File
@@ -1,44 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build Spark Client",
"type": "process",
"command": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Common7\\Tools\\VsDevCmd.bat",
"args": [
"-arch=x64",
"-host_arch=x64",
"&&",
"${workspaceRoot}\\tools\\buildspark.cmd"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new",
"showReuseMessage": false,
"clear": false,
"close": false
}
},
{
"label": "Run Spark Client",
"type": "process",
"command": "${workspaceRoot}\\build\\Debug\\bin\\net6.0-windows\\Spark.exe",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"problemMatcher": []
}
]
}
+2 -2
View File
@@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.5)
project(doc)
include(${CMAKE_SOURCE_DIR}/cmake/safe_dep_glob.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/Protobuf.cmake)
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
include(${CMAKE_SOURCE_DIR}/tools/Protobuf.cmake)
# DEPENDENCIES
set(DOC_GLOBS
-135
View File
@@ -1,135 +0,0 @@
FROM ubuntu:latest
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=America/New_York
# light deps (should be quick to download/install)
RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
gnupg \
liblttng-ust-dev \
libpcap0.8-dev \
locales \
ninja-build \
software-properties-common \
sqlite3 \
tzdata
# clang
RUN apt-get update && apt-get install -y \
clang-13 \
clang-format-13 \
clangd-13 \
clang-15 \
clang-format-15 \
clangd-15
# g++
RUN apt-get update && apt-get install -y \
g++-11 \
g++-12
# python
RUN apt-get update && apt-get install -y \
python3-pip \
python3-venv \
&& python3 -m pip install --upgrade pip
# dotnet
RUN apt-get update && apt-get install -y \
dotnet7
# ssh
RUN apt-get update && apt-get install -y \
libssh2-1-dev \
openssh-server
# cmake
RUN \
curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc 2> /dev/null \
| gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg > /dev/null \
&& apt-add-repository "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" \
&& apt-get update \
&& apt-get install -y cmake
# node
ARG node_major=18
RUN mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$node_major.x nodistro main" \
| tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& apt-get install -y nodejs
# ccache is done here so that ccache sees clang++ installed, not clang-10
RUN apt-get update && apt-get install -y \
ccache
# emumba (light deps)
RUN apt-get update && apt-get install -y \
build-essential \
doxygen \
python3-sphinx \
graphviz \
libgtest-dev \
libssl-dev \
rapidjson-dev \
zlib1g-dev \
pkg-config
# emumba (boost)
RUN apt-get update && apt-get install -y \
libboost-filesystem-dev \
libboost-program-options-dev \
libboost-test-dev \
libboost-date-time-dev
# dev utils
RUN apt-get update && apt-get install -y \
fish \
git-all \
sudo \
vim \
wget
# set up default executables (update-alternatives)
RUN \
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-13 100; \
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-13 100; \
update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-13 100; \
update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-13 100; \
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100; \
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
# Add compiler symlinks for ccache
RUN \
ln -s /usr/bin/ccache /usr/local/bin/clang; \
ln -s /usr/bin/ccache /usr/local/bin/clang++; \
ln -s /usr/bin/ccache /usr/local/bin/clang-13; \
ln -s /usr/bin/ccache /usr/local/bin/clang++-13; \
ln -s /usr/bin/ccache /usr/local/bin/clang-15; \
ln -s /usr/bin/ccache /usr/local/bin/clang++-15; \
ln -s /usr/bin/ccache /usr/local/bin/gcc; \
ln -s /usr/bin/ccache /usr/local/bin/g++; \
ln -s /usr/bin/ccache /usr/local/bin/gcc-11; \
ln -s /usr/bin/ccache /usr/local/bin/g++-11; \
ln -s /usr/bin/ccache /usr/local/bin/gcc-12; \
ln -s /usr/bin/ccache /usr/local/bin/g++-12
RUN locale-gen en_US.UTF-8
ARG docker_user="bts"
ARG docker_uid="1000"
ARG docker_pass="bts"
RUN \
groupadd -g "$docker_uid" "$docker_user" \
&& useradd \
-m \
-u "$docker_uid" \
-g "$docker_uid" \
-G sudo \
--password "$(openssl passwd -1 "$docker_pass")" \
"$docker_user"
-64
View File
@@ -1,64 +0,0 @@
[dev containers]: https://code.visualstudio.com/docs/devcontainers/containers
[dev containers ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers
[docker repo sparkd-dev]: https://hub.docker.com/repository/docker/bluetradingsystems/sparkd-dev/general
# Sparkd Dev Container
## How to create a VS Code [Dev Container][dev containers]
1. Make sure the **[Dev Containers][dev containers ext]** extension is installed
1. Make sure your Docker image is up to date
```
docker pull bluetradingsystems/sparkd-dev:latest
```
1. Copy **devcontainer.json** from this folder to **$workspaceFolder/.devcontainer/**.
1. In the VS Code Command Palette, run **Dev Containers: Reopen in Container**
---
## How to create a Sparkd Container in the terminal (without VS Code)
1. Make sure your Docker image is up to date
```
docker pull bluetradingsystems/sparkd-dev:latest
```
2. Start the container
```
# Replace <cont_name> with a name for the container. The container's
# "$HOME" dir will be mounted to "$HOME/.sparkd-dev/mounts/<cont_name> on
# the host
./start_docker.sh --container <cont_name> --mount-existing
```
* *Replace <cont_name> with a name for the container*
* *The container's `$HOME` dir will be mounted to
`$HOME/.sparkd-dev/mounts<cont_name>` on the host*<br>
* *Run: `./start_docker.sh --help` to see additional options*
3. *(Optional) Attach VS Code to the running container.*<br>
Run **Dev Containers: Attach to Running Container** from the command palette,
then select your container in the dropdown
---
## How to update the Docker image
1. Change the image by modifying the Dockerfile (don't tag arbitrary
images on your machine and push them to our repo on Docker Hub)
1. After modifying the Dockerfile, (re)build the image
```
# Run in this dir
docker build -t bluetradingsystems/sparkd-dev:latest .
```
***Note:** Your docker login must have write permissions to push to the
[sparkd-dev][docker repo sparkd-dev] repository*
2. Commit the changes to Dockerfile in git
3. Push the new image to Docker Hub
```
docker push bluetradingsystems/sparkd-dev:latest
```
***Note:** The image doesn't have to be on Docker Hub, we can simply build
it from scratch on our local machines (with the Dockerfile). However, this
takes time. It is significantly quicker to download a prebuilt image, so we
store it on a remote repository to save time when setting up a new dev
machine.*
-123
View File
@@ -1,123 +0,0 @@
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
# append to the history file, don't overwrite it
shopt -s histappend
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar
# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color|*-256color) color_prompt=yes;;
esac
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes
if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
;;
*)
;;
esac
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi
# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
# Add an "alert" alias for long running commands. Use like so:
# sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
if [ -f /usr/share/bash-completion/bash_completion ]; then
. /usr/share/bash-completion/bash_completion
elif [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
fi
if [[ ! -v XDG_CONFIG_HOME ]]; then
export XDG_CONFIG_HOME="$HOME/.config"
fi
export PATH="$HOME/.local/bin:$PATH"
-25
View File
@@ -1,25 +0,0 @@
if ! set -q XDG_CONFIG_HOME
set -g XDG_CONFIG_HOME "$HOME/.config"
end
set fish_config_home $XDG_CONFIG_HOME/fish
if test -f "$fish_config_home/aliases.fish"
source "$fish_config_home/aliases.fish"
end
# enable general colors and case insensitive search in 'less'
set -x LESS "$LESS -R -Q -I"
set -x LC_ALL en_US.UTF-8
# enable syntax highlighting in 'less'
# set highlight color scheme in 'less' (fg)(bg)
set -g LESS_TERMCAP_md (tput bold)(tput setaf 36)
set -g LESS_TERMCAP_me (tput sgr0)
set -g LESS_TERMCAP_so (tput setaf 232)(tput setab 49)
set -q TERM
if test $status != 0; or test $TERM = 'xterm'
set -g TERM 'xterm-256color'
end
# Fix impossible-to-see navy blue command text color
set fish_color_command F8F8F2
set -g PATH "$HOME/.local/bin" "$PATH"
-32
View File
@@ -1,32 +0,0 @@
{
"image": "bluetradingsystems/sparkd-dev:latest",
"initializeCommand": "${localWorkspaceFolder}/docker/sparkd/dev/devcontainer_initialize.sh",
"hostRequirements": {
"memory": "4gb",
"storage": "6gb"
},
"postCreateCommand": "/home/bts/spark/docker/sparkd/dev/devcontainer_post_create.sh",
"containerUser": "bts",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/bts/spark,type=bind",
"workspaceFolder": "/home/bts/spark",
"updateRemoteUserUID": true,
"shutdownAction": "none",
// Note: If running multiple containers on the same host, each must use a
// unique set of ports. Usually we just add 1000 to each port as needed. For
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
"runArgs": [
"--shm-size",
"2gb",
"-p",
"8000:8000",
"-p",
"8001:8001",
"-p",
"8002:8002",
"-p",
"8003:8003"
],
"mounts": [
"source=${localEnv:HOME}/.sparkd-dev/mounts/sparkd-dev,target=/home/bts,type=bind"
]
}
@@ -1,3 +0,0 @@
#!/usr/bin/env bash
mkdir -p "$HOME/.sparkd-dev/mounts/sparkd-dev"
@@ -1,33 +0,0 @@
#!/usr/bin/env bash
this_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
repo_dir="$(cd "$this_dir" && git rev-parse --show-toplevel)"
tools_dir="$repo_dir/tools"
gen_tag="GENERATED BY $(basename "${BASH_SOURCE[0]}")"
function copy_if_not_exist() {
src="$1"
dest="$2"
if [[ ! -e "$src" ]]; then
echo "Unable to copy $src to $dest. Source file doesn't exist"
exit 1
fi
if [[ ! -e "$dest" ]]; then
cp "$src" "$dest"
fi
}
# Init bashrc
echo "Initializing bashrc"
copy_if_not_exist "$this_dir/bashrc" "$HOME/.bashrc"
# Init fish config
echo "Initializing fish config"
fish_cfg_dir="$HOME/.config/fish/"
mkdir -p "$fish_cfg_dir"
exec "$tools_dir/write_tag_block.sh" \
--tag "$gen_tag" \
--replace \
--in-place \
--file "$fish_cfg_dir/config.fish" \
--text "$(cat "$this_dir/config.fish")"
-250
View File
@@ -1,250 +0,0 @@
#!/usr/bin/env bash
function _exit() {
if [[ "$#" -gt 1 ]]; then
# When there are multiple args, the last arg is the exit code.
echo "${@:1:($# - 1)}"
exit "${@: -1}"
fi
echo "$1"
exit 0
}
help_text="\
Add or update a user's uid
This script is intended to be run in a Docker container as a means to obtain
file permissions in mounted directories owned by the specified UID.
Usage: set_user.sh [OPTIONS] USER UID
Positionals:
USER The name of the user
Options:
--user USER The name of the user (defaults to 'bts')
--uid UID The new uid to give USER (defaults to '1000')
--group GROUP The primary group to give USER (defaults to USER)
--gid GID The primary gid to give USER (defaults to UID)
--pass PASSWORD The password to give USER (defaults to USER, only applies
if USER hasn't been created yet)
--modify-existing Update UID/GID even if USER already exists
-h,--help Print this help message and exit
"
function _add_group() {
if [[ "$#" -ne 2 ]]; then
_exit "'_add_group' requires exactly 2 args (group, gid). Received $#." 1
fi
local group="$1"
local gid="$2"
# Check if gid already exists
local existing_group=""
existing_group="$(getent group "$gid" | cut -d : -f 1)"
if [[ -n "$existing_group" ]]; then
if [[ "$existing_group" != "$group" ]]; then
echo "\
The gid '$gid' is already in use by group '$existing_group' (will not rename \
the group)"
fi
return
fi
# Check if group with same name already exists
local existing_gid=""
existing_gid="$(getent group "$group" | cut -d : -f 3)"
if [[ -n "$existing_gid" ]]; then
# The group already exists. Only modify if --modify-existing was specified.
if [[ "$modify_existing" -eq 0 ]]; then
_exit "\
The group '$group' already exists with gid '$existing_gid'.
Specify --modify-existing to change the gid anyways." 1
fi
echo "Changing gid for group '$group' from '$existing_gid' to '$gid'"
groupmod -g "$gid" "$group"
else
# The group doesn't exist. Add it
echo "Adding group '$group:$gid'"
groupadd -g "$gid" "$group"
fi
}
function _add_user() {
local user="$1"
local uid="$2"
local group="$3"
local gid="$4"
local pass="$5"
if [[ "$#" -ne 5 ]]; then
_exit "'_add_user' requires exactly 5 args (user, uid, group, gid, pass). \
Received $#." 1
fi
# Check if uid is being used by another user
local existing_user=""
existing_user="$(getent passwd "$uid" | cut -d : -f 1)"
if [[ -n "$existing_user" ]]; then
if [[ "$existing_user" != "$user" ]]; then
_exit "The uid '$uid' is already in use by user '$existing_user'." 1
fi
echo "The user '$user' already has the specified uid '$uid'."
# Modify gid if necessary
local existing_gid=""
existing_gid="$(id -g "$user" 2> /dev/null)"
if [[ "$existing_gid" = "$gid" ]]; then
echo "The user '$user' already has the specified gid '$gid'."
return
fi
if [[ "$modify_existing" -eq 0 ]]; then
_exit "\
The user '$user' is associated with the primary group '$existing_gid'.
Specify --modify-existing to change the gid anyways." 1
fi
echo "Changing primary group for '$user' from '$existing_gid' to '$gid'."
usermod -g "$gid" "$user"
return
fi
# Check if user with same name already exists
local existing_uid=""
existing_uid="$(id -u "$user" 2> /dev/null)"
if [[ -n "$existing_uid" ]]; then
if [[ "$existing_uid" = "$uid" ]]; then
_exit "The user '$user' already has the specified uid '$uid'" 0
fi
if [[ "$modify_existing" -eq 0 ]]; then
_exit "\
User '$user' already exists with uid '$existing_uid'.
Specify --modify-existing to change the uid anyways." 1
fi
echo "Changing uid for user '$user' from '$existing_uid' to '$uid'"
if ! usermod -u "$uid" -g "$gid" "$user"; then
_exit "usermod exited with code $?" 1
fi
return
fi
# The user doesn't exist. Add it
echo "Adding user '$user:$uid' (group='$group:$gid', password='$pass')"
useradd -m -u "$uid" -g "$gid" -G sudo --password "$(openssl passwd -1 "$pass")" "$user"
}
function _update_fish_dir_perms() {
local user="$1"
local uid="$2"
local gid="$3"
# When a fish shell session starts, a temp dir named '/tmp/fish.<user>' is
# created with the user's current uid/gid. If such a dir exists for the
# specified USER, make sure the uid/gid match the new values.
fish_dir="/tmp/fish.$user"
if [[ -d "$fish_dir" ]]; then
perms="$uid:$gid"
existing_perms="$(stat -c %u:%g "$fish_dir")"
if [[ "$existing_perms" != "$perms" ]]; then
echo "Changing uid:gid for dir '$fish_dir' from '$existing_perms' to '$perms'"
chown -R "$perms" "$fish_dir"
fi
fi
}
user=""
group=""
pass=""
declare -i uid=0
declare -i gid=0
declare -i modify_existing=0
while [[ $# -gt 0 ]]; do
case "$1" in
--user)
if [[ -z "$2" ]]; then
_exit "USER (--user) must be non-empty" 1
elif [[ "$2" = "root" ]]; then
_exit "USER (--user) cannot be 'root'" 1
fi
user="$2"
shift 2
;;
--group)
if [[ -z "$2" ]]; then
_exit "GROUP (--group) must be non-empty" 1
elif [[ "$2" = "root" ]]; then
_exit "GROUP (--group) cannot be 'root'" 1
fi
group="$2"
shift 2
;;
--uid)
if [[ -z "$2" ]]; then
_exit "UID (--uid) must be non-empty" 1
fi
uid="$2"
shift 2
;;
--gid)
if [[ -z "$2" ]]; then
_exit "GID (--gid) must be non-empty" 1
fi
gid="$2"
shift 2
;;
--pass)
if [[ -z "$2" ]]; then
_exit "PASS (--pass) must be non-empty" 1
fi
pass="$2"
shift 2
;;
--modify-existing)
modify_existing=1
shift 1
;;
-h)
;&
--help)
_exit "$help_text" 0
;;
*)
if [[ -z "$1" ]]; then
_exit "Invalid positional argument '$1'" 1
elif [[ -z "$user" ]]; then
user="$1"
elif [[ -z "$uid" ]]; then
uid="$1"
else
_exit "Unexpected argument '$1'" 1
fi
shift 1
;;
esac
done
if [[ -z "$user" ]]; then
user="bts"
fi
if [[ $uid -eq 0 ]]; then
uid=1000
fi
# In general, a user's primary group/gid matches the user's name/uid.
# If group/gid args weren't specified, set them to name/uid.
if [[ -z "$group" ]]; then
group="$user"
fi
if [[ $gid -eq 0 ]]; then
gid=$uid
fi
if [[ -z "$pass" ]]; then
pass="$user"
fi
_add_group "$group" "$gid"
_add_user "$user" "$uid" "$group" "$gid" "$pass"
_update_fish_dir_perms "$user" "$uid" "$gid"
-230
View File
@@ -1,230 +0,0 @@
#!/usr/bin/env bash
function _exit() {
if [[ "$#" -gt 1 ]]; then
# When there are multiple args, the last arg is the exit code.
echo "${@:1:($# - 1)}"
exit "${@: -1}"
fi
echo "$1"
exit 0
}
help_text="\
Start a docker dev container
Usage: start_docker.sh [OPTIONS] image container
Positionals:
image The image to instantiate
container The name for the container
Options:
-u,--user USER The user to add in the container (defaults to 'bts')
-p,--pass PASS The password for USER in the container (defaults to USER)
-i,--image IMAGE The image to instantiate (defaults to 'bluetradingsystems/sparkd-dev:latest')
-c,--container CONT The name for the container
-m,--mount MOUNT The name for the folder (outside the container) to create/mount to the home
directory in the container. Mount folders are added here: $mount_prefix
--mount-existing Mount the specified dir even if it already exists
--port <cont_port>:<host_port> OR [<cont_port>:<host_port>,...] Port mapping(s). May be
specified multiple times.
-h,--help Print this help message and exit
"
mount_prefix="$HOME/.sparkd-dev/mounts"
declare docker_user=""
declare docker_pass=""
declare image_name=""
declare cont_name=""
declare mount_name=""
declare -i mount_existing=0
declare -a port_mappings=()
while [[ $# -gt 0 ]]; do
case "$1" in
-u)
;&
--user)
if [[ -z "$2" ]]; then
_exit "User (-u,--user) must be non-empty" 1
fi
docker_user="$2"
shift 2
;;
-p)
;&
--pass)
if [[ -z "$2" ]]; then
_exit "Password (-p,--pass) must be non-empty" 1
fi
docker_pass="$2"
shift 2
;;
-i)
;&
--image)
if [[ -n "$image_name" ]]; then
_exit "Unexpected argument '$1'" 1
elif [[ -z "$2" ]]; then
_exit "Image (-i,--image) must be non-empty" 1
fi
image_name="$2"
shift 2
;;
-c)
;&
--container)
if [[ -n "$cont_name" ]]; then
_exit "Unexpected argument '$1'" 1
elif [[ -z "$2" ]]; then
_exit "Container (-c,--container) must be non-empty" 1
fi
cont_name="$2"
shift 2
;;
--port)
# Parse port(s) arg, and append the result to 'port_mappings'
# Note: port(s) may be specified in bracketed notation:
# [a:b,c:d,...,y:z]
next_ports="$(echo "$2" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,/ /g')"
if [[ "${#port_mappings[@]}" -gt 0 ]]; then
next_ports=" $next_ports"
fi
port_mappings+=("$next_ports")
shift 2
;;
-m)
;&
--mount)
if [[ -z "$2" ]]; then
echo "Mount (-m,--mount) must be non-empty"
fi
mount_name="$2"
shift 2
;;
--mount-existing)
mount_existing=1
shift 1
;;
-h)
;&
--help)
_exit "$help_text" 0
;;
*)
if [[ -z "$1" ]]; then
_exit "Invalid positional argument '$1'" "$help_text" 1
elif [[ -z "$image_name" ]]; then
image_name="$1"
elif [[ -z "$cont_name" ]]; then
cont_name="$1"
else
_exit "Unexpected argument '$1'" "$help_text" 1
fi
shift 1
;;
esac
done
if [[ -z "$image_name" ]]; then
image_name="bluetradingsystems/sparkd-dev:latest"
fi
host_name="$(hostname)"
if [[ -n "$cont_name" ]]; then
host_name="$host_name.$cont_name"
fi
if [[ -z "$mount_name" ]]; then
mount_name="$cont_name"
fi
if [[ -z "$mount_name" && -z "$cont_name" ]]; then
_exit "Please specify mount (-m,--mount) and/or container (-c,--container)" 1
fi
if [[ -z "$docker_user" ]]; then
docker_user="bts"
fi
if [[ "$docker_user" = "root" ]]; then
_exit "USER (--user) cannot be 'root'" 1
fi
cont_home="/home/$docker_user"
home_src="$mount_prefix/$mount_name"
home_tgt="$cont_home"
this_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
repo_src="$(cd "$this_dir" && git rev-parse --show-toplevel)"
repo_tgt="$cont_home/spark"
working_dir="$cont_home"
if [[ $mount_existing -eq 0 && -e "$home_src" ]]; then
# Don't overwrite existing dir (might be mounted in another container)
_exit "Mount dir '$home_src' already exists (use --mount-existing to mount \
an existing directory)" 1
fi
mkdir -p "$home_src"
if [[ "${#port_mappings[@]}" -eq 0 ]]; then
port_mappings=("8000:8000" "8001:8001" "8002:8002" "8003:8003")
fi
# Adapt port_mappings into args for `docker run` by prepending "-p " to each
# item
declare -a docker_port_args=()
for port_mapping in "${port_mappings[@]}"; do
docker_port_args+=("-p $port_mapping")
done
echo "Starting container '$cont_name'
image_name : $image_name
cont_name : $cont_name
home_src : $home_src
home_tgt : $home_tgt
repo_src : $repo_src
repo_tgt : $repo_tgt
working dir : $working_dir
ports : ${port_mappings[*]}
"
# DON'T wrap $docker_port_args in quotes. We want bash to split args by
# whitespace
docker run \
--net bridge \
-h "$host_name" \
-e "TZ=America/New_York" \
--cap-add=SYS_PTRACE \
--security-opt seccomp=unconfined \
--name "$cont_name" \
-v "$home_src:$home_tgt" \
-v "$repo_src:$repo_tgt" \
-w "$working_dir" \
${docker_port_args[*]} \
--shm-size=4024m \
-t \
-i \
-d \
-e DISPLAY="$DISPLAY" \
"$image_name"
# Add user w/ same uid and gid as the current user outside of docker, so the
# user inside the docker has file permissions in the mounted dir.
docker_group="$docker_user"
docker_uid="$(id -u "$USER")"
docker_gid="$(id -g "$USER")"
if [[ -z "$docker_pass" ]]; then
docker_pass="$docker_user"
fi
docker exec -u root -it "$cont_name" "$repo_tgt/docker/sparkd/dev/set_user.sh" \
--user "$docker_user" \
--uid "$docker_uid" \
--group "$docker_group" \
--gid "$docker_gid" \
--pass "$docker_pass"
# Run same script VS Code runs to initialize the container
docker exec -u "$docker_user" -it "$cont_name" "$repo_tgt/docker/sparkd/dev/devcontainer_post_create.sh"
-64
View File
@@ -1,64 +0,0 @@
#!/usr/bin/env bash
function print_help_and_exit() {
cat <<EOF
Open a shell inside the docker container
Usage: docker_shell.sh [OPTIONS] [CONTAINER]
Options:
-u,--user The user to login with in the container (defaults to the current user)
-c,--container The container name (may also be specified as positional arg)
-h,--help Print this help message and exit
EOF
exit "$1"
}
declare docker_user=""
declare cont_name=""
while [[ $# -gt 0 ]]; do
case "$1" in
-u)
;&
--user)
if [[ -z "$2" ]]; then
echo "User (-u,--user) must be non-empty"
print_help_and_exit 1
fi
docker_user="$2"
shift 2
;;
-h)
;&
--help)
print_help_and_exit 0
;;
-c)
;&
--container)
shift 1
;&
*)
if [[ ! -z "$cont_name" ]]; then
echo "Unexpected argument '$1'"
print_help_and_exit 1
fi
cont_name="$1"
shift 1
;;
esac
done
if [[ -z "$cont_name" ]]; then
echo "Please specify the container ([-c,--container])"
print_help_and_exit 1
fi
if [[ -z "$docker_user" ]]; then
docker_user="$USER"
fi
docker exec -u "$docker_user" -it "$cont_name" bash
-233
View File
@@ -1,233 +0,0 @@
#!/usr/bin/env bash
mount_prefix="$HOME/.spark-dev/mounts"
function print_help_and_exit() {
cat <<EOF
Start a docker dev container
Usage: start_docker.sh [OPTIONS] image container
Positionals:
image The image to instantiate
container The name for the container
Options:
-u,--user USER The user to add in the container (defaults to the current user)
-p,--pass PASS The password for USER in the container (defaults to USER)
-i,--image IMAGE The image to instantiate
-c,--container CONT The name for the container
-m,--mount MOUNT The name for the folder (outside the container) to create/mount to the home
directory in the container. Mount folders are added here: $mount_prefix
--mount-existing Mount the specified dir even if it already exists
--port <cont_port>:<host_port> OR [<cont_port>:<host_port>,...] Port mapping(s). May be
specified multiple times.
-h,--help Print this help message and exit
EOF
exit "$1"
}
declare docker_user=""
declare docker_pass=""
declare image_name=""
declare cont_name=""
declare mount_name=""
declare -i mount_existing=0
declare -a port_mappings=()
while [[ $# -gt 0 ]]; do
case "$1" in
-u)
;&
--user)
if [[ -z "$2" ]]; then
echo "User (-u,--user) must be non-empty"
print_help_and_exit 1
fi
docker_user="$2"
shift 2
;;
-p)
;&
--pass)
if [[ -z "$2" ]]; then
echo "Password (-p,--pass) must be non-empty"
print_help_and_exit 1
fi
docker_pass="$2"
shift 2
;;
-i)
;&
--image)
if [[ -n "$image_name" ]]; then
echo "Unexpected argument '$1'"
print_help_and_exit 1
elif [[ -z "$2" ]]; then
echo "Image (-i,--image) must be non-empty"
print_help_and_exit 1
fi
image_name="$2"
shift 2
;;
-c)
;&
--container)
if [[ -n "$cont_name" ]]; then
echo "Unexpected argument '$1'"
print_help_and_exit 1
elif [[ -z "$2" ]]; then
echo "Container (-c,--container) must be non-empty"
print_help_and_exit 1
fi
cont_name="$2"
shift 2
;;
--port)
# Parse port(s) arg, and append the result to 'port_mappings'
# Note: port(s) may be specified in bracketed notation:
# [a:b,c:d,...,y:z]
next_ports="$(echo "$2" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,/ /g')"
if [[ "${#port_mappings[@]}" -gt 0 ]]; then
next_ports=" $next_ports"
fi
port_mappings+=("$next_ports")
shift 2
;;
-m)
;&
--mount)
if [[ -z "$2" ]]; then
echo "Mount (-m,--mount) must be non-empty"
fi
mount_name="$2"
shift 2
;;
--mount-existing)
mount_existing=1
shift 1
;;
-h)
;&
--help)
print_help_and_exit 0
;;
*)
if [[ -z "$1" ]]; then
echo "Invalid positional argument '$1'"
print_help_and_exit 1
elif [[ -z "$image_name" ]]; then
image_name="$1"
elif [[ -z "$cont_name" ]]; then
cont_name="$1"
else
echo "Unexpected argument '$1'"
print_help_and_exit 1
fi
shift 1
;;
esac
done
if [[ -z "$image_name" ]]; then
image_name="bluetradingsystems/spark-crypto-dev"
fi
host_name="$(hostname)"
if [[ -n "$cont_name" ]]; then
host_name="$host_name.$cont_name"
fi
if [[ -z "$mount_name" ]]; then
mount_name="$cont_name"
fi
if [[ -z "$mount_name" && -z "$cont_name" ]]; then
echo "Please specify mount (-m,--mount) and/or container (-c,--container)"
print_help_and_exit 1
fi
if [[ -z "$docker_user" ]]; then
docker_user="$USER"
fi
home_src=$mount_prefix/$mount_name
home_tgt=$HOME
repo_src=$(git rev-parse --show-superproject-working-tree)
repo_tgt=$HOME/$(basename "$repo_src")
working_dir=$HOME
if [[ $mount_existing -eq 0 && -e "$home_src" ]]; then
# Don't overwrite existing dir (might be mounted in another container)
echo "Mount dir '$home_src' already exists (use --mount-existing to mount an existing directory)"
print_help_and_exit 1
fi
mkdir -p "$home_src"
if [[ "${#port_mappings[@]}" -eq 0 ]]; then
port_mappings=("8000:8000" "8001:8001" "8002:8002" "8003:8003")
fi
# Adapt port_mappings into args for `docker run` by prepending "-p " to each
# item
declare -a docker_port_args=()
for port_mapping in "${port_mappings[@]}"; do
docker_port_args+=("-p $port_mapping")
done
echo "Starting container '$cont_name'
image_name : $image_name
cont_name : $cont_name
home_src : $home_src
home_tgt : $home_tgt
repo_src : $repo_src
repo_tgt : $repo_tgt
working dir : $working_dir
ports : ${port_mappings[*]}
"
# DON'T wrap $docker_port_args in quotes. We want bash to split args by
# whitespace
docker run \
--net bridge \
-h "$host_name" \
-e "TZ=America/New_York" \
--cap-add=SYS_PTRACE \
--security-opt seccomp=unconfined \
--name "$cont_name" \
-v "$home_src:$home_tgt" \
-v "$repo_src:$repo_tgt" \
-w "$working_dir" \
${docker_port_args[*]} \
--shm-size=4024m \
-t \
-i \
-d \
-e DISPLAY="$DISPLAY" \
"$image_name" \
bash
# Add user w/ same uid and gid as the current user outside of docker, so the
# user inside the docker has file permissions in the mounted dir.
docker_group="$docker_user"
docker_user_uid="$(id -u "$USER")"
docker_user_gid="$(id -g "$USER")"
echo "Adding user in container '$cont_name'
name : $docker_user
uid : $docker_user_uid
gid : $docker_user_gid"
if [[ -z "$docker_pass" ]]; then
docker_pass="$docker_user"
echo "Giving user '$docker_user' a secure password: $docker_pass"
fi
docker exec -u root -it "$cont_name" groupadd -g "$docker_user_gid" "$docker_group"
docker exec -u root -it "$cont_name" useradd -u "$docker_user_uid" -g "$docker_user_gid" "$docker_user"
docker exec -u root -it "$cont_name" usermod --password "$(openssl passwd -1 "$docker_pass")" "$docker_user"
docker exec -u root -it "$cont_name" usermod -aG sudo "$docker_user"
docker exec -u root -it "$cont_name" "$repo_tgt/externals/src/emumba/install_emumba-deribit-connector.sh"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "6.0.200",
"version": "2.1.508",
"rollForward": "latestMajor"
}
}
-1
View File
@@ -12,7 +12,6 @@ pycodestyle==2.7.0
pycparser==2.20
pylint==2.10.2
PyMeta3==0.5.1
termcolor==1.1.0
toml==0.10.2
wrapt==1.12.1
yapf==0.31.0
+22 -32
View File
@@ -1,10 +1,10 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32922.545
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
@@ -12,17 +12,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sparkcmd", "src\client\spar
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
ProjectSection(ProjectDependencies) = postProject
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25} = {FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40} = {9FA60215-26AF-3EB7-AB89-D4F4B126EF40}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{ECD78CC5-138F-4809-A764-2358C2AC2E82}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_websocket", "src\client\spark_websocket\spark_websocket.csproj", "{3201359D-C071-4089-9EDE-B04D759F1508}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api_server", "src\client\spark_api_server\spark_api_server.csproj", "{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -39,14 +37,14 @@ Global
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Release|x64.Build.0 = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Debug|x64.ActiveCfg = Debug|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Debug|x64.Build.0 = Debug|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Release|x64.ActiveCfg = Release|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Release|x64.Build.0 = Release|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Debug|x64.ActiveCfg = Debug|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Debug|x64.Build.0 = Debug|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Release|x64.ActiveCfg = Release|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Release|x64.Build.0 = Release|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.Build.0 = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.MinSizeRel|x64.ActiveCfg = Release|x64
@@ -71,6 +69,14 @@ Global
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Release|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Debug|x64.ActiveCfg = Debug|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Debug|x64.Build.0 = Debug|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Release|x64.ActiveCfg = Release|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Release|x64.Build.0 = Release|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.Build.0 = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x64.ActiveCfg = Release|x64
@@ -87,22 +93,6 @@ Global
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Release|x64.Build.0 = Release|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x64.Build.0 = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Debug|x64.ActiveCfg = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Debug|x64.Build.0 = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.MinSizeRel|x64.ActiveCfg = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.MinSizeRel|x64.Build.0 = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Release|x64.ActiveCfg = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Release|x64.Build.0 = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.RelWithDebInfo|x64.Build.0 = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Debug|x64.ActiveCfg = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Debug|x64.Build.0 = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.MinSizeRel|x64.ActiveCfg = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.MinSizeRel|x64.Build.0 = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Release|x64.ActiveCfg = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Release|x64.Build.0 = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.RelWithDebInfo|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+52 -55
View File
@@ -1,55 +1,52 @@
<Project>
<PropertyGroup>
<ComputerSpecificConfig>..\..\tools\override\$(computername).props</ComputerSpecificConfig>
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
<Externals>$(SparkRoot)\externals\windows</Externals>
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
<ErrorReport>None</ErrorReport>
<Prefer32Bit>False</Prefer32Bit>
<Deterministic>true</Deterministic>
<EnableDefaultContentItems>False</EnableDefaultContentItems>
<ShowMissingItemTypes>false</ShowMissingItemTypes>
<Configuration Condition="'$(configuration)'==''">Debug</Configuration>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<LangVersion>11</LangVersion>
</PropertyGroup>
<PropertyGroup Label="Output">
<BaseOutputPath>$(SparkRoot)\build\$(Configuration)</BaseOutputPath>
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
<ProtoOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</ProtoOutputPath>
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
</PropertyGroup>
<Import
Project="$(ComputerSpecificConfig)"
Condition="Exists($(ComputerSpecificConfig))" />
<PropertyGroup Label="ProtoBuf">
<ProtocPluginBase>$(SparkRoot)\src\lib\api\service_compiler</ProtocPluginBase>
<protoc>$(Externals)\bin\protoc.exe</protoc>
<protogen>$(Externals)\bin\protogen.exe</protogen>
</PropertyGroup>
<ItemDefinitionGroup>
<Proto2></Proto2>
<Proto3>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCS>%(OutBase)%(FileName).cs</OutCS>
<OutCSClient>%(OutBase)%(FileName).client.pb.cs</OutCSClient>
<OutCSServer>%(OutBase)%(FileName).server.pb.cs</OutCSServer>
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
<CompileOptions></CompileOptions>
</Proto3>
<Proto3Excel>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCSExcel>%(OutBase)%(FileName).excel.pb.cs</OutCSExcel>
<CompileOptions></CompileOptions>
</Proto3Excel>
</ItemDefinitionGroup>
</Project>
<Project>
<PropertyGroup>
<ComputerSpecificConfig>..\..\tools\override\$(computername).props</ComputerSpecificConfig>
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
<Externals>$(SparkRoot)\externals\windows</Externals>
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
<ErrorReport>None</ErrorReport>
<Prefer32Bit>False</Prefer32Bit>
<Deterministic>true</Deterministic>
<EnableDefaultContentItems>False</EnableDefaultContentItems>
<ShowMissingItemTypes>false</ShowMissingItemTypes>
<Configuration Condition="'$(configuration)'==''">Debug</Configuration>
</PropertyGroup>
<PropertyGroup Label="Output">
<BaseOutputPath>$(SparkRoot)\build\$(Configuration)</BaseOutputPath>
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
</PropertyGroup>
<Import
Project="$(ComputerSpecificConfig)"
Condition="Exists($(ComputerSpecificConfig))" />
<PropertyGroup Label="ProtoBuf">
<ProtocPluginBase>$(SparkRoot)\src\lib\api\service_compiler</ProtocPluginBase>
<protoc>$(Externals)\bin\protoc.exe</protoc>
<protogen>$(Externals)\bin\protogen.exe</protogen>
</PropertyGroup>
<ItemDefinitionGroup>
<Proto2></Proto2>
<Proto3>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCS>%(OutBase)%(FileName).cs</OutCS>
<OutCSClient>%(OutBase)%(FileName).client.pb.cs</OutCSClient>
<OutCSServer>%(OutBase)%(FileName).server.pb.cs</OutCSServer>
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
<CompileOptions></CompileOptions>
</Proto3>
<Proto3Excel>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCSExcel>%(OutBase)%(FileName).excel.pb.cs</OutCSExcel>
<CompileOptions></CompileOptions>
</Proto3Excel>
</ItemDefinitionGroup>
</Project>
+2 -11
View File
@@ -52,7 +52,8 @@
<Exec Command="set PYTHONPATH=$(SparkRoot)\.venv\Scripts\python" />
<Exec
Command="
$(protoc) %* %(Proto3.FullPath) ^
$(SparkRoot)\tools\build_protos.cmd ^
%(Proto3.FullPath) ^
--error_format=msvs ^
--proto_path=$(SourceRoot) ^
--plugin=&quot;protoc-gen-service=$(ProtocPluginBase)\plugin.cmd&quot; ^
@@ -67,16 +68,6 @@
</ItemGroup>
</Target>
<Target Name="RemoveSystemRuntimeFromRefPack"
BeforeTargets="_HandlePackageFileConflicts"
Condition="'@(Reference -> WithMetadataValue('NugetPackageId', 'System.Runtime.Experimental'))' != ''">
<ItemGroup>
<Reference Remove="@(Reference)"
Condition="$([System.String]::Copy(%(Reference.Identity)).Contains('System.Runtime.dll')) and
'%(Reference.NuGetPackageId)' == 'Microsoft.NETCore.App.Ref'" />
</ItemGroup>
</Target>
<Target
Name="AfterClean"
AfterTargets="Clean">
@@ -1,7 +1,4 @@
#nullable enable
namespace mbts.spark {
using bts.api.analytics;
using mbts.analytics;
using mbts.ui;
using mbts.ui.shell;
@@ -15,9 +12,9 @@ namespace mbts.spark {
readonly AnalyticsExpirationParameters _committed_value;
readonly Property<bool> _is_selected;
readonly Property<ExpirationDriverImmutable?> _driver;
readonly Property<SecurityExpiration?> _deliverable;
readonly Property<SecurityExpiration?> _roll_schedule;
readonly Property<SecurityExpiration> _driver;
readonly Property<SecurityExpiration> _deliverable;
readonly Property<SecurityExpiration> _roll_schedule;
readonly Property<double> _roll_target;
readonly Property<double> _riskless_rate;
readonly Property<double> _settlement_price;
@@ -36,19 +33,18 @@ namespace mbts.spark {
bool _is_last_row_of_month_group;
public AnalyticsExpirationParametersRowVM(
AnalyticsExpirationParametersVM owner, AnalyticsExpirationParameters committed_value) {
this.owner = owner;
_committed_value = committed_value;
public AnalyticsExpirationParametersRowVM(AnalyticsExpirationParametersVM owner, AnalyticsExpirationParameters committed_value) {
this.owner = owner ?? throw new ArgumentNullException(nameof(owner));
_committed_value = committed_value ?? throw new ArgumentNullException(nameof(committed_value));
security = committed_value.security ?? throw new ArgumentException("security must be non-null", nameof(committed_value));
_is_selected = backing(nameof(is_selected), false);
_is_selected.Changed += (oldval, newval) => is_selected_changed?.Invoke(newval);
var is_dirty_srcs = new List<IObservable<bool>>();
(_driver, _is_driver_dirty) = mk_cell_props<ExpirationDriverImmutable?>(nameof(driver), committed_value.driver);
(_deliverable, _is_deliverable_dirty) = mk_cell_props<SecurityExpiration?>(nameof(deliverable), committed_value.deliverable_security);
(_roll_schedule, _is_roll_schedule_dirty) = mk_cell_props<SecurityExpiration?>(nameof(roll_schedule), committed_value.roll_schedule_expiration);
(_driver, _is_driver_dirty) = mk_cell_props(nameof(driver), committed_value.driver_security);
(_deliverable, _is_deliverable_dirty) = mk_cell_props(nameof(deliverable), committed_value.deliverable_security);
(_roll_schedule, _is_roll_schedule_dirty) = mk_cell_props(nameof(roll_schedule), committed_value.roll_schedule_expiration);
(_roll_target, _is_roll_target_dirty) = mk_cell_props(nameof(roll_target), committed_value.roll_target);
(_riskless_rate, _is_riskless_rate_dirty) = mk_cell_props(nameof(riskless_rate), committed_value.riskless_rate);
(_settlement_price, _is_settlement_price_dirty) = mk_cell_props(nameof(settlement_price), committed_value.settlement_price);
@@ -68,22 +64,13 @@ namespace mbts.spark {
}
}
public event Action<bool>? is_selected_changed;
public event Action<bool> is_selected_changed;
public AnalyticsExpirationParametersVM owner { get; }
public SecurityExpiration security { get; }
public ExpirationDriverImmutable? driver {
get => _driver.Value;
set => _driver.set(value);
}
public SecurityExpiration? deliverable {
get => _deliverable.Value;
set => _deliverable.set(value);
}
public SecurityExpiration? roll_schedule {
get => _roll_schedule.Value;
set => _roll_schedule.set(value);
}
public SecurityExpiration driver { get => _driver.Value; set => _driver.set(value); }
public SecurityExpiration deliverable { get => _deliverable.Value; set => _deliverable.set(value); }
public SecurityExpiration roll_schedule { get => _roll_schedule.Value; set => _roll_schedule.set(value); }
public AnalyticsExpirationParameters original_params => _committed_value;
public double roll_target { get => _roll_target.Value; set => _roll_target.set(value); }
@@ -115,15 +102,15 @@ namespace mbts.spark {
public AnalyticsExpirationParameters candidate_value => new(
security,
driver,
deliverable?.is_empty() ?? false ? null : deliverable,
roll_schedule?.is_empty() ?? false ? null : roll_schedule,
deliverable.is_empty() ? null : deliverable,
roll_schedule.is_empty() ? null : roll_schedule,
roll_target,
riskless_rate,
settlement_price,
delta_beta,
vega_beta);
public IObservable<SecurityExpiration?> observe_roll_schedule() =>
public IObservable<SecurityExpiration> observe_roll_schedule() =>
_roll_schedule.observe_current(true);
public IObservable<bool> observe_roll_schedule_dirty() =>
@@ -133,7 +120,7 @@ namespace mbts.spark {
_is_dirty.observe_current(true);
public void revert() {
driver = _committed_value.driver;
driver = _committed_value.driver_security;
deliverable = _committed_value.deliverable_security;
roll_schedule = _committed_value.roll_schedule_expiration;
roll_target = _committed_value.roll_target;
@@ -146,11 +133,11 @@ namespace mbts.spark {
public override string ToString() =>
$"sec: {security}; drv: {driver}; dlv: {deliverable}; roll: {roll_schedule} @ {roll_target}";
public bool Equals(AnalyticsExpirationParametersRowVM? other) =>
public bool Equals(AnalyticsExpirationParametersRowVM other) =>
other is not null &&
security.Equals(other.security);
public override bool Equals(object? obj) =>
public override bool Equals(object obj) =>
Equals(obj as AnalyticsExpirationParametersRowVM);
public override int GetHashCode() =>
@@ -1,8 +1,6 @@
#nullable enable
namespace mbts.spark {
namespace mbts.spark {
using bts.api;
using bts.api.analytics;
using bts.collections;
using bts.reactive;
using bts.utils;
using mbts;
@@ -14,18 +12,11 @@ namespace mbts.spark {
using mbts.ui.utility;
using mbts.utils;
using Spark.Core;
using Spark.Core.Collections;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using Spark.ViewModel.Commands;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
@@ -35,93 +26,70 @@ namespace mbts.spark {
class AnalyticsExpirationParametersVM : DisposableViewModelBase, IHandleRowSelection {
readonly IBus _bus;
readonly ObservableCollection<AnalyticsExpirationParameters> _parameters = new();
readonly DeferrableAction _sync_visible_rows;
readonly Property<AnalyticsExpirationParametersRowVM?> _copy_row_source;
readonly Property<bool> _auto_copy_rolls_is_checked;
readonly Property<bool> _auto_copy_rates_is_checked;
readonly DeferrableActionWrapper _sync_visible_rows;
readonly Property<AnalyticsExpirationParametersRowVM> _copy_row_source;
readonly DelegateProperty<bool> _is_dirty;
readonly ObservableHashSet<AnalyticsExpirationParametersRowVM> _selected_rows = new();
readonly DeferrableAction _raise_selected_rows_changed;
readonly IRaiseCanExecuteChangedCommand _set_driver_to_nearest_exp_cmd;
IList<string>? _pending_selected_symbols;
readonly HashSet<AnalyticsExpirationParametersRowVM> _selected_rows = new();
AnalyticsExpirationParametersMap? _parameter_map;
AnalyticsExpirationParametersMap _parameter_map;
bool _auto_copy_rolls_is_checked;
bool _auto_copy_rates_is_checked;
(ICommand cmd, IDisposable can_exe_sub)? _smart_copy_roll_target_to_selection_cmd;
(ICommand cmd, IDisposable can_exe_sub)? _smart_copy_riskless_rate_to_selection_cmd;
public AnalyticsExpirationParametersVM(
IBus bus, AnalyticsExpirationParametersSettings startup_data) {
_bus = bus;
_auto_copy_rates_is_checked = backing(nameof(auto_copy_rates_is_checked), false);
_auto_copy_rolls_is_checked = backing(nameof(auto_copy_rolls_is_checked), false);
_raise_selected_rows_changed = new(() => selected_rows_changed?.Invoke(_selected_rows));
public AnalyticsExpirationParametersVM(IBus bus, AnalyticsExpirationParametersSettings startup_data) {
_bus = bus ?? throw new ArgumentNullException(nameof(bus));
var selected_symbol_ref_count = new RefCountMap<string>();
_sync_visible_rows = new(() =>
visible_rows.sync_with(
rows.Where(x => selected_symbol_ref_count.ContainsKey(x.security.symbol))));
visible_rows.sync_with(rows.Where(x => selected_symbol_ref_count.ContainsKey(x.security.symbol))));
non_options_with_empty = new ObservableAggregateCollection<SecurityExpiration>(
new() { SecurityExpiration.empty }, non_options);
_copy_row_source = backing<AnalyticsExpirationParametersRowVM?>(nameof(copy_row_source));
_copy_row_source = backing<AnalyticsExpirationParametersRowVM>(nameof(copy_row_source));
_is_dirty = backing(nameof(is_dirty), () => rows.Any(r => r.is_dirty));
if (startup_data is not null) {
_pending_selected_symbols = startup_data.selected_symbols;
if (!startup_data.selected_symbols.is_null_or_empty()) {
selected_symbols.AddRange(startup_data.selected_symbols);
}
auto_copy_rolls_is_checked = startup_data.auto_copy_rolls_is_checked;
auto_copy_rates_is_checked = startup_data.auto_copy_rates_is_checked;
}
var explicit_drivers = new ObservableCollection<ExpirationDriverImmutable>();
drivers = ObservableAggregateCollection.create(
new() { new ExpirationDriverImmutable(new() { TimeWeightedInterpolated = new() }) },
explicit_drivers);
_set_driver_to_nearest_exp_cmd = new Command(
() => _set_driver_to_nearest_exp(_selected_rows), () => _selected_rows.Count > 0);
commit_cmd = ObservantCommand.create(_commit, out var can_commit_observer);
revert_cmd = ObservantCommand.create(_revert, out var can_revert_observer);
selected_rows_changed += _ => _set_driver_to_nearest_exp_cmd.RaiseCanExecuteChanged();
_dispose = Disposable.Create(
_selected_rows.Subscribe((s, e) => _raise_selected_rows_changed.Invoke()),
ObservableAutoWrapper.connect(_parameters, rows, p => new(this, p), r => r.Dispose()),
ObservableAutoWrapper.connect(
non_options,
explicit_drivers,
sec => new ExpirationDriverImmutable(new() { Security = sec.to_api_sec_exp() })),
rows.Subscribe((s, e) => _sync_visible_rows.Invoke()),
rows.Subscribe((s, e) => _sync_visible_rows.execute()),
_bus.subscribe(SparkTags.ExpirationParametersInputTag).OnDispatcher().Subscribe(
CompositeObserver.create(
Observer.Create<AnalyticsExpirationParametersMap>(_on_parameter_map),
can_commit_observer.wrap<bool, AnalyticsExpirationParametersMap>(m => m is not null))),
ObservableAutoWrapper.connect_item_hooks(selected_symbols, sym => {
if (selected_symbol_ref_count.increment_adds_key(sym)) {
_sync_visible_rows.Invoke();
_sync_visible_rows.execute();
}
return Disposable.Create(() => {
if (selected_symbol_ref_count.decrement_removes_key(sym)) {
_sync_visible_rows.Invoke();
_sync_visible_rows.execute();
}
});
}),
ObservableAutoWrapper.connect_item_hooks(
rows, r => r.observe_is_dirty().Subscribe(_ => _is_dirty.refresh())),
ObservableAutoWrapper.connect_item_hooks(rows, r => r.observe_is_dirty().Subscribe(_ => _is_dirty.refresh())),
_is_dirty.observe_current(true).OnDispatcher().Subscribe(can_revert_observer),
Disposable.wrap_collection(rows),
Disposable.Create(() => {
rows.DisposeAll();
_smart_copy_roll_target_to_selection_cmd?.can_exe_sub?.Dispose();
_smart_copy_riskless_rate_to_selection_cmd?.can_exe_sub?.Dispose();
}));
}
public event Action<IReadOnlySet<AnalyticsExpirationParametersRowVM>>? selected_rows_changed;
public ObservableCollection<SecurityExpiration> non_options { get; } = new();
public IReadOnlyObservableCollection<ExpirationDriverImmutable> drivers { get; }
public ObservableCollection<SecurityExpiration> non_options_with_empty { get; } = new();
public ObservableCollection<AnalyticsExpirationParametersRowVM> rows { get; } = new();
public ObservableCollection<AnalyticsExpirationParametersRowVM> visible_rows { get; } = new();
public AnalyticsExpirationParametersRowVM? copy_row_source {
public AnalyticsExpirationParametersRowVM copy_row_source {
get => _copy_row_source.Value;
set => _copy_row_source.set(value);
}
@@ -132,29 +100,18 @@ namespace mbts.spark {
public bool is_dirty => _is_dirty.Value;
public bool auto_copy_rolls_is_checked {
get => _auto_copy_rolls_is_checked.Value;
set => _auto_copy_rolls_is_checked.set(value);
get => _auto_copy_rolls_is_checked;
set => Set(ref _auto_copy_rolls_is_checked, value);
}
public IObservable<bool> observe_auto_copy_rolls() =>
_auto_copy_rolls_is_checked.observe_current(true);
public bool auto_copy_rates_is_checked {
get => _auto_copy_rates_is_checked.Value;
set => _auto_copy_rates_is_checked.set(value);
get => _auto_copy_rates_is_checked;
set => Set(ref _auto_copy_rates_is_checked, value);
}
public IObservable<bool> observe_auto_copy_rates() =>
_auto_copy_rates_is_checked.observe_current(true);
public ICommand toggle_auto_copy_rates_cmd =>
new Command(() => auto_copy_rates_is_checked = !auto_copy_rates_is_checked);
public ICommand toggle_auto_copy_rolls_cmd =>
new Command(() => auto_copy_rolls_is_checked = !auto_copy_rolls_is_checked);
public ICommand commit_cmd { get; }
public ICommand revert_cmd { get; }
public ICommand set_driver_to_nearest_exp_cmd => _set_driver_to_nearest_exp_cmd;
public ICommand copy_driver_to_selection_cmd => _copy_to_selection_cmd((f, t) => t.driver = f.driver);
public ICommand copy_deliverable_to_selection_cmd => _copy_to_selection_cmd((f, t) => t.deliverable = f.deliverable);
public ICommand copy_roll_schedule_to_selection_cmd => _copy_to_selection_cmd((f, t) => t.roll_schedule = f.roll_schedule);
@@ -181,7 +138,7 @@ namespace mbts.spark {
public IEnumerable<SecurityExpiration> get_smart_copy_roll_exps(
SecurityExpiration roll_schedule_expiration) {
foreach (var (exp, exp_params) in (_parameter_map?.exp_map).empty_if_null()) {
foreach (var (exp, exp_params) in _parameter_map?.exp_map.empty_if_null()) {
if (exp_params.roll_schedule_expiration.Equals(roll_schedule_expiration)) {
yield return exp;
}
@@ -195,38 +152,17 @@ namespace mbts.spark {
auto_copy_rolls_is_checked = auto_copy_rolls_is_checked
};
void _set_driver_to_nearest_exp(IEnumerable<AnalyticsExpirationParametersRowVM> rows) {
Func<ExpirationDriverImmutable, bool> is_unexpired = (d) => {
var st = d.Security.to_sec_exp().security_tuple.to_api_sec_tuple();
var exp = d.Security.Expiration.date_time();
return SecurityExpirationHelper.is_unexpired(st, exp);
};
foreach (var r in rows) {
if (drivers
.Where(d => d.HasSecurity && d.Security.Symbol == r.security.symbol)
.Where(d => is_unexpired(d))
.TryGetMinElement(
d => (r.security.expiration - d.Security.Expiration.date_time()).Ticks.abs(),
out var nearest_exp)) {
r.driver = nearest_exp;
}
}
}
Action<AnalyticsExpirationParametersRowVM> _copy_to_selection(
Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
Action<AnalyticsExpirationParametersRowVM> _copy_to_selection(Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
from_row => {
foreach (var to_row in _selected_rows) {
copy_to_row(from_row, to_row);
}
};
ICommand _copy_to_selection_cmd(
Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
ICommand _copy_to_selection_cmd(Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
Command.create(_copy_to_selection(copy_to_row));
Action<AnalyticsExpirationParametersRowVM> _smart_copy_to_selection(
Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
Action<AnalyticsExpirationParametersRowVM> _smart_copy_to_selection(Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
from_row => {
foreach (var to_row in _get_smart_copy_roll_target_rows(from_row)) {
copy_to_row(from_row, to_row);
@@ -235,26 +171,21 @@ namespace mbts.spark {
(ICommand cmd, IDisposable can_exe_sub) _smart_copy_to_selection_cmd(
Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) {
var copy_fn = _smart_copy_to_selection(copy_to_row);
var cmd = ObservantCommand.create<AnalyticsExpirationParametersRowVM?>(
r => copy_fn(r ?? throw new InvalidOperationException()),
observe_can_execute,
out var can_exe_observer);
var can_exe_sub = _copy_row_source.observe_current(true).OnDispatcher()
.Subscribe(can_exe_observer);
var cmd = ObservantCommand.create(
_smart_copy_to_selection(copy_to_row), observe_can_execute, out var can_exe_observer);
var can_exe_sub = _copy_row_source.observe_current(true).OnDispatcher().Subscribe(can_exe_observer);
return (cmd, can_exe_sub);
static IObservable<bool> observe_can_execute(AnalyticsExpirationParametersRowVM? row) =>
static IObservable<bool> observe_can_execute(AnalyticsExpirationParametersRowVM row) =>
row is null ?
Observable.Return(false) :
row.observe_roll_schedule()
.CombineLatest(row.observe_roll_schedule_dirty(), (rs, rs_dirty) => (rs, rs_dirty))
.Select(tup => !tup.rs?.is_empty() ?? false && !tup.rs_dirty)
.Select(tup => !tup.rs.is_empty() && !tup.rs_dirty)
.OnDispatcher();
}
IEnumerable<AnalyticsExpirationParametersRowVM> _get_smart_copy_roll_target_rows(
AnalyticsExpirationParametersRowVM row) {
IEnumerable<AnalyticsExpirationParametersRowVM> _get_smart_copy_roll_target_rows(AnalyticsExpirationParametersRowVM row) {
var target_exps = get_smart_copy_roll_exps(
row.original_params.roll_schedule_expiration)
.ToHashSet();
@@ -276,13 +207,11 @@ namespace mbts.spark {
}
}
[MemberNotNull(nameof(_parameter_map))]
void _on_parameter_map(AnalyticsExpirationParametersMap parameter_map) {
bool is_first_update = _parameter_map is null;
_parameter_map = parameter_map;
_parameter_map = parameter_map ?? throw new ArgumentNullException(nameof(parameter_map));
_parameters.sync_with(parameter_map.get_sorted_exps().Select(parameter_map.get_parameter));
non_options.sync_with(
parameter_map.get_sorted_exps().Where(exp => exp.security_type != SecurityType.Option));
non_options.sync_with(parameter_map.get_sorted_exps().Where(exp => exp.security_type != SecurityType.Option));
// Set `is_last_row_of_month_group` for each row
foreach (var exp_rows in rows.GroupBy(r => r.security.expiration.Month)) {
@@ -294,10 +223,10 @@ namespace mbts.spark {
}
symbols.sync_with(_parameters.Select(p => p.security.symbol).Distinct().Ordered());
using var _ = _sync_visible_rows.defer();
using var _ = _sync_visible_rows.defer_execution();
if (is_first_update) {
selected_symbols.sync_with(_pending_selected_symbols?.Intersect(symbols) ?? symbols);
_pending_selected_symbols = null;
// select all symbols on first update
selected_symbols.sync_with(symbols);
}
}
@@ -9,36 +9,18 @@
xmlns:u="clr-namespace:mbts.ui"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:shell="clr-namespace:mbts.ui.shell"
Background="{u:BTSBrush UI.common_background}">
Background="{StaticResource CommonBackground}">
<UserControl.Resources>
<cv:DoubleTextConverter x:Key="DoubleTextConverter" />
<cv:DoubleTextConverter
x:Key="RisklessRateConverter"
Multiplier="100" />
<CollectionViewSource x:Key="Drivers" Source="{Binding drivers}" />
<CollectionViewSource x:Key="NonOptionsWithEmpty" Source="{Binding non_options_with_empty}" />
<DataTemplate x:Key="DriverTemplate">
<TextBlock Text="{Binding Converter={x:Static shell:AnalyticsExpirationParametersView.driver_display_text_converter}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger
Property="Text"
Value="None">
<Setter
Property="FontStyle"
Value="Italic" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
<shell:ExpTextConverter
x:Key="ExpirationTextConverter"
ShowSymbol="True"
MapExp="True" />
<DataTemplate x:Key="ExpirationTemplate">
<TextBlock Text="{Binding Converter={x:Static shell:AnalyticsExpirationParametersView.expiration_text_converter}}">
<TextBlock Text="{Binding Converter={StaticResource ExpirationTextConverter}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
@@ -48,6 +30,9 @@
<Setter
Property="FontStyle"
Value="Italic" />
<Setter
Property="Foreground"
Value="Gray" />
</Trigger>
</Style.Triggers>
</Style>
@@ -117,20 +102,6 @@
CommandParameter="{Binding}"
/>
</ContextMenu>
<Style x:Key="TextBlockCellElement" TargetType="TextBlock">
<Setter
Property="Foreground"
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=DataGridCell}}" />
</Style>
<Style x:Key="ComboBoxCellElement" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter
Property="Foreground"
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=DataGridCell}}" />
<Setter Property="Margin" Value="2" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.Resources>
@@ -153,23 +124,17 @@
SelectedItems="{Binding selected_symbols}"
HorizontalAlignment="Left"
Margin="5,2,2,2"
TextBlock.Foreground="Black"
HeaderContent="Symbols"
ShowSelectAll="True">
<c:ComboCheckBox.HeaderContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="{u:BTSColor UI.default_text}" />
</DataTemplate>
</c:ComboCheckBox.HeaderContentTemplate>
</c:ComboCheckBox>
ShowSelectAll="True"
/>
<WrapPanel>
<TextBlock
Margin="5,5,0,0" Foreground="{u:BTSBrush UI.default_text}" Text="Auto copy rolls:" />
<TextBlock Margin="5,5,0,0" Text="Auto copy rolls:" />
<CheckBox Margin="5,5,10,0" IsChecked="{Binding auto_copy_rolls_is_checked}" />
</WrapPanel>
<WrapPanel>
<TextBlock
Margin='0,5,0,0' Foreground="{u:BTSBrush UI.default_text}" Text="Auto copy rates:" />
<TextBlock Margin='0,5,0,0' Text="Auto copy rates:" />
<CheckBox Margin="5,5,0,0" IsChecked="{Binding auto_copy_rates_is_checked}" />
</WrapPanel>
</StackPanel>
@@ -177,8 +142,8 @@
x:Name="_grid"
Grid.Row="1"
ItemsSource="{Binding visible_rows}"
Background="Transparent"
VerticalGridLinesBrush="{u:BTSColor Grid.grid_line}"
AlternatingRowBackground="{u:BTSColor MarketData.driver_background}"
VerticalGridLinesBrush="Gray"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
CanUserAddRows="False"
@@ -191,10 +156,35 @@
SelectionUnit="FullRow"
SelectedUnits="{Binding selected_rows}"
PreviewMouseRightButtonUp="_grid_PreviewMouseRightButtonUp">
<c:DataGridEx.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay}"/>
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="BorderThickness" Value="1" />
<Style.Triggers>
<Trigger
Property="IsSelected"
Value="True">
<Setter
Property="Background"
Value="{u:BTSColor Grid.grid_selection_background}" />
<Setter
Property="BorderBrush"
Value="{u:BTSColor Grid.grid_selection_background}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay}"/>
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger
Property="IsSelected"
Value="true">
<Setter
Property="Background"
Value="{u:BTSColor Grid.grid_selection_background}" />
</Trigger>
<DataTrigger
Binding="{Binding is_last_row_of_month_group}"
Value="True">
@@ -207,7 +197,7 @@
</DataTrigger>
</Style.Triggers>
</Style>
</c:DataGridEx.RowStyle>
</DataGrid.RowStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter
@@ -219,6 +209,9 @@
<Setter
Property="HorizontalContentAlignment"
Value="Center" />
<Setter
Property="BorderThickness"
Value="0,0,1,0" />
<Setter
Property="BorderBrush"
Value="Black" />
@@ -233,7 +226,7 @@
<DataGrid.Columns>
<DataGridTextColumn
Header="Security"
Binding="{Binding security, Converter={x:Static shell:AnalyticsExpirationParametersView.expiration_text_converter}}"
Binding="{Binding security, Converter={StaticResource ExpirationTextConverter}}"
IsReadOnly="True">
<DataGridTextColumn.CellStyle>
<Style
@@ -245,6 +238,9 @@
<Setter
Property="Foreground"
Value="{u:BTSColor Name=header_text}" />
<Setter
Property="BorderThickness"
Value="0" />
<Setter
Property="VerticalContentAlignment"
Value="Center" />
@@ -291,7 +287,9 @@
<DataTrigger
Binding="{Binding is_driver_dirty}"
Value="True">
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
<Setter
Property="BorderBrush"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -299,10 +297,10 @@
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Source={StaticResource Drivers}}"
ItemTemplate="{StaticResource DriverTemplate}"
ItemsSource="{Binding DataContext.non_options, RelativeSource={RelativeSource AncestorType=c:DataGridEx}}"
ItemTemplate="{StaticResource ExpirationTemplate}"
SelectedItem="{Binding driver, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ComboBoxCellElement}" />
Margin="2" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@@ -320,28 +318,20 @@
<DataTrigger
Binding="{Binding is_deliverable_dirty}"
Value="True">
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
<Setter
Property="BorderBrush"
Value="Red" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding is_deliverable_dirty}" Value="False" />
<Condition Binding="{Binding
deliverable,
Converter={x:Static shell:AnalyticsExpirationParametersView.expiration_is_defined_converter}}"
Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" Value="{u:BTSColor UI.default_foreground_dark}" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Source={StaticResource NonOptionsWithEmpty}}"
ItemsSource="{Binding DataContext.non_options_with_empty, RelativeSource={RelativeSource AncestorType=c:DataGridEx}}"
ItemTemplate="{StaticResource ExpirationTemplate}"
SelectedItem="{Binding deliverable, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ComboBoxCellElement}" />
Margin="2" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@@ -359,28 +349,20 @@
<DataTrigger
Binding="{Binding is_roll_schedule_dirty}"
Value="True">
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
<Setter
Property="BorderBrush"
Value="Red" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding is_roll_schedule_dirty}" Value="False" />
<Condition Binding="{Binding
roll_schedule,
Converter={x:Static shell:AnalyticsExpirationParametersView.expiration_is_defined_converter}}"
Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" Value="{u:BTSColor UI.default_foreground_dark}" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Source={StaticResource NonOptionsWithEmpty}}"
ItemsSource="{Binding DataContext.non_options_with_empty, RelativeSource={RelativeSource AncestorType=c:DataGridEx}}"
ItemTemplate="{StaticResource ExpirationTemplate}"
SelectedItem="{Binding roll_schedule, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ComboBoxCellElement}" />
Margin="2" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@@ -396,7 +378,9 @@
<DataTrigger
Binding="{Binding is_roll_target_dirty}"
Value="True">
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
<Setter
Property="BorderBrush"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -407,15 +391,14 @@
Text="{Binding roll_target, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource RollTargetContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
ContextMenu="{StaticResource RollTargetContextMenu}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding roll_target, Converter={StaticResource DoubleTextConverter}}"
Margin="2" />
Text="{Binding roll_target, Converter={StaticResource DoubleTextConverter}}"
Margin="2" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -431,7 +414,9 @@
<DataTrigger
Binding="{Binding is_riskless_rate_dirty}"
Value="True">
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
<Setter
Property="BorderBrush"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -442,14 +427,12 @@
Text="{Binding riskless_rate, Converter={StaticResource RisklessRateConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource RisklessRateContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
ContextMenu="{StaticResource RisklessRateContextMenu}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding riskless_rate, Converter={StaticResource RisklessRateConverter}}" />
<TextBox Text="{Binding riskless_rate, Converter={StaticResource RisklessRateConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -465,7 +448,9 @@
<DataTrigger
Binding="{Binding is_settlement_price_dirty}"
Value="True">
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
<Setter
Property="BorderBrush"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -476,14 +461,12 @@
Text="{Binding settlement_price, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource SettlementPriceContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
ContextMenu="{StaticResource SettlementPriceContextMenu}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding settlement_price, Converter={StaticResource DoubleTextConverter}}" />
<TextBox Text="{Binding settlement_price, Converter={StaticResource DoubleTextConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -500,7 +483,9 @@
<DataTrigger
Binding="{Binding is_delta_beta_dirty}"
Value="True">
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
<Setter
Property="BorderBrush"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -511,14 +496,12 @@
Text="{Binding delta_beta, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource DeltaBetaContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
ContextMenu="{StaticResource DeltaBetaContextMenu}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding delta_beta, Converter={StaticResource DoubleTextConverter}}" />
<TextBox Text="{Binding delta_beta, Converter={StaticResource DoubleTextConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -535,7 +518,9 @@
<DataTrigger
Binding="{Binding is_vega_beta_dirty}"
Value="True">
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
<Setter
Property="BorderBrush"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -543,11 +528,10 @@
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding vega_beta, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource VegaBetaContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
Text="{Binding vega_beta, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource VegaBetaContextMenu}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
@@ -1,14 +1,10 @@
#nullable enable
namespace mbts.ui.shell {
using bts.api.analytics;
namespace mbts.ui.shell {
using bts.api;
using mbts.spark;
using mbts.ui.controls;
using mbts.ui.converters;
using mbts.ui.extensions;
using Spark.Core.Disposables;
using System;
using System.Windows;
using System.Windows.Controls;
@@ -20,8 +16,8 @@ namespace mbts.ui.shell {
/// Interaction logic for AnalyticsExpirationParametersView.xaml
/// </summary>
partial class AnalyticsExpirationParametersView : UserControl {
IDisposable? _context_menu_op;
IDisposable? _data_context_hooks;
IDisposable _context_menu_op;
IDisposable _data_context_hooks;
public AnalyticsExpirationParametersView() {
InitializeComponent();
@@ -35,28 +31,6 @@ namespace mbts.ui.shell {
}
}
public static ExpirationTextConverter expiration_text_converter =>
new() {
ShowSymbol = true,
ShowType = true,
MapExp = true
};
public static IValueConverter driver_display_text_converter =>
ValueConverter.create<ExpirationDriverImmutable?, string?>(
d => d.HasValue ?
d.Value.ModeCase switch {
ExpirationDriver.ModeOneofCase.Security =>
expiration_text_converter.convert(d.Value.Security.to_sec_exp()),
ExpirationDriver.ModeOneofCase.TimeWeightedInterpolated => "Calculate",
_ => null
} :
null);
public static IValueConverter expiration_is_defined_converter =>
ValueConverter.create<SecurityExpiration?, bool>(exp =>
exp is not null && exp.expiration != default);
void _grid_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) {
Disposable.dispose(ref _context_menu_op);
if (e.OriginalSource is not FrameworkElement el || DataContext is not AnalyticsExpirationParametersVM vm) {
@@ -73,4 +47,30 @@ namespace mbts.ui.shell {
e.Handled = true;
}
}
class ExpTextConverter : IValueConverter {
public ExpTextConverter() {
NullOrEmptyText = "None";
}
public bool ShowType { get; set; }
public bool ShowSymbol { get; set; }
public bool MapExp { get; set; }
public string NullOrEmptyText { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
var exp = value as SecurityExpiration;
if (exp == null || exp.is_empty()) {
return NullOrEmptyText;
}
return ShowSymbol ?
SecurityFormatter.format_future(exp.symbol, exp.expiration) :
DateFormatter.Format(exp.expiration, DateFormat.MonthCodeLastDigitYear);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
}
@@ -1,13 +1,10 @@
namespace mbts.spark {
namespace mbts.spark {
using bts.api;
using bts.spark;
using mbts.ui;
using mbts.ui.shell;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
@@ -41,30 +38,11 @@ namespace mbts.spark {
public void thread_init() {
_vm = new AnalyticsExpirationParametersVM(_bus, _init_settings);
var auto_copy_rolls_cmd = new SparkCommandCommand(
"Auto Copy Rolls", null, _vm.toggle_auto_copy_rolls_cmd);
_vm.SubscribeDisposed(_vm.observe_auto_copy_rolls().Subscribe(v =>
auto_copy_rolls_cmd.icon = v ? Icons.RecurringSchedule : Icons.RecurringScheduleGray));
var auto_copy_rates_cmd = new SparkCommandCommand(
"Auto Copy Rates", null, _vm.toggle_auto_copy_rates_cmd);
_vm.SubscribeDisposed(_vm.observe_auto_copy_rates().Subscribe(v =>
auto_copy_rates_cmd.icon = v ? Icons.FinancialGrowth : Icons.FinancialGrowthGray));
_view = new AnalyticsExpirationParametersView { DataContext = _vm };
_commands = new() {
new SparkCommandCommand("Commit", Icons.Disk, _vm.commit_cmd, new KeyGesture(Key.S, ModifierKeys.Control)),
new SparkCommandCommand("Revert", Icons.UndoCircular, _vm.revert_cmd, new KeyGesture(Key.Z, ModifierKeys.Control)),
auto_copy_rates_cmd,
auto_copy_rolls_cmd,
new SparkCommandCommand(
"Set Driver to Nearest Exp", null, _vm.set_driver_to_nearest_exp_cmd) {
show_in_toolbar = false,
show_in_context_menu = true
},
};
_view = new AnalyticsExpirationParametersView { DataContext = _vm };
}
}
}
@@ -1,69 +1,101 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using bts;
using mbts.analytics;
using bts.api.analytics;
using System.Reactive.Linq;
using bts.api;
using System.Threading;
namespace mbts.spark {
using bts;
using bts.api;
using bts.api.analytics;
// 1. Reads exp params from the network and publishes
// it to the BB
// 2. Listens to ExpirationParametersCommitTag and
// writes the calls into persistence to save the data
public class AnalyticsParametersManager : IDisposable {
private readonly ThreadedBlackboard _bb;
private AnalyticsService _analytics_service;
private List<IDisposable> _disposables = new List<IDisposable>();
private IDictionary<mbts.SecurityExpiration, AnalyticsExpirationParameters>
_data = new Dictionary<mbts.SecurityExpiration, AnalyticsExpirationParameters>();
private readonly ITag<IEnumerable<AnalyticsExpirationParameters>>
_in_bb_exp_params_commit_tag = SparkTags.ExpirationParametersCommitTag;
private readonly ITag<SystemContractData> _in_contracts_tag = SparkTags.SystemContractsTag;
private readonly ITag<AnalyticsExpirationParametersMap> _out_exp_params_tag =
SparkTags.ExpirationParametersInputTag;
private HighPerformanceTimer _timer = new HighPerformanceTimer();
using mbts.analytics;
public AnalyticsParametersManager(ThreadedBlackboard bb,
AnalyticsService analytics_server) {
_bb = bb;
_analytics_service = analytics_server;
var exp_params_sub = _analytics_service.subscribei_exp_parameters()
.ObserveOn(SynchronizationContext.Current)
.Subscribe(_on_exp_parameters);
_disposables.Add(exp_params_sub);
_bb.subscribe(_in_bb_exp_params_commit_tag);
_bb.subscribe(_in_contracts_tag);
_bb.data_callback += _on_bb_data;
}
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
/// <summary>
/// Reads exp params from the network and publishes it to the bus; listens to
/// <see cref="SparkTags.ExpirationParametersCommitTag"/> and persists the data
/// </summary>
public sealed class AnalyticsParametersManager : IDisposable {
IDisposable? _dispose;
public AnalyticsParametersManager(IBus bus, AnalyticsService analytics_service) {
var contracts_src = bus.subscribe(SparkTags.SystemContractsTag);
var exp_params_in_src = analytics_service.subscribei_exp_parameters()
.Select(upd => upd.all
.SelectMany(ep => ep.ParamList
.Select(ep => new AnalyticsExpirationParameters(ep)))
.ToDictionary(ep => ep.security));
var exp_params_out_src = bus.subscribe(SparkTags.ExpirationParametersCommitTag)
.Select(eps => eps
.Where(ep => ep.security.expiration.Date >= DateTime.Today - TimeSpan.FromDays(1)));
_dispose = Disposable.Create(
exp_params_in_src.CombineLatest(contracts_src)
.Select(tup => new AnalyticsExpirationParametersMap(tup.First, tup.Second))
.ObserveOn(SynchronizationContext.Current ?? throw new InvalidOperationException())
.Subscribe(bus.post_callback(SparkTags.ExpirationParametersInputTag)),
exp_params_out_src.Subscribe(save_exp_params));
void save_exp_params(IEnumerable<AnalyticsExpirationParameters> exp_params) {
Func<AnalyticsExpirationParameters, bool> is_unexpired = (p) => {
var st = p.driver_security.security_tuple.to_api_sec_tuple();
var exp = p.security.expiration;
return SecurityExpirationHelper.is_unexpired(st, exp);
};
exp_params = exp_params.Where(t => is_unexpired(t));
private void _on_bb_data(object tag, object data) {
if (_in_contracts_tag.Equals(tag)) {
_assemble_and_publish();
} else if (_in_bb_exp_params_commit_tag == tag) {
// listens to the commit tag and saves it
Log.Gen.info($"AnalyticsParametersManager received commit req. count:{exp_params.Count()}");
var to_save = new ExpirationParameterEntryList();
IEnumerable<AnalyticsExpirationParameters> exp_params =
data as IEnumerable<AnalyticsExpirationParameters>;
exp_params = exp_params.Where(e => e.security.expiration.Date >=
DateTime.Now.Date.Subtract(TimeSpan.FromDays(1)));
bts.Log.Gen.info(
String.Format("AnalyticsParametersManager._on_bb_data received commit count:{0}",
exp_params.Count()));
ExpirationParameterEntryList to_save = new ExpirationParameterEntryList();
to_save.ParamList.AddRange(exp_params.Select(i => i.to_exp_param_entry()));
Log.Gen.record(
null,
(dt, elapsed, res) => {
var msg = "AnalyticsParametersManager saved exp_params. " +
$"count:{exp_params.Count()} t:{elapsed.TotalMilliseconds:0.000}ms";
return (LogLevel.Info, msg);
},
() => analytics_service.set_exp_parameters(to_save));
_timer.start();
_analytics_service.set_exp_parameters(to_save);
_timer.stop();
bts.Log.Gen.info(
String.Format("AnalyticsParametersManager saved exp_params count:{0} t:{1} msecs",
exp_params.Count(), _timer.lastIntervalTime));
}
}
public void Dispose() =>
Disposable.dispose(ref _dispose);
void _on_exp_parameters(bts.api.core.Update<ExpirationParameterEntryListImmutable, int> update) {
_data = new Dictionary<mbts.SecurityExpiration, AnalyticsExpirationParameters>();
bts.Log.Gen.info(String.Format("AnalyticsParametersManager._on_exp_parameters received count:{0}",
update.all.Count()));
bts.Log.assert(update.all.Count() == 0 || update.all.Count() == 1);
foreach (ExpirationParameterEntryListImmutable l in update.all) {
foreach (ExpirationParameterEntryImmutable e in l.ParamList) {
AnalyticsExpirationParameters new_entry = new AnalyticsExpirationParameters(e);
_data[new_entry.security] = new_entry;
}
break;
}
Log.Gen.info($"AnalyticsParametersManager._on_exp_parameters count: {_data.Count}");
_assemble_and_publish();
// _bb.post(_out_exp_params_tag, new AnalyticsExpirationParametersMap(_data));
}
private void _assemble_and_publish() {
if (_data != null) {
SystemContractData sc_data = _bb.read_state(_in_contracts_tag);
if (sc_data != null) {
_bb.post(_out_exp_params_tag, new AnalyticsExpirationParametersMap(_data, sc_data));
}
}
}
#region IDisposable
public void Dispose() {
_bb.data_callback -= _on_bb_data;
foreach (IDisposable d in _disposables) {
d.Dispose();
}
}
#endregion
}
}
@@ -125,8 +125,8 @@ namespace mbts.spark {
if (_is_different(res)) {
_last_published = new OptionDriverPriceMap(_sec_tup.security_name, res);
var to_fire = new OptionDriverPriceMap(_sec_tup.security_name, res);
// bts.Log.Gen.info("SingleOptionDriverPricePublisher._compute_and_fire \n" +
// $"map:{to_fire.log_string()}");
bts.Log.Gen.info("SingleOptionDriverPricePublisher._compute_and_fire \n" +
$"map:{to_fire.log_string()}");
_logger.stop_default_timer();
_bb.post(SparkTags.OptionDriverBBOTag(_sec_tup.security_name),
new OptionDriverPriceMap(_sec_tup.security_name, res));
@@ -1,120 +0,0 @@
namespace mbts.spark {
using bts;
using bts.api;
using mbts;
using mbts.analytics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
// For each vs, it sends the vol surface calc request (_do_periodic_auto_fit)
// and then commits the response
public class SingleVolSurfaceAutoFitSource : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly int _vs_id;
private readonly ITag<VolSurfaceInput> _vs_input_tag;
private readonly ITag<VolSurfaceCalculationResponse> _vs_calc_response_tag;
private DateTime _last_req_time = DateTime.MinValue;
private TimeSpan _req_period = TimeSpan.MaxValue;
private int _req_id = 0;
public SingleVolSurfaceAutoFitSource(int vs_id, ThreadedBlackboard bb) {
_bb = bb;
_vs_id = vs_id;
_vs_input_tag = SparkTags.VolSurfaceCommittedInputTag(vs_id);
_vs_calc_response_tag = SparkTags.VolSurfaceAutoCalculationResponseTag(vs_id);
_bb.subscribe(_vs_input_tag);
_bb.subscribe(_vs_calc_response_tag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.data_callback += _on_bb_data;
_bb.timer_callback += _on_bb_timer;
}
private void _on_bb_data(object tag, object data) {
if (tag.Equals(_vs_input_tag))
_possibly_autofit();
else if (tag.Equals(_vs_calc_response_tag)) {
VolSurfaceCalculationResponse calc_resp = data as VolSurfaceCalculationResponse;
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource._on_bb_data vs_id: {_vs_id} " +
$"received calculation response");
_bb.post(SparkTags.VolSurfaceCommitTag(_vs_id),
calc_resp.volsurface_calculation_output.volsurface_input);
}
}
private bool _user_has_vs_control() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
return vs_input != null &&
vs_input.surface_settings.core_settings.vs_manager == current_user;
}
private void _on_bb_timer() {
_possibly_autofit();
}
private void _possibly_autofit() {
var auto_fit_period = _get_auto_fit_periodic();
if (!auto_fit_period.HasValue)
return;
if (DateTime.Now - _last_req_time <= auto_fit_period)
return;
if (!_user_has_vs_control())
return;
// auto fit here
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource vs_id: {_vs_id} requesting fit " +
$"last_req_time: {_last_req_time} period: {auto_fit_period.Value}");
_send_request();
}
private Nullable<TimeSpan> _get_auto_fit_periodic() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
if (vs_input == null)
return null;
var fit_settings =
VolSurfaceDataFunctions.get_market_fit_settings(vs_input.surface_settings);
if (fit_settings == null)
return null;
var auto_fit_setting = fit_settings.vs_auto_fit_settings;
if (auto_fit_setting == null)
return null;
if (auto_fit_setting.auto_fit_region == VolSurfaceEnums.VolCurveFitRegion.None)
return null;
var time_based_fit_setting = auto_fit_setting.time_based_settings;
if (time_based_fit_setting == null)
return null;
if (!time_based_fit_setting.period_seconds.HasValue)
return null;
return TimeSpan.FromSeconds(time_based_fit_setting.period_seconds.Value);
}
private void _send_request() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
if (vs_input == null)
return;
VolCurveMarketFitAction mf_action = new VolCurveMarketFitAction(
VolCurveMarketFitAction.ActionType.FitWholeCurve, null, false);
VolCurveAction vc_action = new VolCurveAction(mf_action);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
vs_input.surface_parameters.curve_parameters_map.ToDictionary(
i => i.Item1,
i => vc_action
);
VolSurfaceCalculationRequest req = new VolSurfaceCalculationRequest(vs_input,
_req_id++, action_map, true, true);
_last_req_time = DateTime.Now;
_bb.post(SparkTags.VolSurfaceAutoCalculationRequestTag(_vs_id), req);
}
#region IDisposable
public void Dispose() {
_bb.data_callback -= _on_bb_data;
_bb.timer_callback -= _on_bb_timer;
}
#endregion
}
}
@@ -63,9 +63,9 @@ namespace mbts.spark {
vs_input, vs_nonvol_data.contracts, aux_data_map);
_timer.stop();
var tag_str = tag == null ? "Null" : tag.ToString();
// bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
// $"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
// $"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
$"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
$"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
_logger.log(_log_id, _log_id, _timer.lastIntervalTime);
_bb.post(_out_bb_computed_vol_tag, vs_output);
}
@@ -1,21 +1,17 @@
namespace mbts.spark {
using bts;
using bts.api;
using bts.api.analytics;
using mbts.analytics;
using mbts.ui;
using Spark.Core.Protobuf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Windows;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using mbts.analytics;
using bts;
using bts.api.analytics;
using System.Reactive.Linq;
using System.Threading;
using bts.api;
using System.Collections;
namespace mbts.spark {
// For a single VolSurface this class
// 1. Listents to commmits and calls the service to persist data
// 2. Gathers the vol surface input from the nw and publishes it
@@ -26,6 +22,7 @@
private readonly ITag<VolSurfaceInput> _in_bb_committ_tag;
private readonly AnalyticsService _analytics_service;
private readonly HighPerformanceTimer _timer = new HighPerformanceTimer();
private readonly string _user;
private readonly List<IDisposable> _disposables = new List<IDisposable>();
private IDictionary<UInt64, VolSurfaceInputSnapshot> _snp_shot_map = new
Dictionary<UInt64, VolSurfaceInputSnapshot>();
@@ -36,10 +33,10 @@
volsurface_id));
_volsurface_id = volsurface_id;
_bb = threaded_bb;
_user = "TEST_USER"; // TODO
_in_bb_committ_tag = SparkTags.VolSurfaceCommitTag(volsurface_id);
_analytics_service = analytics_service;
_bb.subscribe(_in_bb_committ_tag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.data_callback += _on_bb_data;
var vs_sub = _analytics_service.subscribei_volsurfaces()
.ObserveOn(SynchronizationContext.Current)
@@ -55,10 +52,8 @@
private void _on_vss(bts.api.core.Update<VolSurfaceEntryImmutable, UInt64> update) {
IEnumerable<VolSurfaceEntryImmutable> all_vss = update.all;
var this_vs_vss = all_vss.Where(vs => vs.VsId == _volsurface_id);
bts.Log.Gen.info(
$"SingleVolSurfaceGateway._on_vss vs_count: {this_vs_vss.Count()} " +
$"all: {update.all.Count()} added: {update.added.Count()} " +
$"updated: {update.changed.Count()} removed: {update.removed.Count()}");
bts.Log.Gen.info(String.Format("SingleVolSurfaceGateway._on_vss count:{0} vs_count:{1}",
all_vss.Count(), this_vs_vss.Count()));
if (this_vs_vss.Count() == 0) {
return;
}
@@ -83,42 +78,28 @@
_snp_shot_map.Values);
}
static VolSurfaceInput _to_vs_input(VolSurfaceEntryImmutable vs) =>
ProtobufSerializationHelper.deserialize<VolSurfaceInput>(vs.Volsurface.ToByteArray());
// private void _show_save_error(string message) {
// BTSMessageBox.Show(message, "Vol Save Error", BTSMessageBoxButtons.OK);
// }
static VolSurfaceInput _to_vs_input(VolSurfaceEntryImmutable vs) {
byte[] vs_bytes = vs.Volsurface.ToByteArray();
return
mbts.analytics.VolSurfaceUtilityFunctions.deserialize_proto<VolSurfaceInput>(vs_bytes);
}
private void _on_bb_data(object tag, object data) {
if (_in_bb_committ_tag.Equals(tag)) {
try {
_timer.start();
VolSurfaceInput vs_input = data as VolSurfaceInput;
var user_tup = _bb.read_state(SparkTags.CurrentUser);
string current_user = user_tup.name;
string vs_manager = vs_input.surface_settings.core_settings.vs_manager;
if (current_user != vs_manager) {
string msg = $"User {current_user} does not have the permission to save vols. " +
$"Permission can be obtained via the grid context menu item 'Acquire ownership from {vs_manager}'";
BTSMessageBox.Show(msg, "Vol Save Error", BTSMessageBoxButtons.OK);
return;
}
_sanitize_input(ref vs_input);
vs_input = VolSurfaceDataFunctions.unset_driver_overrides(vs_input);
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, current_user);
vs_input.saver_login = _user;
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, _user);
_timer.stop();
Log.Gen.info(String.Format(
"SingleVolSurfaceGateway._on_bb_data Time to commit volsurface {0} {1} msecs",
_volsurface_id, _timer.lastIntervalTime));
var archived_theos = _get_theos();
if (archived_theos != null) {
_timer.start();
if (archived_theos != null)
_analytics_service.archive_theos(archived_theos);
_timer.stop();
Log.Gen.info($"SingleVolSurfaceGateway._on_bb_data archiving theos vs_id: {_volsurface_id} size: " +
$"{archived_theos.SecTheo.Count} time: {_timer.lastIntervalTime} msecs");
}
} catch (Exception ex) {
Log.Gen.warn(ex.ToString());
_bb.post(SparkTags.MessageDisplayTag,
@@ -36,11 +36,7 @@ namespace mbts.spark {
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
a_syms => new SingleVolSurfaceCommittedOutputRetriever(_vs_id, _bb, a_syms)),
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
a_syms => new SingleVolSurfaceRequestCalculator(
SingleVolSurfaceRequestCalculator.RequestType.UI, _vs_id, a_syms, _bb)),
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
a_syms => new SingleVolSurfaceRequestCalculator(
SingleVolSurfaceRequestCalculator.RequestType.AUTO, _vs_id, a_syms, _bb)),
a_syms => new SingleVolSurfaceRequestCalculator(_vs_id, a_syms, _bb)),
new SingleVolSurfaceMarketImpliedVolSource(_vs_id, _bb),
// new SingleVolSurfaceTheoSource(_vs_id, _bb),
// TODO: suspending heatmaps for now (12/24/21)
@@ -48,7 +44,6 @@ namespace mbts.spark {
// server work that can optionally be done on the client
new SingleVolSurfaceDataSource (SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
a_syms => new SingleVolSurfaceInterpGreeksPublisher (_vs_id, _bb, a_syms)),
new SingleVolSurfaceAutoFitSource(_vs_id, _bb),
};
}
@@ -75,7 +75,7 @@ namespace mbts.spark {
var exp_price_map = OptionDriverPriceMap.get_vs_price_map(
all_sym_price_map, (s) => _option_symbols.Contains(s));
if (sc_data != null && vs_nonvol_input != null && exp_price_map != null) {
bts.Log.Gen.diag($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
bts.Log.Gen.info($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
"publishing VolSurfaceNonVolData");
_bb.post(_out_bb_vs_nonvol_data_tag,
new VolSurfaceNonVolData(
@@ -18,16 +18,10 @@ namespace mbts.spark {
// Pubs :
// 1. Calc Resp
public class SingleVolSurfaceRequestCalculator : IDisposable {
public enum RequestType {
UI = 0,
AUTO
}
private readonly RequestType _request_type = RequestType.UI;
private readonly int _vs_id;
private readonly HashSet<string> _symbols;
private readonly ThreadedBlackboard _bb;
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_request_tag;
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_auto_request_tag;
private readonly ITag<VolSurfaceNonVolData> _in_bb_nonvol_data_tag;
private readonly ITag<VolSurfaceStandardTime> _in_bb_vs_standard_time_tag;
private readonly ITag<VolSurfaceEnums.MarketSide> _in_bb_market_impliedvol_side_tag;
@@ -36,22 +30,18 @@ namespace mbts.spark {
/// data
private VolSurfaceOutput _last_output;
public SingleVolSurfaceRequestCalculator(RequestType request_type,
int vs_id, IEnumerable<string> symbols,
public SingleVolSurfaceRequestCalculator(int vs_id, IEnumerable<string> symbols,
ThreadedBlackboard threaded_bb) {
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id: {vs_id} request_type: {request_type}");
_request_type = request_type;
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id:{vs_id}");
_vs_id = vs_id;
_symbols = new HashSet<string>(symbols);
_bb = threaded_bb;
_in_bb_request_tag = SparkTags.VolSurfaceCalculationRequestTag(vs_id);
_in_bb_auto_request_tag = SparkTags.VolSurfaceAutoCalculationRequestTag(vs_id);
_in_bb_nonvol_data_tag = SparkTags.VolSurfaceNonVolDataTag(vs_id);
_in_bb_vs_standard_time_tag = SparkTags.VolSurfaceStandardTimeTag(vs_id);
_in_bb_market_impliedvol_side_tag = SparkTags.VolSurfaceMarketImpliedVolSide(vs_id);
_in_bb_dirty_tag = SparkTags.VolSurfaceDirtyStateTag(vs_id);
_bb.subscribe(_in_bb_request_tag);
_bb.subscribe(_in_bb_auto_request_tag);
_bb.subscribe(_in_bb_nonvol_data_tag);
_bb.subscribe(_in_bb_vs_standard_time_tag);
_bb.subscribe(_in_bb_vs_standard_time_tag);
@@ -61,21 +51,11 @@ namespace mbts.spark {
}
private void _on_bb_data(object tag, object data) {
switch (_request_type) {
case RequestType.UI: {
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
_recompute_and_publish(true);
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
_recompute_and_publish(false);
break;
}
case RequestType.AUTO: {
if (tag.Equals(_in_bb_auto_request_tag)) {
_serve_auto_request();
}
break;
}
}
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
_recompute_and_publish(true);
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
_recompute_and_publish(false);
}
private void _recompute_and_publish(bool is_new_request) {
@@ -100,7 +80,7 @@ namespace mbts.spark {
nonvol_data.driver_price_map,
vs_standard_time, true);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
_post_process_action(_bb, _vs_id, effective_req, aux_data_map);
_post_process_action(effective_req, aux_data_map);
_last_output = GeneralVolSurfaceCalculator.compute(effective_req.volsurface_input,
nonvol_data.contracts,
aux_data_map,
@@ -109,49 +89,16 @@ namespace mbts.spark {
effective_req.cascade_up,
effective_req.cascade_down);
_timer.stop();
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish vs_id: {_vs_id} " +
$"time: {_timer.lastIntervalTime}");
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish time:" +
$"{_timer.lastIntervalTime} vs_output:{_last_output.log_string()}");
_bb.post(SparkTags.VolSurfaceCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(_last_output, effective_req.request_id));
}
}
}
private void _serve_auto_request() {
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request " +
$"vs_id: {_vs_id} auto request received");
bool is_dirty = _bb.read_state(_in_bb_dirty_tag, false);
VolSurfaceNonVolData nonvol_data = _bb.read_state(_in_bb_nonvol_data_tag);
VolSurfaceStandardTime vs_standard_time = _bb.read_state(_in_bb_vs_standard_time_tag);
VolSurfaceCalculationRequest auto_req = _bb.read_state(_in_bb_auto_request_tag);
if (is_dirty || nonvol_data == null || vs_standard_time == null || auto_req == null) {
return;
}
_timer.start();
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map =
VolSurfaceUtilityFunctions.compute_aux_data(auto_req.volsurface_input,
nonvol_data.input,
nonvol_data.driver_price_map,
vs_standard_time, true);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
_post_process_action(_bb, _vs_id, auto_req, aux_data_map);
var vs_output = GeneralVolSurfaceCalculator.compute(auto_req.volsurface_input,
nonvol_data.contracts,
aux_data_map,
action_map,
null,
auto_req.cascade_up,
auto_req.cascade_down);
_timer.stop();
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request vs_id: {_vs_id} " +
$"time: {_timer.lastIntervalTime}");
_bb.post(SparkTags.VolSurfaceAutoCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(vs_output, auto_req.request_id));
}
/// Computes the impl vol if the action is fit to market
private static IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
ThreadedBlackboard bb, int vs_id,
private IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
VolSurfaceCalculationRequest req,
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map) {
IDictionary<VolSurfaceExpiration, VolCurveAction> rv = req.action_map;
@@ -162,7 +109,7 @@ namespace mbts.spark {
VolCurveAction rep_action = rv.ElementAt(0).Value;
if (rep_action.action_type == VolCurveAction.ActionType.FitToMarket) {
IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> impl_vol_map =
_fill_in_implied_vols(bb, vs_id, req.volsurface_input, req.action_map, aux_data_map,
_fill_in_implied_vols(req.volsurface_input, req.action_map, aux_data_map,
req.cascade_up, req.cascade_down);
rv = impl_vol_map.ToDictionary(kv => kv.Key, kv => new VolCurveAction(
new VolCurveMarketFitAction(rep_action.market_fit_action.fit_action, kv.Value,
@@ -172,8 +119,7 @@ namespace mbts.spark {
return rv;
}
private static IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
ThreadedBlackboard bb, int vs_id,
private IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
VolSurfaceInput vs_inp,
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map,
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map,
@@ -194,15 +140,15 @@ namespace mbts.spark {
applicable_exps = applicable_exps.Where(vs_exp => master_exps.Contains(vs_exp));
VolSurfaceEnums.MarketSide impliedvol_side =
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(bb, vs_id);
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(_bb, _vs_id);
foreach (VolSurfaceExpiration vs_exp in applicable_exps) {
VolSurfaceExpiration md_exp;
VolCurveMarketImpliedVolData exp_impl_data =
VolSurfaceCoreFunctions.compute_implied_vols(vs_inp, vs_exp, aux_data_map,
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(bb, arg_vs_exp),
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(_bb, arg_vs_exp),
impliedvol_side,
SingleVolSurfaceMarketImpliedVolSource._ignore_market_implied_vol_source_settings(
bb, vs_id),
_bb, _vs_id),
out md_exp);
rv[vs_exp] = exp_impl_data;
}
@@ -160,8 +160,8 @@ namespace mbts.spark {
public static IEnumerable<bts.api.SecurityTuple> get_vs_sectups(
int vs_id, ThreadedBlackboard bb) {
var vs_usage_list = bb.read_state(SparkTags.VolSurfaceUsageTag);
return (vs_usage_list?.get_sectups(vs_id)).empty_if_null();
VolSurfaceUsageListImmutable vs_usage_list = bb.read_state(SparkTags.VolSurfaceUsageTag);
return vs_usage_list.get_sectups(vs_id);
}
@@ -242,4 +242,4 @@ namespace mbts.spark {
}
#endregion
}
}
}
@@ -11,22 +11,18 @@ namespace mbts.analytics {
// tags, into IReadOnlyDictionary of symbol vs data publsihed under one tag
public class SymbolAggregationPublisher<TData> : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly SecurityType _sec_type;
Func<string, ITag<TData>> _get_tag;
// the tag on which the output will be published
ITag<IReadOnlyDictionary<string, TData>> _out_tag;
private readonly ITag<SystemContractData> _sc_data_tag;
Func<string, ITag<TData>> _get_tag;
ITag<IReadOnlyDictionary<string, TData>> _out_tag;
private Dictionary<string, ITag<TData>> _tag_map =
new Dictionary<string, ITag<TData>>();
private HashSet<object> _obj_tags = new HashSet<object>();
private bool _dirty = true;
public SymbolAggregationPublisher(
ThreadedBlackboard bb,
SecurityType sec_type,
Func<string, ITag<TData>> get_tag,
ITag<IReadOnlyDictionary<string, TData>> out_tag) {
_bb = bb;
_sec_type = sec_type;
_get_tag = get_tag;
_out_tag = out_tag;
_sc_data_tag = SparkTags.SystemContractsTag;
@@ -52,20 +48,15 @@ namespace mbts.analytics {
return;
}
foreach (var st in sc_data.sectup_map) {
if (st.security_type != _sec_type) {
continue;
}
// this is not correct since it is assuming symbol/name is
// unique across securty_tuples
string symbol = st.security_name;
if (!_tag_map.ContainsKey(symbol)) {
var tag = _get_tag(symbol);
if (tag != null) {
_tag_map.Add(symbol, tag);
_obj_tags.Add(tag);
_bb.subscribe(tag);
_dirty = true;
}
var symbol_tag = _get_tag(symbol);
_tag_map.Add(symbol, symbol_tag);
_obj_tags.Add(symbol_tag);
_bb.subscribe(symbol_tag);
_dirty = true;
}
}
}
@@ -81,7 +72,7 @@ namespace mbts.analytics {
output.Add(symbol, symbol_data);
}
IReadOnlyDictionary<string, TData> ro_output = output;
bts.Log.Gen.diag($"SymbolAggregationPublisher._on_timer publishing " +
bts.Log.Gen.info($"SymbolAggregationPublisher._on_timer publishing " +
$"tag: {_out_tag} sz: {ro_output.Count}");
_bb.post(_out_tag, ro_output);
}
@@ -15,8 +15,6 @@ using System.Reactive.Linq;
using System.Collections;
using mbts.spark;
using System.DirectoryServices.ActiveDirectory;
using Spark.Core.Protobuf;
using mbts.ui;
// using mbts.bb;
namespace mbts.spark {
@@ -55,7 +53,6 @@ namespace mbts.spark {
// 2. Listens to the activation tag (also published by the UI) and
// creates the SingleVolSurfaceManager for the VS
public class VolSurfaceBusManager : IDisposable {
private List<IDisposable> _disps = new List<IDisposable>();
private ThreadedBlackboard _bb;
private bts.api.analytics.AnalyticsService _analytics_service;
// in tags
@@ -77,11 +74,10 @@ namespace mbts.spark {
private HighPerformanceTimer _timer = new HighPerformanceTimer();
public VolSurfaceBusManager(bts.api.IBus bus,
bts.api.analytics.AnalyticsService analytics_service,
bts.api.orderman.OrderService order_service) {
bts.api.analytics.AnalyticsService analytics_service) {
bts.Log.Ui.info("VSBusManager cons tid:{VolSurfaceTroubleshootingFunctions.tid()}");
_bus = bus;
_bb = new ThreadedBlackboard("AW2", bus);
_bb = new ThreadedBlackboard(bus);
_analytics_service = analytics_service;
// subs to bb
_bb.subscribe(_in_scdata_tag);
@@ -100,121 +96,48 @@ namespace mbts.spark {
.Subscribe(_on_vs_usage_update);
_subs.Add(vs_sub);
_subs.Add(vs_usage_sub);
_subs.Add(new AnalyticsParametersManager(bus, _analytics_service));
_subs.Add(new AnalyticsParametersManager(_bb, _analytics_service));
_subs.Add(new ResourceAllocator(bus, _bb));
_subs.Add(new SymbolAggregationPublisher<OptionDriverPriceMap>(
_bb, SecurityType.Option, sym => SparkTags.OptionDriverBBOTag(sym),
_bb, (sym) => SparkTags.OptionDriverBBOTag(sym),
SparkTags.AllOptionDriverBBOTag));
_subs.Add(new SymbolAggregationPublisher<OptionTheoInterpolationData>(
_bb, SecurityType.Option, sym => SparkTags.SymbolInterpGreeksTag(sym),
_bb, (sym) => SparkTags.SymbolInterpGreeksTag(sym),
SparkTags.AllInterpGreeks));
_subs.Add(new SymbolAggregationPublisher<ExpStrikeKeyedData<TheoPoint>>(
_bb, SecurityType.Option, sym => SparkTags.TC2Tags.LiveTheosTag(sym),
SparkTags.TC2Tags.AllLiveTheosTag));
_subs.Add(new LiveTheoPublisher(_bb));
_subs.Add(new InterpTheosServerPublisher(_bb, order_service));
_subs.Add(new VolSurfaceInterpGreeksMultiplexer(_bb));
// usage
var list_sub = _bus.subscribe(SparkTags.VolSurfaceListTag).OnDispatcher().Subscribe(t => {
VolSurfaceUsageList ulist = new VolSurfaceUsageList();
IDictionary<string, int> name_to_ids = new Dictionary<string, int>();
foreach (var kv in t) {
name_to_ids[kv.Value] = kv.Key;
//_subs.Add(new AnalyticsServiceTester(_bb));
// TODO: temporatily publish values
VolSurfaceUsageList ulist = new VolSurfaceUsageList();
ulist.UsageList.Add(new VolSurfaceUsageEntry() {
VsId = 6,
SecTup = new bts.api.SecurityTuple() {
Type = SecurityType.Option,
Venue = VolSurfaceTemp.get_default_venue(SecurityType.Option, "BTC"),
Symbol = "BTC"
}
Log.Ui.info($"VolSurfaceUsageList name_to_ids count: {name_to_ids.Count}");
var vs_usage = mbts.config.Configuration.get<bts.config.toggles.VolSurfaceUsage>();
Log.Ui.info($"VolSurfaceUsageList vs_usage acount: {vs_usage.vol_surface_usage.Count}");
foreach (var u in vs_usage.vol_surface_usage) {
string option_name = u.option_name;
string vs_name = u.vs_name;
int vs_key;
if (name_to_ids.TryGetValue(vs_name, out vs_key)) {
ulist.UsageList.Add(new VolSurfaceUsageEntry() {
VsId = vs_key,
SecTup = new bts.api.SecurityTuple {
Type = SecurityType.Option,
Venue = Venue.Deribit,
Symbol = option_name
}
});
} else {
Log.Gen.error($"Could not find vol surface with name: {vs_name}");
}
}
var list_imm = new VolSurfaceUsageListImmutable(ulist);
Log.Ui.info(list_imm.log_string());
_bb.post(SparkTags.VolSurfaceUsageTag, list_imm);
_spin_vss(list_imm);
});
_disps.Add(list_sub);
// f(contract keyed theos, securities) -> id keyed theos
// (so the newer UI controls can access theos vis sec keys)
var theo_sub =
_bus.subscribe(SparkTags.TC2Tags.AllLiveTheosTag)
.CombineLatest(bus.subscribe(SparkTags.SystemContractsTag),
(theos, sc_data) => (theos, sc_data)).Subscribe(
tup => {
var keyed_theos = _assemble_keyed_theos(tup.sc_data, tup.theos);
_bus.post(SparkTags.KeyedTheos, keyed_theos);
}
);
_disps.Add(theo_sub);
}
static IReadOnlyDictionary<uint, VolSurfaceTheoPoint> _assemble_keyed_theos(
SystemContractData sc_data,
IReadOnlyDictionary<string, ExpStrikeKeyedData<TheoPoint>> theos) {
if (sc_data is null || theos is null) {
return new Dictionary<uint, VolSurfaceTheoPoint>(0);
}
HighPerformanceTimer timer = new HighPerformanceTimer();
timer.start();
Dictionary<uint, VolSurfaceTheoPoint> rv = new Dictionary<uint, VolSurfaceTheoPoint>();
foreach (var kv in theos) {
var symbol = kv.Key;
ExpStrikeKeyedData<TheoPoint> sym_theos = kv.Value;
if (sym_theos == null)
continue;
foreach (var s_kv in sym_theos.get_data_linearly()) {
DateTime exp = s_kv.first;
double strike = s_kv.second;
TheoPoint tp = s_kv.third;
// TODO: fix exchange later
Tuple<uint?, uint?> keys =
sc_data.get_option_sec_id(Venue.Deribit, symbol, exp, strike);
if (keys != null) {
var c_key = keys.Item1;
var p_key = keys.Item2;
if (c_key.HasValue) {
rv[c_key.Value] = new VolSurfaceTheoPoint(tp, true);
}
if (p_key.HasValue) {
rv[p_key.Value] = new VolSurfaceTheoPoint(tp, false);
}
}
ulist.UsageList.Add(new VolSurfaceUsageEntry() {
VsId = 7,
SecTup = new bts.api.SecurityTuple() {
Type = SecurityType.Option,
Venue = VolSurfaceTemp.get_default_venue(SecurityType.Option, "ETH"),
Symbol = "ETH"
}
}
timer.stop();
Log.Gen.info($"VolSurfaceBusManager keyed theos count: {rv.Count} " +
$"time: {timer.lastIntervalTime}");
return rv;
}
public ThreadedBlackboard bb() {
return _bb;
}
void _spin_vss(VolSurfaceUsageListImmutable ulist) {
foreach (var u in ulist.UsageList) {
var vs_id = u.VsId;
_bb.post(SparkTags.VolSurfaceActiveUITag(vs_id), true);
}
});
_bb.post(SparkTags.VolSurfaceUsageTag, new VolSurfaceUsageListImmutable(ulist));
}
void _on_vs_usage_update(Update<VolSurfaceUsageListImmutable, int> update) {
// TODO
return;
// int vs_count = update.all.Count();
// Log.Gen.info($"VSBusManager._on_vs_usage_update " +
// $"tid:{Thread.CurrentThread.ManagedThreadId} count:{update.all.Count()}");
// Log.assert(vs_count <= 1);
// if (update.all.TryGetSingle(out var to_write)) {
// _bb.post(SparkTags.VolSurfaceUsageTag, to_write);
// }
}
void _on_vss(bts.api.core.Update<VolSurfaceEntryImmutable, UInt64> update) {
@@ -249,7 +172,7 @@ namespace mbts.spark {
// callback
mbts.analytics.VolSurfaceSettings vs_settings = data as mbts.analytics.VolSurfaceSettings;
bts.Log.Gen.info(String.Format("VSBusManager._on_bb_update received vs creation tag vs:{0}",
ProtobufSerializationHelper.serialize_to_string(vs_settings)));
VolSurfaceUtilityFunctions.serialize_proto_to_text(vs_settings)));
_waiting_creations.Enqueue(vs_settings);
} else if (_in_bb_delete_tag.Equals(tag)) {
int vs_id = (int)data;
@@ -270,7 +193,6 @@ namespace mbts.spark {
new mbts.analytics.VolSurfaceNonVolInput(exp_param_map.exp_map.ToDictionary()));
} else if (_in_bb_req_vol.Equals(tag)) {
ClientArchivedVolRequest client_req = _bb.read_state(_in_bb_req_vol);
bts.Log.Ui.info($"VolSurfaceBusManager._on_bb_update ClientArchivedVolRequest: {client_req.ToString()}");
ArchivedTheoRetrieval ret = client_req.ret;
if (ret == null || ret.SecTup == null) {
bts.Log.Ui.warn($"VolSurfaceBusManager malformed ArchivedTheoRetrieval");
@@ -1,20 +0,0 @@
namespace mbts.analytics {
using mbts.spark;
// TheoPoint (from edge) has both calls and puts in the same structure.
// This shoudl dis-entangle that
public class VolSurfaceTheoPoint {
public readonly double theo = double.NaN;
public readonly double delta = double.NaN;
public readonly double driver = double.NaN;
public VolSurfaceTheoPoint(TheoPoint tp, bool is_call) {
if (tp != null) {
theo = is_call ? tp.fwd_normalized_call : tp.fwd_normalized_put;
delta = is_call ? tp.cdelta : tp.pdelta;
driver = tp.driver;
}
}
}
}
@@ -6,7 +6,6 @@
using mbts.user.settings;
using System;
using System.Collections.Generic;
public class VolSurfaceDisplayGridColumn : IEquatable<VolSurfaceDisplayGridColumn> {
public readonly Func<object, string> format;
@@ -19,19 +18,16 @@
VolSurfaceColumnSettings settings,
Func<object, string> format = null,
Func<string, object> parse = null,
Func<object, bool, object> tick_move = null,
IEqualityComparer<object> comparer = null) {
Func<object, bool, object> tick_move = null) {
this.settings = settings;
this.format = format ?? (v => v?.ToString() ?? string.Empty);
this.parse = parse ?? (v => v);
this.tick_move = tick_move ?? ((v, is_up) => v);
this.comparer = comparer ?? EqualityComparer<object>.Default;
}
public string unique_col_name => settings.id;
public string display_name => settings.display_name;
public string group => settings.group;
public IEqualityComparer<object> comparer { get; }
internal VolSurfaceColumnType type => settings.type;
@@ -7,7 +7,6 @@
using mbts.user.settings;
using System;
using System.Collections.Generic;
class VolSurfaceDisplayUtilities {
public static TickDisplaySettings default_tick_display =>
@@ -19,10 +18,6 @@
default_tick_display.with(display_multiplier: 100);
public static double parse_double(object obj) {
if (obj is null) {
Log.Gen.warn("parse_double received null input (will return nan and continue)");
return double.NaN;
}
Log.assert(obj is double);
obj = obj ?? Double.NaN;
double rv = obj is Double ? (double)obj : (Double.TryParse(obj.ToString(), out rv) ? rv : Double.NaN);
@@ -54,31 +49,9 @@
}
public static Func<object, bool, object> get_double_tickmove(TickFactors tick_factors, int tick_move) =>
(data, is_up) => {
if (data is not double dbl) {
return data;
}
if (dbl.is_nan()) {
return 0D;
}
return tick_factors.tick(dbl, is_up ? tick_move : -tick_move);
};
public static IEqualityComparer<double> get_double_comparer(
TickFactors tick_factors = null, double display_mult = double.NaN) {
if (tick_factors is null) {
return new TickFactors(10^7, 1).tick_equality_comparer();
}
// This equality comparer is used to compare actual values (not multiplied by
// 'display_mult'), while the TickFactors are used to format the display value (multiplied
// by 'display_mult'). Multiply the tick base by 'display_mult' so the equality comparison
// is done with the correct number of decimal places.
if (display_mult > 0 && Math.Log10(display_mult).is_int()) {
tick_factors = tick_factors.with(
tick_base: tick_factors.tick_base * display_mult);
}
return tick_factors.tick_equality_comparer();
}
(data, is_up) => data is double d_data ?
tick_factors.tick(d_data, is_up ? tick_move : -tick_move) :
data;
public static VolSurfaceDisplayGridColumn get_double_output_col(
string group,
@@ -140,10 +113,7 @@
col_config,
format: get_double_tostring(col_config.tick_factors, col_config.tick_display),
parse: get_parse_double(col_config.tick_factors, col_config.tick_display),
tick_move: get_double_tickmove(col_config.tick_factors, col_config.tick_move),
comparer: get_double_comparer(
col_config.tick_factors, col_config.tick_display.display_multiplier)
.cast<double, object>());
tick_move: get_double_tickmove(col_config.tick_factors, col_config.tick_move));
}
}
}
@@ -1,138 +0,0 @@
using System;
using System.Collections.Generic;
using mbts.analytics;
using bts.api;
using bts.collections;
using System.Linq;
using bts.api.orderman;
using bts.api.analytics;
using bts;
namespace mbts.spark {
// Listens to the interp theos, decorates the data
// and sends them over to the sparkd server
public class InterpTheosServerPublisher : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly bts.api.orderman.OrderService _order_service;
private HighPerformanceTimer _timer = new HighPerformanceTimer();
public InterpTheosServerPublisher(ThreadedBlackboard bb,
bts.api.orderman.OrderService order_service) {
bts.Log.Ui.info(
$"InterpTheosServerPublisher cons tid:{VolSurfaceTroubleshootingFunctions.tid()}");
_bb = bb;
_order_service = order_service;
_bb.subscribe(SparkTags.AllInterpGreeks);
_bb.subscribe(SparkTags.SystemContractsTag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.subscribe(SparkTags.VolSurfaceUsageTag);
_bb.data_callback += _on_data;
}
private void _on_data(object tag, object data) {
if (tag.Equals(SparkTags.SystemContractsTag) ||
tag.Equals(SparkTags.AllInterpGreeks)) {
_publish_data();
}
}
private static IDictionary<UInt32, DriverMD> _get_drivers(
IReadOnlyDictionary<string, OptionDriverPriceMap> driver_map,
SystemContractData sc_data) {
var rv = new Dictionary<UInt32, DriverMD>();
foreach (var kv in driver_map) {
rv.AddRange(
SecurityNormalizer.normalize_drivers(kv.Value, sc_data).ToArray());
}
return rv;
}
private static IEnumerable<Tuple<SecurityImmutable, TheoPoint>> _get_theos(
IReadOnlyDictionary<string, OptionTheoInterpolationData> theo_data,
SystemContractData sc_data) {
var rv = new List<Tuple<SecurityImmutable, TheoPoint>>();
foreach (var kv in theo_data) {
rv.AddRange(
SecurityNormalizer.normalize_interp_theos(kv.Value, sc_data));
}
return rv;
}
private IReadOnlyDictionary<string, OptionTheoInterpolationData>
_filter_for_publish(IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos) {
// find the symbol vs vs_id, get the committed vs, see if it is
// owned by the current user, and only then publish it
HashSet<string> syms = new HashSet<string>();
var usage = _bb.read_state(SparkTags.VolSurfaceUsageTag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
foreach (var u in (usage?.UsageList).empty_if_null()) {
VolSurfaceInput vs_input =
_bb.read_state(SparkTags.VolSurfaceCommittedInputTag(u.VsId));
if (vs_input != null) {
if (vs_input.surface_settings.core_settings.vs_manager ==
current_user) {
syms.Add(u.SecTup.Symbol);
}
}
}
return
all_theos.Where(kv => syms.Contains(kv.Key)).ToDictionary(kv => kv.Key, kv => kv.Value);
}
private void _publish_data() {
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag, null);
IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos =
_bb.read_state(SparkTags.AllInterpGreeks, null);
IReadOnlyDictionary<string, OptionDriverPriceMap> all_drivers =
_bb.read_state(SparkTags.AllOptionDriverBBOTag, null);
if (sc_data == null || all_theos == null || all_drivers == null)
return;
_timer.start();
all_theos = _filter_for_publish(all_theos);
if (all_theos.Count == 0) {
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data all_theos.Count == 0");
return;
}
IDictionary<UInt32, DriverMD> driver_map = _get_drivers(all_drivers, sc_data);
IEnumerable<Tuple<SecurityImmutable, TheoPoint>> theo_map = _get_theos(all_theos, sc_data);
// assemble it together and send it to the server
var ptp_list = new List<bts.api.orderman.PrecomputedTheoPoint>();
foreach (var tup in theo_map) {
SecurityImmutable sec = tup.Item1;
TheoPoint tp = tup.Item2;
bool is_call = sec.Option.CallPut == PutOrCall.Call;
var sec_id = sec.Id;
DriverMD dmd = null;
driver_map.TryGetValue(sec_id, out dmd);
PrecomputedTheoPoint ptp = new PrecomputedTheoPoint();
ptp.SecId = sec_id;
ptp.Driver = tp.driver;
ptp.DriverSecId = dmd != null && dmd.driver_security.HasValue ?
dmd.driver_security.Value.Id : 0;
ptp.Theo = is_call ? tp.call : tp.put;
ptp.Delta = is_call ? tp.cdelta : tp.pdelta;
ptp.Gamma = is_call ? tp.cgamma : tp.pgamma;
ptp.Vega = is_call ? tp.cvega : tp.pvega;
ptp.Theta = is_call ? tp.c_theta : tp.p_theta;
ptp.Rho = Double.NaN;
ptp.Vol = tp.vol;
ptp_list.Add(ptp);
}
bts.api.orderman.PrecomputedTheos server_theos = new PrecomputedTheos();
server_theos.Theos.AddRange(ptp_list);
_timer.stop();
var comp_time = _timer.lastIntervalTime;
_timer.start();
_order_service.set_precomputed_theos(server_theos);
_timer.stop();
var pub_time = _timer.lastIntervalTime;
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data symbols: {all_theos.Count} " +
$"size: {server_theos.Theos.Count} comp_time: {comp_time} msecs " +
$"pub_time: {pub_time}");
}
public void Dispose() {
_bb.data_callback -= _on_data;
}
}
}
+17 -80
View File
@@ -3,10 +3,6 @@ using System.Collections.Generic;
using mbts.analytics;
using bts.api;
using mbts.ui;
using MathNet.Numerics.Providers.FourierTransform;
using BCG;
using System.Windows.Automation;
using bts.collections;
namespace mbts.spark {
// Publishes theos for all options (on the side, also publishes the theos for TC2)
@@ -19,15 +15,12 @@ namespace mbts.spark {
private readonly HighPerformanceTimer _timer = new HighPerformanceTimer();
private readonly PerfStatsLogger _logger = new PerfStatsLogger(50, 100);
private readonly string _log_id = "LiveTheoPublisher";
private DateTime _last_future_theos_published = DateTime.MinValue;
private TimeSpan _futures_theos_publish_period = TimeSpan.FromMilliseconds(300);
public LiveTheoPublisher(ThreadedBlackboard bb) {
_bb = bb;
_bb.subscribe(_greeks_tag);
_bb.subscribe(_drivers_tag);
_bb.data_callback += _on_data;
_bb.timer_callback += _on_timer;
_interp_and_publish();
}
@@ -38,10 +31,6 @@ namespace mbts.spark {
}
}
private void _on_timer() {
_publish_future_theos();
}
private void _log_interpgreeks(
IReadOnlyDictionary<string, OptionTheoInterpolationData> interp_greeks) {
string log_msg = String.Empty;
@@ -70,14 +59,19 @@ namespace mbts.spark {
}
private void _log_theos(SystemContractData sc_data,
IReadOnlyDictionary<uint, GreeksTuple> greeks_map) {
IReadOnlyDictionary<uint, GreeksTuple> greeks_map,
DateTime? exp_filt = null) {
string rv = "LiveTheosPublisher data: ";
foreach (var kv in greeks_map) {
var id = kv.Key;
var g_tup = kv.Value;
if (sc_data.sec_md_map.TryGetValue(id, out var smd)) {
string sec_str = smd.ToString();
rv += "\n" + $"id: {id} sec: {sec_str} greeks: {g_tup}";
if (exp_filt == null ||
(smd.security.security_type() == SecurityType.Option &&
smd.security.Option.expiration_date().Equals(exp_filt.Value))) {
string sec_str = smd.ToString();
rv += "\n" + $"id: {id} sec: {sec_str} greeks: {g_tup}";
}
}
}
bts.Log.Gen.info(rv);
@@ -131,93 +125,36 @@ namespace mbts.spark {
return;
}
var k_theos = new Dictionary<SecurityStrike, TheoPoint>();
// option spreads
foreach (var kv in sc_data.strike_map) {
foreach (SecurityStrike k in kv.Value) {
var (call_id, put_id) = k.call_put_id;
var cp_id = k.call_put_id;
var driver = _get_option_driver(driver_map, k);
var tp = _interp_theo(interp_greeks, k, driver.get_driver());
k_theos[k] = tp;
// put it in the output map
if (call_id.HasValue) {
var normalized_theo = VolSurfaceCoreFunctions.normalize_theo(tp.call, tp.driver);
res.Add(call_id.Value,
if (cp_id.Item1.HasValue) {
res.Add(cp_id.Item1.Value,
new GreeksTuple(tp.vol, tp.driver, tp.call, tp.cdelta, tp.cgamma,
tp.cvega, tp.c_theta, Double.NaN, normalized_theo));
tp.cvega, tp.c_theta, Double.NaN));
}
if (put_id.HasValue) {
var normalized_theo = VolSurfaceCoreFunctions.normalize_theo(tp.put, tp.driver);
res.Add(put_id.Value,
if (cp_id.Item2.HasValue) {
res.Add(cp_id.Item2.Value,
new GreeksTuple(tp.vol, tp.driver, tp.put, tp.pdelta, tp.pgamma,
tp.pvega, tp.p_theta, Double.NaN, normalized_theo));
tp.pvega, tp.p_theta, Double.NaN));
}
}
}
// futures
var future_theos = _get_future_theos();
res.AddRange(future_theos);
// spreads, all option and future non tied spreads
foreach (var spr in sc_data.spreads) {
GreeksTuple gtup = StatelessSpreadCalculator.compute_non_tied_spread(spr.security, res);
res.Add(spr.security.Id, gtup);
}
// copy and publish
IReadOnlyDictionary<uint, GreeksTuple> res_ro = res;
bts.Log.Gen.info($"LiveTheoPublisher._interp_and_publish posting " +
$"count: {res_ro.Count}");
$"count: {res_ro.Count}");
_timer.stop();
_logger.log(_log_id, _log_id, _timer.lastIntervalTime);
_bb.post(SparkTags.Greeks, res_ro);
// oh and those TC2 theos for comptability
_publish_tc2_theos(k_theos);
}
// If we do any rolls like SP for futures, it will be here
IDictionary<UInt32, GreeksTuple> _get_future_theos() {
var rv = new Dictionary<UInt32, GreeksTuple>();
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag);
if (sc_data == null)
return rv;
foreach (var kv in sc_data.exp_map) {
var id = kv.Value;
SecurityMD sec_md;
if (sc_data.sec_md_map.TryGetValue(id, out sec_md)) {
if (sec_md.security.HasFuture) {
SecurityExpiration exp = kv.Key;
double pr = sec_md.md.fixing_price;
rv.Add(sec_md.security.Id,
new GreeksTuple(0, pr, pr, 1, 0, 0, 0, 0, pr));
}
}
}
return rv;
}
// recomputes and publishes them periodicially
void _publish_future_theos() {
if (DateTime.Now - _last_future_theos_published < _futures_theos_publish_period)
return;
_last_future_theos_published = DateTime.Now;
var rv = new Dictionary<SecurityExpiration, FutureTheoStructure>();
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag);
if (sc_data == null)
return;
foreach (var kv in sc_data.exp_map) {
var id = kv.Value;
SecurityMD sec_md;
if (sc_data.sec_md_map.TryGetValue(id, out sec_md)) {
if (sec_md.security.HasFuture) {
SecurityExpiration exp = kv.Key;
double pr = sec_md.md.fixing_price;
rv.Add(exp, new FutureTheoStructure(exp.expiration, pr, pr, 1, 0));
}
}
}
_bb.post(SparkTags.TC2Tags.LiveFutureTheosTag, rv);
}
public void Dispose() {
_bb.data_callback -= _on_data;
}
}
}
}
@@ -24,7 +24,7 @@ namespace mbts.spark {
var exp = kv.Key;
if (!temp.ContainsKey(kv.Key)) {
temp.Add(kv.Key,
new AnalyticsExpirationParameters(exp, null, empty, empty, 0, 0, 0, 0, 0));
new AnalyticsExpirationParameters(exp, empty, empty, empty, 0, 0, 0, 0, 0));
}
}
exp_map = temp;
@@ -1,50 +1,58 @@
#nullable enable
using System;
using System.Collections.Generic;
using mbts.analytics;
using bts.api;
using bts.api.persistence;
using System.Linq;
namespace mbts.spark {
using bts.api;
using mbts.analytics;
using System;
using System.Collections.Generic;
using System.Linq;
// All driver prices (along with some more info) for an option symbol
public class OptionDriverPriceMap {
public readonly string option_symbol;
public readonly IDictionary<SecurityExpiration, DriverMD> exp_price_map;
Dictionary<SecurityExpiration, DriverMD>? _unexpired_exp_price_map_backing;
public readonly String option_symbol = null;
public readonly IDictionary<SecurityExpiration, DriverMD> exp_price_map = null;
public OptionDriverPriceMap(
string option_symbol, IDictionary<SecurityExpiration, DriverMD> exp_price_map) {
this.option_symbol = option_symbol;
this.exp_price_map = exp_price_map;
string option_symbol_a,
IDictionary<SecurityExpiration, DriverMD> exp_price_map_a) {
option_symbol = option_symbol_a;
exp_price_map = exp_price_map_a;
}
public IDictionary<SecurityExpiration, DriverMD> unexpired_exp_price_map {
get {
return exp_price_map.Where(
kv => kv.Key.expiration >= DateTime.Now.Date).ToDictionary(
kv => kv.Key, kv => kv.Value);
}
}
public IDictionary<SecurityExpiration, DriverMD> unexpired_exp_price_map =>
_unexpired_exp_price_map_backing ??=
exp_price_map.Where(kv => kv.Key.expiration >= DateTime.Now.Date).ToDictionary();
// this is the BS needed because of ignoring exchange
public DriverMD get_driver_md(DateTime exp) =>
exp_price_map.FirstOrDefault(kv => kv.Key.expiration == exp).Value ?? DriverMD.empty;
public DriverMD get_driver_md(SecurityExpiration exp) =>
exp_price_map.GetValueOrDefault(exp) ?? DriverMD.empty;
public DriverMD get_driver_md(DateTime exp) {
foreach (var kv in exp_price_map) {
if (kv.Key.expiration == exp) {
return kv.Value;
}
}
return DriverMD.empty;
}
public DriverMD get_driver_md(SecurityExpiration exp) {
if (exp_price_map.TryGetValue(exp, out var d))
return d;
return DriverMD.empty;
}
public static IDictionary<VolSurfaceExpiration, DriverMD> get_vs_price_map(
IReadOnlyDictionary<string, OptionDriverPriceMap> all_sym_map,
Func<string, bool>? sym_filter = null
) {
sym_filter ??= _ => true;
return all_sym_map.SafeWhere(kv => kv.Value is not null && sym_filter(kv.Key))
.SelectMany(kv => kv.Value.exp_price_map)
.ToDictionaryCrushed(kv => kv.Key.to_vs_exp(), kv => kv.Value);
Func<string, bool> sym_filter = null) {
var rv = new Dictionary<VolSurfaceExpiration, DriverMD>();
if (all_sym_map != null)
foreach (var kv in all_sym_map) {
string sym = kv.Key;
OptionDriverPriceMap sym_map = kv.Value;
if (sym_filter == null || sym_filter(sym)) {
foreach (var kv_int in sym_map.exp_price_map) {
rv[new VolSurfaceExpiration(kv_int.Key)] = kv_int.Value;
}
}
}
return rv;
}
public IReadOnlyDictionary<SecurityExpiration, DriverMD> get_price_map() {
return new Dictionary<SecurityExpiration, DriverMD>(exp_price_map);
}
}
}
}
@@ -1,70 +0,0 @@
namespace mbts.spark {
using bts.api;
using System;
using System.Collections.Generic;
using System.Linq;
// A lot of options data (migrated from edge) is not normalized
// on the sec_id, but rather on a combination of symbol, exp, strike.
// Additionally the exchange is missing. This class serves as that
// one since point to resolve that.
// (Over time we will normalize all the data and this wont be needed)
public static class SecurityNormalizer {
// f(option_exp:driver_md) -> (sec_id:driver_md)
public static IDictionary<UInt32, DriverMD> normalize_drivers(
OptionDriverPriceMap driver_map, SystemContractData sc_data) {
if (driver_map is null) {
return new Dictionary<uint, DriverMD>(0);
}
Dictionary<UInt32, DriverMD> rv = new Dictionary<uint, DriverMD>();
foreach (var kv in driver_map.get_price_map()) {
var exp = kv.Key;
var dmd = kv.Value;
IEnumerable<SecurityMD> exp_secs = sc_data.get_securities(
(sec_md) => {
var s = sec_md.security;
return s.security_type() == SecurityType.Option &&
s.Symbol == driver_map.option_symbol &&
s.earliest_expiration() == exp.expiration;
}
);
foreach (var sec_md in exp_secs) {
rv.Add(sec_md.security.Id, kv.Value);
}
}
return rv;
}
public static IEnumerable<Tuple<SecurityImmutable, TheoPoint>> normalize_interp_theos(
OptionTheoInterpolationData symbol_theos, SystemContractData sc_data) {
if (symbol_theos is null) {
return Enumerable.Empty<Tuple<SecurityImmutable, TheoPoint>>();
}
var rv = new List<Tuple<SecurityImmutable, TheoPoint>>();
foreach (DateTime exp in symbol_theos.get_expirations()) {
Dictionary<double, Dictionary<double, TheoPoint>> exp_data =
symbol_theos.get_expiration_data(exp);
// exp_data is driver vs strike vs TP
foreach (var exp_kv in exp_data) {
foreach (var k_kv in exp_kv.Value) {
double strike = k_kv.Key;
TheoPoint tp = k_kv.Value;
IEnumerable<SecurityMD> calls = sc_data.get_options(
null, symbol_theos.symbol, exp, strike, PutOrCall.Call);
IEnumerable<SecurityMD> puts = sc_data.get_options(
null, symbol_theos.symbol, exp, strike, PutOrCall.Put);
foreach (SecurityMD sec_md in calls) {
rv.Add(Tuple.Create(sec_md.security, tp));
}
foreach (SecurityMD sec_md in puts) {
rv.Add(Tuple.Create(sec_md.security, tp));
}
}
}
}
return rv;
}
}
}
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -108,22 +107,6 @@ namespace mbts.spark {
}
}
public SecurityImmutable? get_option_contract(
Venue venue, string symbol, DateTime exp, double strike, PutOrCall cp) {
var k_tup = get_option_sec_id(venue, symbol, exp, strike);
if (k_tup != null) {
var id = cp == PutOrCall.Call ? k_tup.Item1 : k_tup.Item2;
if (id.HasValue) {
var id_val = id.Value;
SecurityMD sec_md;
if (sec_md_map.TryGetValue(id_val, out sec_md)) {
return sec_md.security;
}
}
}
return null;
}
public Tuple<uint?, uint?> get_option_sec_id(
Venue venue, string symbol, DateTime exp, double strike) {
var s_exp = new SecurityExpiration(venue, SecurityType.Option, symbol, exp);
@@ -190,7 +173,7 @@ namespace mbts.spark {
}
IDictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>> rv_data =
new Dictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>>();
return Tuple.Create(exp.to_security_expiration(type: SecurityType.Option), rv_data);
return Tuple.Create(exp.to_security_expiration(), rv_data);
}
internal IDictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>> get_option_bbo(
@@ -199,11 +182,12 @@ namespace mbts.spark {
Dictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>> rv =
new Dictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>>();
foreach (SecurityStrike k in strikes) {
var (call_id, put_id) = k.call_put_id;
// call
uint? call_id = k.call_put_id.Item1;
var call_sec_md = get_sec_md(call_id);
VolSurfaceMDUnit call_bbo = call_sec_md == null ? VolSurfaceMDUnit.empty : call_sec_md.Item2;
// put
uint? put_id = k.call_put_id.Item2;
var put_sec_md = get_sec_md(put_id);
VolSurfaceMDUnit put_bbo = put_sec_md == null ? VolSurfaceMDUnit.empty : put_sec_md.Item2;
rv.Add(k.strike, new Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>(call_bbo, put_bbo));
@@ -259,28 +243,5 @@ namespace mbts.spark {
kv.Value.Count > 0).Select(kv => kv.Key.expiration);
}
public IEnumerable<SecurityMD> spreads {
get {
return sec_md_map.Where(kv => kv.Value.security.HasSpread)
.Select(kv => kv.Value);
}
}
public IEnumerable<SecurityMD> get_securities(Func<SecurityMD, bool> criteria) {
return sec_md_map.Values.Where(sec_md => criteria(sec_md));
}
public IEnumerable<SecurityMD> get_options(
Venue? exchange, string option_name, DateTime? exp, double? strike, PutOrCall? cp) {
return get_securities(sc => {
var s = sc.security;
return s.security_type() == SecurityType.Option &&
(exchange == null || s.Exchange == exchange) &&
(exp == null || s.earliest_expiration() == exp) &&
(strike == null || s.strike() == strike) &&
(cp == null || s.Option.CallPut == cp);
});
}
}
}
@@ -22,7 +22,6 @@ namespace mbts.spark {
/// from the same secondary thread.
/// </summary>
public class ThreadedBlackboard {
private readonly string _bb_name;
private IBus _bus;
private List<IDisposable> _subs = new List<IDisposable>();
private HashSet<object> _tag_map = new HashSet<object>();
@@ -32,10 +31,9 @@ namespace mbts.spark {
private IDictionary<object, object> _cache = new
Dictionary<object, object>();
private static int _tid() { return VolSurfaceTroubleshootingFunctions.tid(); }
public ThreadedBlackboard(string name, IBus bus) {
bts.Log.Ui.info($"ThreadedBlackBoard.cons tid:{_tid()} bb_name: {name}");
public ThreadedBlackboard(IBus bus) {
bts.Log.Ui.info($"ThreadedBlackBoard.cons tid:{ _tid()}");
_bus = bus;
_bb_name = name;
subscribe(SparkTags.SparkTimer);
System.Threading.Thread timer_thread = new System.Threading.Thread(() => {
DateTime start_time = DateTime.Now;
@@ -63,13 +61,15 @@ namespace mbts.spark {
return;
}
_tag_map.Add(tag);
bts.Log.Ui.info($"ThreadedBlackBoard.subscribe tag:{tag} tid:{_tid()}");
bts.Log.Ui.info($"ThreadedBlackBoard.subscribe tag:{tag} tid:{ _tid()}");
var observable = _bus.subscribe(tag).OnDispatcher().Subscribe(
data => _on_bus_data(tag, data));
_subs.Add(observable);
}
public void post<T>(ITag<T> tag, T data, bool block_until_written = false) {
bts.Log.Ui.diag($"ThreadedBlackBoard.post tag:{tag} data:{_data_string(data)} " +
$"tid:{_tid()}");
_cache[tag] = data;
_bus.post(tag, data);
}
@@ -92,6 +92,8 @@ namespace mbts.spark {
timer_callback();
return;
}
bts.Log.Ui.diag($"ThreadedBlackBoard._on_bus_data tag:{tag} data:{_data_string(data)} " +
$"tid:{_tid()}");
data_callback(tag, data);
}
@@ -1,25 +1,17 @@
using bts.api;
using bts.api.analytics;
using bts.collections;
using bts.utils;
using IronPython.Compiler.Ast;
using mbts;
using mbts.analytics;
using mbts.spark;
using mbts.ui.controls;
using mbts.ui.converters;
using mbts.ui.shell;
using Spark.Core;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using mbts;
using mbts.analytics;
using mbts.ui.controls;
using mbts.ui.shell;
using mbts.spark;
using bts.utils;
using bts.api;
using bts.collections;
using System.Collections.Immutable;
namespace mbts.spark {
/// This contains the list of global values for the vol
@@ -63,27 +55,6 @@ namespace mbts.spark {
return rv;
}
public static string log_string(this VolSurfaceCalculationRequest vs_req) {
if (vs_req == null) {
return "Null";
}
string action_str = String.Empty;
foreach (var kv in vs_req.action_map) {
action_str += $"exp: {kv.Key.ToString()} action: {kv.Value.ToString()}, ";
}
action_str = action_str.TrimEnd(',', ' ');
return $"vs_inp: {vs_req.volsurface_input.log_string()}\nreq_id: {vs_req.request_id}\n" +
$"action_map: {action_str}\nup: {vs_req.cascade_up} down: {vs_req.cascade_down}";
}
public static string log_string(this VolSurfaceCalculationResponse resp) {
if (resp == null) {
return "Null";
}
return $"req_id: {resp.request_id} vs_output: " +
$"{resp.volsurface_calculation_output.volsurface_input.log_string()}";
}
private static string _trim_leading_char(string s, char c) {
if (!s.is_null_or_empty() && s.EndsWith(c)) {
return s.Remove(s.Length - 1, 1);
@@ -92,12 +63,11 @@ namespace mbts.spark {
}
internal static string log_string(this VolSurfaceDisplayModel.DisplayModelData dm_data) {
var vs_output = dm_data.vs_output;
var exp_grid = dm_data.exp_grid;
var vs_output_str = vs_output == null ? "NULL" : vs_output.volsurface_input.log_string();
var exp_grid_str = exp_grid == null ? "NULL" : exp_grid.log_string();
var rv = $"DisplayModelData\nvs_output:\n{vs_output_str}\n" +
$"exp_grid\n{exp_grid_str}\n";
string rv = String.Empty;
string global_grid_str = dm_data.global_grid == null ? "Null" : dm_data.global_grid.log_string();
rv += $"global_grid: {global_grid_str} \n";
string exp_grid_str = dm_data.exp_grid == null ? "Null" : dm_data.exp_grid.log_string();
rv += $"exp_grid: {exp_grid_str} \n";
return rv;
}
@@ -113,19 +83,19 @@ namespace mbts.spark {
}
internal static string log_string(this VolSurfaceDisplayGrid dgrid) {
Func<IDictionary<string, object>, string> log_exp = (col_val_map) => {
Func<IDictionary<string, object>, string> log_exp = (map) => {
string rv = String.Empty;
foreach (var kv in col_val_map) {
foreach (var kv in map) {
string val_string = kv.Value == null ? "Null" : kv.Value.ToString();
rv += $"{kv.Key}: {val_string}, ";
rv += $"{kv.Key}:{val_string},";
}
return _trim_leading_char(rv, ',');
};
string rv = String.Empty;
foreach (var kv in dgrid.data_map) {
rv += $"Exp: {kv.Key.expiration.ToShortDateString()} Data: {log_exp(kv.Value)}\n";
rv += $"Exp:{kv.Key.expiration.ToShortDateString()} Data:{log_exp(kv.Value)},";
}
return rv;
return _trim_leading_char(rv, ',');
}
internal static string log_string(this OptionDriverPriceMap map) {
@@ -162,7 +132,7 @@ namespace mbts.spark {
internal static string log_string(this Theos t) {
string rv = $"strike: {t.strike} fwd: {t.fwd} driver: {t.driver} " +
$"move: {t.move} vol: {t.vol} c_theo: {t.call} " +
$"move: { t.move} vol: {t.vol} c_theo: {t.call} " +
$"p_theo: {t.put}";
return rv;
}
@@ -181,8 +151,8 @@ namespace mbts.spark {
foreach (var kv in sc_data.strike_map) {
var exp = kv.Key;
foreach (var k in kv.Value) {
rv += $"exp: {exp} k: {k} c_id: {k_id(k.call_put_id.call)} " +
$"p_id: {k.call_put_id.put}" + "\n";
rv += $"exp: {exp} k: {k} c_id: {k_id(k.call_put_id.Item1)} " +
$"p_id: {k.call_put_id.Item2}" + "\n";
}
}
return rv;
@@ -208,67 +178,6 @@ namespace mbts.spark {
return rv;
}
internal static string log_string(this ComboFullData cf_data) {
string rv = String.Empty;
if (cf_data.option_expirations != null) {
foreach (var kv in cf_data.option_pos_map) {
var exp = kv.Key;
foreach (var kv_in in kv.Value) {
double k = kv_in.Key;
ComboStrikeData kp = kv_in.Value;
rv += $"exp: {exp.ToString()} k: {k} Pos: {kp.call_pos},{kp.put_pos} ({kp.call_raw_pos},{kp.put_raw_pos}) ";
}
}
}
if (cf_data.future_pos_map != null) {
foreach (var kv in cf_data.future_pos_map) {
rv += $"exp: {kv.Key.ToString()} {kv.Value.Item1},{kv.Value.Item2} ";
}
}
return rv;
}
internal static string log_string(this SlideFullData sl_data) {
Func<SlideGreekUnitKey, string> sguk_str = (sguk) => sguk.display_name;
Func<SlideGreekUnit, string> sgu_str = (sgu) => {
if (sgu == null)
return "No Handles";
string rv = $"Handle: {sgu.driver_price} ";
foreach (double d in sgu.position_greeks)
rv += $"{d}, ";
rv = rv.Trim();
if (rv.EndsWith(","))
rv = rv.TrimEnd(',');
return rv;
};
string rv = String.Empty;
foreach (var kv in sl_data.map) {
rv += $"SlideGreekUnitKey: {sguk_str(kv.Key)}\n";
foreach (var i_kv in kv.Value) {
rv += sgu_str(i_kv.Value) + "\n";
}
}
rv += $"Overriden?: {sl_data.ui_override != null}";
return rv;
}
internal static string log_string(this VolSurfaceUsageListImmutable vsu_list) {
string rv = "VolSurfaceUsageList:";
foreach (var u in vsu_list.UsageList) {
rv += $" {u.SecTup.Symbol}:{u.VsId}";
}
return rv;
}
internal static string log_string(this IPNLUnit p) {
string rv = "IPNLUnit:";
rv += $" account: {p.account} sec: {p.security.security_desc()} driver: {p.driver} theo: {p.theo} settle: {p.settle_price}" +
$" start_pos: {p.start_long_pos - p.start_short_pos} day_pos: {p.day_long_pos - p.day_short_pos}" +
$" start_pnl: {p.start_pnl} start_delta: {p.start_delta} start_vega: {p.start_vega}" +
$" day_pnl: {p.day_pnl} day_delta: {p.day_delta} day_vega: {p.day_vega}";
return rv;
}
internal static int tid() {
return System.Threading.Thread.CurrentThread.ManagedThreadId;
}
@@ -86,14 +86,9 @@ namespace mbts.spark {
}
private void _save_exp_params() {
var api_driver = new ExpirationDriverImmutable(new() {
Security = driver_exp.to_api_sec_exp()
});
var exp_params = new AnalyticsExpirationParameters[] {
new AnalyticsExpirationParameters(
option_exp1, api_driver, driver_exp, driver_exp, 0, 0, double.NaN, 1, 1),
new AnalyticsExpirationParameters(
option_exp2, api_driver, driver_exp, driver_exp, 0, 0, double.NaN, 1, 1),
var exp_params = new AnalyticsExpirationParameters[]{
new AnalyticsExpirationParameters(option_exp1, driver_exp, driver_exp, driver_exp, 0, 0, Double.NaN, 1, 1),
new AnalyticsExpirationParameters(option_exp2, driver_exp, driver_exp, driver_exp, 0, 0, Double.NaN, 1, 1),
};
_bb.post(SparkTags.ExpirationParametersCommitTag, exp_params);
}
@@ -108,4 +103,4 @@ namespace mbts.spark {
}
#endregion
}
}
}
@@ -31,24 +31,11 @@ namespace mbts.ui.controls {
VolSurfaceExpiration exp = p.Key;
VolCurveOutput exp_output = p.Value;
IDictionary<VolSurfaceDisplayGridColumn, object> out_exp_map = VolSurfaceUIAdapterUtils.get_exp_data(ref out_grid, exp);
out_exp_map[_fwd_col] = exp_output?.fwd ?? double.NaN;
out_exp_map[_ref_fwd_col] = exp_output
?.adjusted_curve_parameters
?.core_parameters
?.ref_forward
?? double.NaN;
out_exp_map[_live_driver_col] = exp_output
?.aux_data
?.driver_price
?? double.NaN;
out_exp_map[_driver_override_col] = exp_output
?.adjusted_curve_parameters
?.core_parameters
?.driver_override
?? double.NaN;
out_exp_map[_fwd_col] = exp_output == null ? Double.NaN : exp_output.fwd;
out_exp_map[_ref_fwd_col] = exp_output != null && exp_output.adjusted_curve_parameters != null && exp_output.adjusted_curve_parameters.core_parameters != null ?
exp_output.adjusted_curve_parameters.core_parameters.ref_forward : Double.NaN;
out_exp_map[_live_driver_col] = (exp_output != null && exp_output.aux_data != null) ? exp_output.aux_data.driver_price : Double.NaN;
out_exp_map[_driver_override_col] = exp_output.adjusted_curve_parameters.core_parameters.driver_override;
}
}
@@ -3,16 +3,15 @@ namespace mbts.spark {
using mbts.ui;
using mbts.ui.controls.graph;
using mbts.ui.shell;
using ProtoBuf;
using Spark.Core.Protobuf;
using System;
using System.Collections.Generic;
[ProtoContract]
class VolSurfaceGraphSettings : IProtobuf<VolSurfaceGraphSettings> {
class VolSurfaceGraphSettings : IEquatable<VolSurfaceGraphSettings>, ICloneable {
public static VolSurfaceGraphSettings get_default() {
return new VolSurfaceGraphSettings {
put_control_points = new ScatterSeriesSettings {
@@ -110,8 +109,8 @@ namespace mbts.spark {
// }
//}
public VolSurfaceGraphSettings Clone() =>
ProtobufSerializationHelper.clone(this);
public object Clone() =>
ProtoBufSerializationHelper.clone_protobuf(this);
public bool Equals(VolSurfaceGraphSettings other) =>
other != null &&
@@ -6,10 +6,6 @@ using mbts.analytics;
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core;
using Spark.Core.Disposables;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -55,7 +51,7 @@ namespace mbts.spark {
var vs_removes = removed.Select(exp => exp.ToVSExp());
var true_removed = removed.Where(rmv => !vs_adds.Contains(rmv.ToVSExp()));
var true_added = added.Except(vs_removes.Concat(_hidden_exps), v => v.ToVSExp());
var true_added = added.Where(add => !vs_removes.Contains(add.ToVSExp()));
var changed_calc_modes = added.Where(add => vs_removes.Contains(add.ToVSExp()));
foreach (var tr in true_removed) {
@@ -65,10 +61,16 @@ namespace mbts.spark {
}
}
var comp = ComparerWrapper<object>.wrap_cast(
NullCheckComparer.create<IExpirationBasedRow>(
(r1, r2) => r1.expiration.CompareTo(r2.expiration)));
visible_rows.InsertSorted(true_added, comp);
foreach (var ta in true_added) {
if (_hidden_exps.Contains(ta.ToVSExp()) ||
!rows.TryGetFirst(r => r.expiration.expiration == ta.expiration, out var row)) {
continue;
}
visible_rows.InsertSorted(row,
Comparer<IExpirationBasedRow>.Create(
(r1, r2) => r1.expiration.CompareTo(r2.expiration))
.wrap<IExpirationBasedRow, object>());
}
foreach (var ccm in changed_calc_modes) {
int index = visible_rows
@@ -10,10 +10,6 @@ namespace mbts.spark {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Analytics.View;
using Spark.Core.Disposables;
using Spark.Core.Protobuf;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -31,13 +27,13 @@ namespace mbts.spark {
readonly LinearAxisVM _x_axis = new() { position = AxisPosition.BottomLeftToRight };
readonly LinearAxisVM _y_axis = new() { position = AxisPosition.LeftBottomToTop };
readonly Dictionary<SecurityExpiration, ExpGraphBounds> _expiration_graph_bounds;
readonly VolSurfaceGraphSettings _settings;
readonly ObservableCollection<VolSurfaceDisplayGridExpiration> _expirations = new();
readonly Dictionary<VolSurfaceDisplayGridExpiration, SeriesSetVM> _expiration_series_cache = new();
VolSurfaceExpiration _md_exp;
VolSurfaceDisplayGridExpiration _expiration;
VolSurfaceDisplayGridExpiration _earliest_selected_expiration;
VolSurfaceGraphSettings _settings;
object _implied_vol_market_side = ImpliedVolHelper.None;
bool _show_vol_path;
@@ -50,9 +46,7 @@ namespace mbts.spark {
if (startup_data.vol_curve_x_axis == null) {
var x_axis = BTSOxyGraphAxisDouble.Default;
x_axis.name = "Strike";
x_axis.axis_line_color = BTSColors.Graph.axis_line;
x_axis.major_grid_line_color = BTSColors.Graph.grid_line;
x_axis.tick_line_color = BTSColors.Graph.tick_line;
x_axis.position = AxisPosition.BottomLeftToRight;
x_axis.minimum = 0;
x_axis.maximum = 2000;
@@ -64,9 +58,7 @@ namespace mbts.spark {
if (startup_data.vol_curve_y_axis == null) {
var y_axis = BTSOxyGraphAxisDouble.Default;
y_axis.name = "Volatility";
y_axis.axis_line_color = BTSColors.Graph.axis_line;
y_axis.major_grid_line_color = BTSColors.Graph.grid_line;
y_axis.tick_line_color = BTSColors.Graph.tick_line;
y_axis.position = AxisPosition.LeftBottomToTop;
y_axis.minimum = 0;
y_axis.maximum = 1;
@@ -86,17 +78,13 @@ namespace mbts.spark {
if (_model.get_active_settings() is not VolSurfaceSettings vss) {
return null;
}
var vm = new VolSurfaceGraphSettingsVM(vss.core_settings.vol_surface_name);
vm.load_from(_settings.Clone());
vm.request_commit += commit;
return new VolSurfaceGraphSettingsWindow {
DataContext = vm
};
void commit(VolSurfaceGraphSettings settings) {
_settings = settings;
_recreate_series();
}
// return new VolSurfaceGraphSettingsEditor {
// DataContext = new VolSurfaceGraphSettingsEditorVM(vss.core_settings.vol_surface_name, ProtoBufSerializationHelper.clone_protobuf(_settings), settings => {
// _settings = settings;
// _recreate_series();
// })
// };
return null;
});
graph_vm = new BTSGraphVM {
plot_area_background = Colors.Transparent,
@@ -125,7 +113,7 @@ namespace mbts.spark {
})
.BufferLatest(TimeSpan.FromSeconds(0.1))
.OnDispatcher()
.SelectSeries(series => Observable.Create<SeriesVM>(o => {
.SelectManyInSeries(series => Observable.Create<SeriesVM>(o => {
o.OnNext(null);
return Disposable.Create(series
.Select(svm => svm.points.Subscribe((s, e) => o.OnNext(svm)))
@@ -193,20 +181,6 @@ namespace mbts.spark {
static (double call_wing_start, double put_wing_start) _get_actual_wing_starts(VolCurveDisplayGraph disp_graph) =>
disp_graph.all_points.Select(p => p.Item1).MinMax() ?? (double.NaN, double.NaN);
void _recreate_series() {
/// 'graph_vm.series_set' is bound to '_expirations', so clearing and re-adding expirations
/// in turn regenerates the series. '_expiration_series_cache' must
/// be cleared as well, otherwise cached series vms would just be back to the collection
/// ('graph_vm.series_set').
var visible_exp = _earliest_selected_expiration;
_expiration_series_cache.Clear();
_expirations.SetItems(_model.get_ordered_expirations());
foreach (var svm in graph_vm.series.OfType<SeriesSetVM>()) {
svm.is_visible = svm.args.Equals(visible_exp);
}
_update();
}
void _expirations_changed(IEnumerable<VolSurfaceDisplayGridExpiration> added, IEnumerable<VolSurfaceDisplayGridExpiration> removed) {
_expirations.sync_with(_model.get_ordered_expirations());
_update();
@@ -0,0 +1,84 @@
namespace mbts.spark {
using mbts.ui;
using mbts.ui.shell;
using System;
using System.Reactive.Linq;
using System.Windows.Input;
class VolGridCellVM : DisposableViewModelBase {
readonly Property<object> _candidate_value;
readonly Property<object> _committed_value;
readonly Property<bool> _is_dirty;
public VolGridCellVM(string column_id) {
this.column_id = column_id ?? throw new ArgumentNullException(nameof(column_id));
_committed_value = backing<object>(nameof(committed_value));
_candidate_value = backing<object>(nameof(candidate_value));
_is_dirty = backing(nameof(is_dirty),
_committed_value.observe_current(true)
.CombineLatest(_candidate_value.observe_current(true))
.Select(tup => !Equals(tup.First, tup.Second))
.OnDispatcher());
}
public string column_id { get; }
public object committed_value {
get => _committed_value.Value;
set => _committed_value.set(value);
}
public object candidate_value {
get => _candidate_value.Value;
set => _candidate_value.set(value);
}
public bool is_dirty => _is_dirty.Value;
public IObservable<object> observe_candidate(bool start_immediately = false) =>
_candidate_value.observe_current(start_immediately);
public void revert() =>
candidate_value = committed_value;
public override string ToString() =>
$"column_id: {column_id}, committed: {committed_value}, candidate: {candidate_value}";
}
class VolGridCellVM<T> : VolGridCellVM {
public VolGridCellVM(string column_id) : base(column_id) { }
public VolGridCellVM(string column_id, T committed_value, T candidate_value) : base(column_id) {
this.committed_value = committed_value;
this.candidate_value = candidate_value;
}
public new T committed_value {
get => base.committed_value is T v ? v : default;
set => base.committed_value = value;
}
public new T candidate_value {
get => base.candidate_value is T v ? v : default;
set => base.candidate_value = value;
}
}
class VolGridTickCellVM : VolGridCellVM {
ICommand _tick_up_cmd;
ICommand _tick_down_cmd;
public VolGridTickCellVM(string column_id) : base(column_id) { }
public event Action<bool> request_tick;
public ICommand tick_up_cmd =>
_tick_up_cmd ??= new DelegateCommand(() =>
request_tick?.Invoke(true));
public ICommand tick_down_cmd =>
_tick_down_cmd ??= new DelegateCommand(() =>
request_tick?.Invoke(false));
}
}
@@ -1,11 +0,0 @@
#nullable enable
namespace mbts.spark.VolSurface {
using System.Windows.Media;
readonly struct VolSurfaceCellState {
public bool is_dirty => committed_text is not null;
public FormattedText? text { get; init; }
public FormattedText? committed_text { get; init; }
}
}
@@ -1,80 +0,0 @@
#nullable enable
namespace mbts.spark {
using bts;
using mbts.ui.controls;
using mbts.ui.shell;
using Spark.Core;
using System;
class VolSurfaceCellVM : DisposableViewModelBase {
readonly Property<object?> _candidate_value;
readonly Property<object?> _committed_value;
readonly DelegateProperty<bool> _is_dirty;
readonly DeferrableAction _refresh;
public VolSurfaceCellVM(
VolSurfaceDisplayGridExpiration expiration, VolSurfaceDisplayGridColumn column) {
this.column = column ?? throw new ArgumentNullException(nameof(column));
this.expiration = expiration ?? throw new ArgumentNullException(nameof(expiration));
_committed_value = backing<object?>(nameof(committed_value), column.comparer);
_candidate_value = backing<object?>(nameof(candidate_value), column.comparer);
_is_dirty = backing(
nameof(is_dirty), () => !column.comparer.Equals(candidate_value, committed_value));
_refresh = new(_is_dirty.refresh);
_committed_value.Changed += on_committed_changed;
_candidate_value.Changed += on_candidate_changed;
if (column.type.is_editable()) {
_is_dirty.Changed += (o, n) => {
Log.Ui.info(
$"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
$"commited: {committed_value}; candidate: {candidate_value}");
};
}
void on_committed_changed(object? oldval, object? newval) =>
_is_dirty.refresh();
void on_candidate_changed(object? oldval, object? newval) =>
_is_dirty.refresh();
}
public event PropertyChange<object?>? candidate_value_changed {
add => _candidate_value.Changed += value;
remove => _candidate_value.Changed -= value;
}
public VolSurfaceDisplayGridColumn column { get; }
public string column_id => column.unique_col_name;
public VolSurfaceDisplayGridExpiration expiration { get; }
public object? committed_value {
get => _committed_value.Value;
set => _committed_value.set(value);
}
public object? candidate_value {
get => _candidate_value.Value;
set => _candidate_value.set(value);
}
public bool is_dirty => _is_dirty.Value;
public IObservable<object?> observe_candidate(bool start_immediately = false) =>
_candidate_value.observe_current(start_immediately);
public void reset(object? committed_value, object? candidate_value) {
using var _ = _refresh.defer();
this.committed_value = committed_value;
this.candidate_value = candidate_value;
}
public void revert() =>
candidate_value = committed_value;
public override string ToString() =>
$"column: {column}, committed: {committed_value}, candidate: {candidate_value}";
}
}
@@ -26,7 +26,7 @@ namespace mbts.spark.VolSurface {
id = key ?? throw new InvalidOperationException("Column key cannot be null");
}
public VolSurfaceDisplayGridColumn model { get; }
public VolSurfaceDisplayGridColumn model { get; set; }
public VolSurfaceColumnType type => model.type;
public new VolSurfaceColumnSettings create() {
@@ -43,7 +43,7 @@ namespace mbts.spark.VolSurface {
static Thickness _header_cell_padding => new(6, 0, 4, 0);
IValueConverter _data_context_converter =>
ValueConverter.create<VolSurfaceRowVM, object>(row => row?.get_cell(model));
ValueConverter.create<VolSurfaceRowContext, object>(row => row?.get_cell(model));
public override FrameworkElement view_factory() {
return type switch {
@@ -58,7 +58,7 @@ namespace mbts.spark.VolSurface {
HorizontalAlignment = HorizontalAlignment.Left,
Foreground = BTSColors.header_text.to_brush(),
};
text.SetBinding(TextBlock.TextProperty, new Binding(nameof(VolSurfaceRowVM.expiration)));
text.SetBinding(TextBlock.TextProperty, new Binding(nameof(VolSurfaceRowContext.expiration)));
var bd = new Border {
HorizontalAlignment = HorizontalAlignment.Stretch,
Padding = _header_cell_padding,
@@ -73,12 +73,12 @@ namespace mbts.spark.VolSurface {
var view = new TextBlock {
HorizontalAlignment = HorizontalAlignment.Right,
Margin = _cell_padding,
Foreground = BTSColors.VolSurfaceGrid.readonly_column_foreground
Foreground = Brushes.Blue,
};
view.SetBinding(FrameworkElement.DataContextProperty, new Binding() {
Converter = _data_context_converter
});
view.SetBinding(TextBlock.TextProperty, new Binding(nameof(VolSurfaceCellVM.candidate_value)) {
view.SetBinding(TextBlock.TextProperty, new Binding(nameof(VolGridCellVM.candidate_value)) {
Converter = ValueConverter.create(obj => model.format(obj))
});
return view;
@@ -101,16 +101,16 @@ namespace mbts.spark.VolSurface {
view.SetBinding(FrameworkElement.DataContextProperty, new Binding() {
Converter = _data_context_converter
});
view.SetBinding(VolSurfaceCellView.CommittedValueProperty, new Binding(nameof(VolSurfaceCellVM.committed_value)) {
view.SetBinding(VolSurfaceCellView.CommittedValueProperty, new Binding(nameof(VolGridCellVM.committed_value)) {
Mode = BindingMode.OneWay,
Converter = ValueConverter.create(obj => model.format(obj))
});
view.SetBinding(VolSurfaceCellView.CandidateValueProperty, new Binding(nameof(VolSurfaceCellVM.candidate_value)) {
view.SetBinding(VolSurfaceCellView.CandidateValueProperty, new Binding(nameof(VolGridCellVM.candidate_value)) {
Mode = BindingMode.TwoWay,
Converter = ValueConverter.create<object, string>(obj => model.format(obj), obj => model.parse(obj))
});
view.SetBinding(VolSurfaceCellView.TickDownCommandProperty, new Binding(nameof(VolSurfaceTickCellVM.tick_down_cmd)));
view.SetBinding(VolSurfaceCellView.TickUpCommandProperty, new Binding(nameof(VolSurfaceTickCellVM.tick_up_cmd)));
view.SetBinding(VolSurfaceCellView.TickDownCommandProperty, new Binding(nameof(VolGridTickCellVM.tick_down_cmd)));
view.SetBinding(VolSurfaceCellView.TickUpCommandProperty, new Binding(nameof(VolGridTickCellVM.tick_up_cmd)));
var bd = new Border {
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
@@ -1,5 +1,4 @@
#nullable enable
namespace mbts.spark.VolSurface {
namespace mbts.spark.VolSurface {
using bts;
using bts.collections;
using bts.utils;
@@ -12,10 +11,6 @@ namespace mbts.spark.VolSurface {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core;
using Spark.Core.Disposables;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -29,25 +24,19 @@ namespace mbts.spark.VolSurface {
class VolSurfaceGridVM : DisposableViewModelBase {
readonly VolSurfaceVM _parent;
readonly UserTuple _user;
readonly ObservableCollection<VolSurfaceDisplayGridExpiration> _expirations = new();
readonly DeferrableAction _invalidate_rows;
readonly DeferrableActionWrapper _invalidate_rows;
readonly ColumnsModel _columns_model;
ISet<string> _hidden_columns;
ISet<VolSurfaceExpiration>? _hidden_expirations;
IDisposable? _shift_editor;
ISet<VolSurfaceExpiration> _hidden_expirations;
IDisposable _shift_editor;
public VolSurfaceGridVM(
VolSurfaceVM vm,
VolSurfaceDisplayModel model,
ISet<string> hidden_columns,
ISet<VolSurfaceExpiration> hidden_expirations,
UserTuple user) {
Log.notnull(hidden_columns);
Log.notnull(hidden_expirations);
public VolSurfaceGridVM(VolSurfaceVM vm, VolSurfaceDisplayModel model,
ISet<string> hidden_columns, ISet<VolSurfaceExpiration> hidden_expirations) {
Log.assert(hidden_columns != null);
Log.assert(hidden_expirations != null);
_parent = vm;
_user = user;
this.model = model;
_columns_model = new(create_exp_column_view());
_hidden_columns = hidden_columns;
@@ -74,31 +63,16 @@ namespace mbts.spark.VolSurface {
};
_dispose = Disposable.Create(
ObservableAutoWrapper.connect(_expirations, rows, row_factory),
ObservableAutoWrapper.connect_item_hooks(rows, connect_row),
ObservableAutoWrapper.connect(_expirations, rows, exp => new VolSurfaceRowContext(this, exp), row => row.Dispose()),
sorted_visible_rows.bind(visible_rows),
Disposable.Create(() => {
this.model.on_candidate_grid_change -= _on_grid_updated;
this.model.on_committed_grid_change -= _on_grid_updated;
rows.DisposeAll(true);
Disposable.dispose(rows);
Disposable.dispose(ref _shift_editor);
}),
_columns_model);
VolSurfaceRowVM row_factory(VolSurfaceDisplayGridExpiration exp) =>
new(this, exp, _user);
IDisposable connect_row(VolSurfaceRowVM row) {
row.request_calculate += request_recalc;
return Disposable.Create(() => {
row.request_calculate -= request_recalc;
row.Dispose();
});
void request_recalc() =>
model.set_candidate_grid(current_grid);
}
VolSurfaceColumn create_exp_column_view() {
var filter = new ComboCheckBox();
filter.SetResourceReference(FrameworkElement.StyleProperty, "ExpirationFilterDropDown");
@@ -118,7 +92,7 @@ namespace mbts.spark.VolSurface {
}
}
public event Action<IEnumerable<VolSurfaceDisplayGridExpiration>>? on_selection_changed;
public event Action<IEnumerable<VolSurfaceDisplayGridExpiration>> on_selection_changed;
public VolSurfaceDisplayGrid current_grid { get; private set; }
public VolSurfaceDisplayGrid committed_grid { get; private set; }
@@ -128,14 +102,14 @@ namespace mbts.spark.VolSurface {
public IReadOnlyObservableCollection<DIYGrid.ColumnBase> columns =>
_columns_model.views_with_header_ro;
public ObservableCollection<VolSurfaceRowVM> rows { get; } = new();
public ObservableCollection<VolSurfaceRowVM> visible_rows { get; } = new();
public ObservableSortedCollection<VolSurfaceRowVM> sorted_visible_rows { get; } = new(_row_comparer);
public ObservableCollection<VolSurfaceRowContext> rows { get; } = new();
public ObservableCollection<VolSurfaceRowContext> visible_rows { get; } = new();
public ObservableSortedCollection<VolSurfaceRowContext> sorted_visible_rows { get; } = new(_row_comparer);
public bool any_masters_selected =>
selected_rows.select_display_exp().where_master().Any();
public IEnumerable<VolSurfaceRowVM> selected_rows =>
public IEnumerable<VolSurfaceRowContext> selected_rows =>
visible_rows.where_selected();
public IEnumerable<DIYGridSuperHeaderVM> super_headers =>
@@ -144,14 +118,13 @@ namespace mbts.spark.VolSurface {
.Select(group => new DIYGridSuperHeaderVM(group.Key, group.Count(), collapsed_content: _short_header(group.Key)))
.ToList();
public ICommand populate_empty_exps_cmd => _parent.populate_empty_exps_cmd;
public ICommand main_settings_cmd => _parent.main_settings_cmd;
public ICommand advanced_settings_cmd => _parent.advanced_settings_cmd;
public ICommand graph_settings_cmd => _parent.vol_curve_vm.settings_command;
public ICommand grid_settings_cmd => _parent.grid_settings_cmd;
public ICommand main_settings_command => _parent.main_settings_command;
public ICommand advanced_settings_command => _parent.advanced_settings_command;
public ICommand graph_settings_command => _parent.vol_curve_vm.settings_command;
public ICommand grid_settings_command => _parent.grid_settings_command;
static IComparer<VolSurfaceRowVM> _row_comparer =>
Comparer<VolSurfaceDisplayGridExpiration>.Default.wrap<VolSurfaceDisplayGridExpiration, VolSurfaceRowVM>(r => r.expiration);
static IComparer<VolSurfaceRowContext> _row_comparer =>
Comparer<VolSurfaceDisplayGridExpiration>.Default.wrap<VolSurfaceDisplayGridExpiration, VolSurfaceRowContext>(r => r.expiration);
public void load_from(VolSurfaceMacroGridSettings grid_settings) {
_hidden_columns = new HashSet<string>(grid_settings.hidden_cols());
@@ -168,14 +141,13 @@ namespace mbts.spark.VolSurface {
rows.select_exp().Except(visible_rows.select_exp());
public void invalidate_rows() =>
_invalidate_rows.Invoke();
_invalidate_rows.execute();
public IDisposable defer_invalidate_rows() =>
_invalidate_rows.defer();
_invalidate_rows.defer_execution();
public VolSurfaceColumn get_column(string id) =>
columns.FirstOrDefault(c => c.id == id) as VolSurfaceColumn ??
throw new InvalidOperationException($"Failed to find column '{id}'");
columns.FirstOrDefault(c => c.id == id) as VolSurfaceColumn;
internal void signal_selection_changed() {
var e = on_selection_changed;
@@ -221,23 +193,23 @@ namespace mbts.spark.VolSurface {
}
}
internal void copy_to_selection(
VolSurfaceDisplayGridExpiration exp,
VolSurfaceDisplayGridColumn column,
GridColumnSet set) =>
_replicate(exp, selected_rows.select_display_exp().Except(exp), _get_columns(column, set));
internal void copy_to_selection(VolSurfaceDisplayGridExpiration exp, VolSurfaceDisplayGridColumn column, GridColumnSet set) {
if (column.type == VolSurfaceColumnType.ReadWriteTick) {
_replicate(exp, selected_rows.select_display_exp().Except(exp), _get_columns(column, set));
}
}
internal void copy_to_masters(
VolSurfaceDisplayGridExpiration exp,
VolSurfaceDisplayGridColumn column,
GridColumnSet set) =>
_replicate(exp, _expirations.where_master().Except(exp), _get_columns(column, set));
internal void copy_to_masters(VolSurfaceDisplayGridExpiration exp, VolSurfaceDisplayGridColumn column, GridColumnSet set) {
if (column.type == VolSurfaceColumnType.ReadWriteTick) {
_replicate(exp, _expirations.where_master().Except(exp), _get_columns(column, set));
}
}
internal void copy_to_all(
VolSurfaceDisplayGridExpiration exp,
VolSurfaceDisplayGridColumn column,
GridColumnSet set) =>
_replicate(exp, _expirations.Except(exp), _get_columns(column, set));
internal void copy_to_all(VolSurfaceDisplayGridExpiration exp, VolSurfaceDisplayGridColumn column, GridColumnSet set) {
if (column.type == VolSurfaceColumnType.ReadWriteTick) {
_replicate(exp, _expirations.Except(exp), _get_columns(column, set));
}
}
static string _short_header(string header) =>
string.Join("", header.Where(Char.IsUpper));
@@ -246,7 +218,7 @@ namespace mbts.spark.VolSurface {
!_hidden_columns.Contains(column.unique_col_name);
void _on_grid_updated() {
using var _ = _invalidate_rows.defer();
using var _ = _invalidate_rows.defer_execution();
var grid_new = model.get_active_grid();
// add/remove columns
var cols_new = grid_new.get_ordered_columns()
@@ -271,7 +243,7 @@ namespace mbts.spark.VolSurface {
}
current_grid = grid_new;
committed_grid = is_dirty ? model.get_committed_grid() : current_grid;
_invalidate_rows.Invoke();
_invalidate_rows.execute();
}
void _launch_shift_editor(VolSurfaceDisplayGridColumn column, Action<double> commit_shift) {
@@ -327,7 +299,7 @@ namespace mbts.spark.VolSurface {
VolSurfaceDisplayGridExpiration source,
IEnumerable<VolSurfaceDisplayGridExpiration> target_exps,
IEnumerable<VolSurfaceDisplayGridColumn> cols) {
foreach (var col in cols.Where(c => c.type.is_editable())) {
foreach (var col in cols) {
foreach (var exp in target_exps) {
_copy(source, exp, col);
}
@@ -335,14 +307,7 @@ namespace mbts.spark.VolSurface {
_commit();
}
void _copy(
VolSurfaceDisplayGridExpiration source,
VolSurfaceDisplayGridExpiration dest,
VolSurfaceDisplayGridColumn col
) {
if (!col.type.is_editable()) {
throw new ArgumentException("Expected col to be editable", nameof(col));
}
void _copy(VolSurfaceDisplayGridExpiration source, VolSurfaceDisplayGridExpiration dest, VolSurfaceDisplayGridColumn col) {
if (current_grid.read(source, col, out var value)) {
current_grid.set_grid_data(dest, col.unique_col_name, value);
}
@@ -352,7 +317,7 @@ namespace mbts.spark.VolSurface {
model.set_candidate_grid(current_grid);
class ColumnsModel : IDisposable {
IDisposable? _dispose;
IDisposable _dispose;
public ColumnsModel(VolSurfaceColumn row_header_col) {
views_with_header_ro = ObservableAutoWrapper.create_read_only_wrapper(views_with_header);
@@ -0,0 +1,335 @@
namespace mbts.spark.VolSurface {
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Input;
using System.Windows.Media;
public struct VolSurfaceCellState {
public bool is_dirty => committed_text != null;
public FormattedText text;
public FormattedText committed_text;
}
class VolSurfaceRowContext : DisposableViewModelBase, ISelectable, IFormatCellsToClipboard, DIYGrid.IRowVM {
readonly VolSurfaceGridVM _parent;
readonly VolSurfaceDisplayGridExpiration _exp;
/// TODO: remove _text_cache when we are sure that the new row view
/// (<see cref="VolSurfaceRowView"/>) is solid. It must remain in place
/// until then, as it is used by old row view
/// (<see cref="VolSurfaceRowControl"/>).
readonly Dictionary<string, CellData> _cells = new();
MenuContext _context_menu;
bool _is_selected;
public VolSurfaceRowContext(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp) {
_parent = parent;
_exp = exp;
_dispose = Disposable.Create(() => {
_cells.Values.DisposeAll();
_cells.Clear();
});
}
public event Action<bool> is_selected_changed;
public event Action invalidated;
public VolSurfaceDisplayGridExpiration expiration => _exp;
public bool is_selected {
get => _is_selected;
set {
if (_is_selected != value) {
_is_selected = value;
is_selected_changed?.Invoke(value);
_parent.signal_selection_changed();
}
}
}
public MenuContext context_menu =>
_context_menu ??= new(_parent, this);
public VolGridCellVM get_cell(VolSurfaceDisplayGridColumn column) {
if (!_cells.TryGetValue(column.unique_col_name, out var cell_data)) {
_cells.Add(column.unique_col_name, cell_data = _create_cell(column));
}
return cell_data.vm;
}
public void invalidate() {
foreach (var cell in _cells.Values) {
_sync_cell(cell.vm);
}
raise_invalidated();
}
public override string ToString() =>
_exp.ToString();
public string[] get_cell_clipboard_strings() =>
new[] { _exp.ToString() }.Concat(
_parent.columns.OfType<VolSurfaceColumn>()
.Skip(1)
.Select(c => {
var val = _parent.current_grid.read(_exp, c.model.unique_col_name);
return c.model.format(val);
}))
.ToArray();
public void raise_invalidated() =>
invalidated?.Invoke();
CellData _create_cell(VolSurfaceDisplayGridColumn column) {
var cell = column.type switch {
VolSurfaceColumnType.ReadWriteTick => create_tick_cell(),
_ => new VolGridCellVM(column.unique_col_name),
};
_sync_cell(cell);
var dispose = cell.observe_candidate().DistinctUntilChanged().OnDispatcher().Subscribe(value => {
var curr = _parent.current_grid.read(expiration, column);
if (Equals(value, curr)) {
return;
}
var grid = _parent.current_grid;
grid.write(expiration, column, value);
_parent.model.set_candidate_grid(grid);
});
return new(cell, dispose);
VolGridTickCellVM create_tick_cell() {
var cell = new VolGridTickCellVM(column.unique_col_name);
cell.request_tick += is_up => {
var col = _parent.get_column(column.unique_col_name);
if (col is null) {
throw new InvalidOperationException($"Failed to find column with id {column.unique_col_name}");
}
if (col.model?.tick_move is var tm) {
cell.candidate_value = tm(cell.candidate_value, is_up);
return;
}
throw new InvalidOperationException($"tick_move is not defined");
};
return cell;
}
}
void _sync_cell(VolGridCellVM cell) {
if (cell is null) {
return;
}
cell.committed_value = _parent.committed_grid.read(_exp, cell.column_id);
cell.candidate_value = _parent.current_grid.read(_exp, cell.column_id);
}
public class MenuContext : ViewModelBase {
readonly VolSurfaceRowContext _row;
readonly VolSurfaceGridVM _parent;
VolSurfaceColumn _col;
DIYGridSuperHeaderVM _supercol;
ICommand _revert_command;
ICommand _shift_selection_command;
ICommand _shift_masters_command;
ICommand _shift_all_command;
ICommand _clear_command;
ICommand _copy_to_selection_command;
ICommand _copy_to_masters_command;
ICommand _copy_to_all_command;
ICommand _copy_all_to_clipboard_command;
ICommand _smart_copy_rolls_rates_command;
ICommand _import_csv_command;
public MenuContext(
VolSurfaceGridVM parent,
VolSurfaceRowContext row) {
_parent = parent;
_row = row;
}
public string column_name => _col.model.display_name;
public string group_name => _supercol.id;
public string expiration_name => _row.ToString();
public VolSurfaceColumn column {
get => _col;
set => Set(ref _col, value);
}
public DIYGridSuperHeaderVM super_header {
get => _supercol;
set => Set(ref _supercol, value);
}
public VolSurfaceDisplayGridExpiration expiration => _row.expiration;
public class SymbolFilter {
readonly MenuContext _context;
bool? _visible;
public SymbolFilter(MenuContext context, string symbol, int count, int visible_count) {
_context = context;
this.symbol = symbol;
if (count == visible_count) {
_visible = true;
} else if (visible_count == 0) {
_visible = false;
} else {
_visible = null;
}
}
public string symbol { get; private set; }
public bool? visible {
get => _visible;
set {
_visible = value;
if (value == true) {
_context._show_symbol(symbol);
} else if (value == false) {
_context._hide_symbol(symbol);
}
}
}
}
public IEnumerable<SymbolFilter> symbols {
get {
var visible_exps = _parent.visible_rows
.Cast<VolSurfaceRowContext>()
.GroupBy(r => r._exp.symbol)
.ToDictionary(g => g.Key);
var result = _parent.rows
.GroupBy(r => r._exp.symbol)
.OrderBy(g => g.Key)
.Select(g => new SymbolFilter(this, g.Key, g.Count(), visible_exps.ContainsKey(g.Key) ? visible_exps[g.Key].Count() : 0));
return result;
}
}
public bool is_input_cell => _col.type == VolSurfaceColumnType.ReadWriteTick;
public ICommand main_settings_command => _parent.main_settings_command;
public ICommand advanced_settings_command => _parent.advanced_settings_command;
public ICommand grid_settings_command => _parent.grid_settings_command;
public ICommand graph_settings_command => _parent.graph_settings_command;
public ICommand revert_command =>
_revert_command ??= new DelegateCommand(_ => _row.get_cell(_col.model)?.revert());
public ICommand shift_selection_command =>
_shift_selection_command ??= new DelegateCommand(() => {
if (_col.model.type == VolSurfaceColumnType.ReadWriteTick) {
_parent.shift_selection(_col.model);
}
}, () => _col.model.type == VolSurfaceColumnType.ReadWriteTick);
public ICommand shift_masters_command =>
_shift_masters_command ??= new DelegateCommand(() => {
if (_col.model.type == VolSurfaceColumnType.ReadWriteTick) {
_parent.shift_masters(_col.model);
}
}, () => _col.model.type == VolSurfaceColumnType.ReadWriteTick);
public ICommand shift_all_command =>
_shift_all_command ??= new DelegateCommand(() => {
if (_col.model.type == VolSurfaceColumnType.ReadWriteTick) {
_parent.shift_all(_col.model);
}
}, () => _col.model.type == VolSurfaceColumnType.ReadWriteTick);
public ICommand clear_command =>
_clear_command ??= new DelegateCommand<string>(where => {
if (Enum.TryParse(where, out GridColumnSet gc)) {
_parent.clear(_col.model, gc);
}
},
_has_editable_cells);
public ICommand copy_to_selection_command =>
_copy_to_selection_command ??= new DelegateCommand<string>(where => {
if (Enum.TryParse(where, out GridColumnSet gc)) {
_parent.copy_to_selection(expiration, _col.model, gc);
}
},
_has_editable_cells);
public ICommand copy_to_masters_command =>
_copy_to_masters_command ??= new DelegateCommand<string>(where => {
if (Enum.TryParse(where, out GridColumnSet gc)) {
_parent.copy_to_masters(expiration, _col.model, gc);
}
},
_has_editable_cells);
public ICommand copy_to_all_command =>
_copy_to_all_command ??= new DelegateCommand<string>(where => {
if (Enum.TryParse(where, out GridColumnSet gc)) {
_parent.copy_to_all(expiration, _col.model, gc);
}
},
_has_editable_cells);
public ICommand copy_all_to_clipboard_command =>
_copy_all_to_clipboard_command ??= new DelegateCommand(() => {
string clipboard_text = ClipboardHelper.GetExcelFormattedText(
_parent.visible_rows
.OfType<VolSurfaceRowContext>()
.Select(r => r.get_cell_clipboard_strings())
.Where(cells => cells != null));
if (!clipboard_text.is_null_or_empty()) {
ClipboardHelper.CopyTextToClipboard(clipboard_text);
}
});
public ICommand smart_copy_rolls_rates_command =>
_smart_copy_rolls_rates_command ??= new DelegateCommand(_ => _parent.model.sync_slave_ref_forwards(expiration.ToVSExp()));
public ICommand import_csv_command =>
_import_csv_command ??= FileDialogCommand.open_file(
_parent.model.import_csv_file,
() => _parent.model.csv_importing_enabled(),
null);
bool _has_editable_cells(string where) =>
!Enum.TryParse(where, out GridColumnSet gc) ||
gc == GridColumnSet.Column || _col.model.type != VolSurfaceColumnType.ReadOnly;
void _hide_symbol(string symbol) {
_parent.visible_rows.RemoveAll(s => ((VolSurfaceRowContext)s).expiration.symbol == symbol);
_parent.signal_selection_changed();
}
void _show_symbol(string symbol) {
// Insert items from rows to visible_Rows that have symbol=true in sorted order
// do this by making the desired collection and then leveraging sync_with
var visible = new HashSet<VolSurfaceDisplayGridExpiration>(_parent.visible_rows.Cast<VolSurfaceRowContext>().Select(row => row.expiration));
var desired = _parent.rows.Where(row => row.expiration.symbol == symbol || visible.Contains(row.expiration));
_parent.visible_rows.sync_with(desired);
}
}
class CellData : IDisposable {
IDisposable _dispose;
VolGridCellVM _vm;
public CellData(VolGridCellVM vm, IDisposable dispose) {
_vm = vm;
_dispose = dispose;
}
public VolGridCellVM vm => _vm;
public void Dispose() {
Disposable.dispose(ref _vm);
Disposable.dispose(ref _dispose);
}
}
}
}
@@ -1,262 +0,0 @@
#nullable enable
namespace mbts.spark {
using bts;
using bts.collections;
using mbts.spark.VolSurface;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Input;
class VolSurfaceRowContextMenuVM : DisposableViewModelBase {
readonly VolSurfaceRowVM _row;
readonly VolSurfaceGridVM _parent;
readonly Property<VolSurfaceColumn?> _column;
readonly Property<DIYGridSuperHeaderVM?> _super_header;
readonly UserTuple _user;
readonly Property<bool> _is_open;
readonly DelegateProperty<string> _vs_manager;
readonly List<IDisposable> _resources = new();
ICommand? _revert_cmd;
ICommand? _shift_selection_cmd;
ICommand? _shift_masters_cmd;
ICommand? _shift_all_cmd;
ICommand? _clear_cmd;
ICommand? _copy_to_selection_cmd;
ICommand? _copy_to_masters_cmd;
ICommand? _copy_to_all_cmd;
ICommand? _copy_all_to_clipboard_cmd;
ICommand? _smart_copy_rolls_rates_cmd;
ICommand? _import_csv_cmd;
ObservantCommand? _acquire_ownership_cmd;
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row, UserTuple user) {
_parent = parent;
_row = row;
_user = user;
_column = backing<VolSurfaceColumn?>(nameof(column));
_super_header = backing<DIYGridSuperHeaderVM?>(nameof(super_header));
_vs_manager = backing(
nameof(vs_manager),
() => _parent.model.get_active_settings()?.core_settings?.vs_manager ?? string.Empty);
_is_open = backing(nameof(is_open), false);
_resources.AddRange(
_column.invalidate_others_on_change(nameof(column_name)),
_super_header.invalidate_others_on_change(nameof(group_name)),
_is_open.Subscribe(_ => _vs_manager.refresh()),
_parent.visible_rows.ObserveSelf()
.BufferLatest(TimeSpan.FromSeconds(1))
.OnDispatcher()
.Subscribe(_ => sync_symbols()));
_dispose = Disposable.Create(() => _resources.DisposeAll(true));
void sync_symbols() {
var visible_exps = _parent.visible_rows.Map(r => r.expiration.symbol);
symbols.sync_with_models(
visible_exps,
kv => new SymbolItemVM(this, kv.Key),
(svm, kv) => {
svm.symbol = kv.Key;
svm.visible = SelectAllToggle.is_all_selected(
visible_exps[svm.symbol].Count(),
kv.Value.Count());
return svm;
});
}
}
public string column_name => column?.model?.display_name ?? string.Empty;
public string group_name => super_header?.id ?? string.Empty;
public string expiration_name => _row.ToString();
public VolSurfaceColumn? column {
get => _column.Value;
set => _column.set(value);
}
public DIYGridSuperHeaderVM? super_header {
get => _super_header.Value;
set => _super_header.set(value);
}
public VolSurfaceDisplayGridExpiration expiration => _row.expiration;
public ObservableCollection<SymbolItemVM> symbols { get; } = new();
public bool is_input_cell => column?.type == VolSurfaceColumnType.ReadWriteTick;
public ICommand populate_empty_exps_cmd => _parent.populate_empty_exps_cmd;
public ICommand main_settings_cmd => _parent.main_settings_cmd;
public ICommand advanced_settings_cmd => _parent.advanced_settings_cmd;
public ICommand grid_settings_cmd => _parent.grid_settings_cmd;
public ICommand graph_settings_cmd => _parent.graph_settings_cmd;
public ICommand revert_cmd =>
_revert_cmd ??= new Command(_ => {
_row.get_cell(_assert_model())?.revert();
});
public ICommand shift_selection_cmd =>
_shift_selection_cmd ??= new Command(() => {
if (_assert_model().type == VolSurfaceColumnType.ReadWriteTick) {
_parent.shift_selection(_assert_model());
}
}, () => _assert_model().type == VolSurfaceColumnType.ReadWriteTick);
public ICommand shift_masters_cmd =>
_shift_masters_cmd ??= new Command(() => {
if (_assert_model().type == VolSurfaceColumnType.ReadWriteTick) {
_parent.shift_masters(_assert_model());
}
}, () => _assert_model().type == VolSurfaceColumnType.ReadWriteTick);
public ICommand shift_all_cmd =>
_shift_all_cmd ??= new Command(() => {
if (_assert_model().type == VolSurfaceColumnType.ReadWriteTick) {
_parent.shift_all(_assert_model());
}
}, () => _assert_model().type == VolSurfaceColumnType.ReadWriteTick);
public ICommand clear_cmd =>
_clear_cmd ??= new Command<string>(where => {
if (Enum.TryParse(where, out GridColumnSet gc)) {
_parent.clear(_assert_model(), gc);
}
},
_has_editable_cells);
public ICommand copy_to_selection_cmd =>
_copy_to_selection_cmd ??= new Command<string>(where => {
if (Enum.TryParse(where, out GridColumnSet gc)) {
_parent.copy_to_selection(expiration, _assert_model(), gc);
}
});
public ICommand copy_to_masters_cmd =>
_copy_to_masters_cmd ??= new Command<string>(where => {
if (Enum.TryParse(where, out GridColumnSet gc)) {
_parent.copy_to_masters(expiration, _assert_model(), gc);
}
});
public ICommand copy_to_all_cmd =>
_copy_to_all_cmd ??= new Command<string>(where => {
if (Enum.TryParse(where, out GridColumnSet gc)) {
_parent.copy_to_all(expiration, _assert_model(), gc);
}
});
public ICommand copy_all_to_clipboard_cmd =>
_copy_all_to_clipboard_cmd ??= new Command(() => {
string clipboard_text = ClipboardHelper.GetExcelFormattedText(
_parent.visible_rows
.OfType<VolSurfaceRowVM>()
.Select(r => r.get_cell_clipboard_strings())
.Where(cells => cells != null));
if (!clipboard_text.is_null_or_empty()) {
ClipboardHelper.CopyTextToClipboard(clipboard_text);
}
});
public ICommand smart_copy_rolls_rates_cmd =>
_smart_copy_rolls_rates_cmd ??= new Command(
() => _parent.model.sync_slave_ref_forwards(expiration.ToVSExp()));
public ICommand import_csv_cmd =>
_import_csv_cmd ??= FileDialogCommand.open_file(
_parent.model.import_csv_file,
_parent.model.csv_importing_enabled,
null);
public ICommand acquire_ownership_cmd {
get {
if (_acquire_ownership_cmd is null) {
_acquire_ownership_cmd = new(
() => {
var settings = _parent.model.get_active_settings();
if (settings is null) {
Log.Ui.error("Unable to acquire volsurface ownership: settings are null.");
return;
}
settings.core_settings.vs_manager = _user.name;
_parent.model.set_candidate_settings(settings);
});
_resources.Add(_vs_manager.observe_current(true)
.Subscribe(SelectorObserver.builder(_acquire_ownership_cmd).wrap<string>(
curr => curr != _user.name)));
}
return _acquire_ownership_cmd;
}
}
public bool is_open {
get => _is_open.Value;
set => _is_open.set(value);
}
public string vs_manager => _vs_manager.Value;
VolSurfaceDisplayGridColumn _assert_model() =>
column?.model ?? throw new InvalidOperationException();
bool _has_editable_cells(string where) =>
!Enum.TryParse(where, out GridColumnSet gc) ||
gc == GridColumnSet.Column || _assert_model().type != VolSurfaceColumnType.ReadOnly;
void _hide_symbol(string symbol) {
_parent.visible_rows.RemoveAll(s => s.expiration.symbol == symbol);
_parent.signal_selection_changed();
}
void _show_symbol(string symbol) {
// Insert items from rows to visible_Rows that have symbol=true in sorted order
// do this by making the desired collection and then leveraging sync_with
var visible = _parent.visible_rows
.Select(row => row.expiration)
.ToHashSet();
var desired = _parent.rows.Where(
row => row.expiration.symbol == symbol || visible.Contains(row.expiration));
_parent.visible_rows.sync_with(desired);
}
public class SymbolItemVM : ViewModelBase {
readonly VolSurfaceRowContextMenuVM _context;
string _symbol;
bool? _visible;
public SymbolItemVM(
VolSurfaceRowContextMenuVM context, string symbol) {
_context = context;
_symbol = symbol;
}
public string symbol {
get => _symbol;
set => Set(ref _symbol, value);
}
public bool? visible {
get => _visible;
set {
if (Set(ref _visible, value)) {
if (value is true) {
_context._show_symbol(symbol);
} else if (value is false) {
_context._hide_symbol(symbol);
}
}
}
}
}
}
}
@@ -0,0 +1,372 @@
// using mbts;
// using mbts.ui;
// using mbts.ui.controls;
// using mbts.ui.dpi;
// using mbts.ui.utility.text;
// using System;
// using System.Collections.Generic;
// using System.Diagnostics;
// using System.Globalization;
// using System.Linq;
// using System.Text;
// using System.Threading.Tasks;
// using System.Windows;
// using System.Windows.Controls;
// using System.Windows.Input;
// using System.Windows.Media;
// namespace mbts.spark.VolSurface {
// class VolSurfaceRowControl : DIYGrid.HighPerformanceRow<VolSurfaceRowContext> {
// FormattedText _exp_text;
// readonly double _padding = 5.0;
// readonly Pen _dirty_pen = new Pen(Brushes.Red, 1.0);
// readonly Pen _selection_pen = new Pen(Brushes.Black, 1.0) { DashStyle = DashStyles.Dot };
// TickUpDown _cell_editor_backing;
// TickUpDown _cell_editor => _cell_editor_backing ??= new ();
// int? _selected_cell;
// public VolSurfaceRowControl() {
// Loaded += (s, e) => ContextMenu = TryFindResource("context_menu") as ContextMenu;
// GotKeyboardFocus += (s, e) => {
// if (e.OriginalSource is not FrameworkElement el) {
// return;
// }
// var cell_tl = el.TranslatePoint(new Point(0, 0), this);
// _on_focus_move(DIYGrid.RowUtils.column_at(_column_layout, cell_tl));
// };
// }
// int? _column_index(DIYGrid.ColumnGeometry col) =>
// _column_layout.TryGetFirst(c => c.Equals(col), out var _, out var idx) ? idx : null;
// //private static int _render_count = 0;
// protected override void OnRender(DrawingContext dc) {
// // Fill with transparent for hit testing
// dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, new Rect(0, 0, _column_layout.Sum(c => c.width), RenderSize.Height));
// string last_group = "";
// foreach (var cell in _column_layout) {
// // Render collapsed columns
// if (cell.column.is_collapsed) {
// dc.DrawRectangle(BTSColors.AW2.collapsed, null, new Rect(cell.left, 0, cell.width, Height));
// continue;
// }
// var column = (VolSurfaceSimplifiedColumn)cell.column;
// VolSurfaceCellState celldata;
// // Get the cell text
// if (column.type == VolSurfaceSimplifiedColumnType.RowHeader) {
// // Fill in this one
// dc.DrawRectangle(BTSColors.header, null, new Rect(cell.left, 0, cell.width, Height));
// var exp = _row.expiration;
// if (_exp_text == null) {
// _exp_text = format_text(exp.ToString(), exp.is_slave ? Brushes.LightGray : Brushes.White);
// }
// celldata = new VolSurfaceCellState() {
// text = _exp_text
// };
// } else if (column.type == VolSurfaceSimplifiedColumnType.ReadOnly) {
// celldata = _row.get_value(column.volsurface_column, format(Brushes.Blue), _ => null);
// } else {
// celldata = _row.get_value(column.volsurface_column, format(Brushes.Black), format(Brushes.DarkGray));
// }
// // If the cell is partly hidden clip the draw
// if (cell.is_partially_obscured) {
// dc.PushClip(new RectangleGeometry(
// new Rect(cell.left, 0, cell.width, Height)));
// }
// // draw text centered vertially
// double total_width = _padding + celldata.text.Width;
// double pad_top = (Height - celldata.text.Height) / 2.0;
// dc.DrawText(celldata.text, new Point(cell.unclipped_left + _padding, pad_top));
// if (celldata.committed_text != null) {
// pad_top = (Height - celldata.committed_text.Height) / 2.0;
// dc.DrawText(celldata.committed_text, new Point(cell.unclipped_left + total_width, pad_top));
// total_width += celldata.committed_text.Width;
// }
// // draw grid lines
// double line_width; Brush fill;
// if (last_group == cell.group) { line_width = 1; fill = _grid.VerticalGridLineBrush; } else { line_width = 2; fill = _grid.VerticalGroupSeparatorBrush; }
// dc.DrawRectangle(fill, null, new Rect(cell.left - line_width, 0, line_width, Height));
// Rect cell_border_rect = new Rect(cell.unclipped_left + 1, 0, Math.Max(0, cell.width - (line_width * 2)), Height - 1);
// // draw dirty rect
// if (celldata.is_dirty) {
// dc.DrawRectangle(null, _dirty_pen, cell_border_rect);
// }
// // Draw selection
// if (_selected_cell == _column_index(cell) && column.type != VolSurfaceSimplifiedColumnType.ReadWriteTick) {
// dc.DrawRectangle(null, _selection_pen, cell_border_rect);
// }
// if (cell.is_partially_obscured) { dc.Pop(); }
// total_width += _padding;//right padding
// if (!celldata.is_dirty && column.type == VolSurfaceSimplifiedColumnType.ReadWriteTick) total_width += 22; // size of buttons on a tickupdown
// if (total_width > cell.width) {
// Dispatcher.BeginInvoke(((Action)(() => cell.column.Width = total_width)));
// }
// last_group = cell.group;
// }
// if (_selected_cell != NO_CELL) {
// if (_cell_editor.Visibility == Visibility.Visible) {
// var position = _column_geometry[_selected_cell];
// PositionControlOver(tud, position);
// }
// }
// //++_render_count;
// //dc.DrawText(
// // format_text(_render_count.ToString(), Brushes.Black),
// // new Point(0, 0));
// }
// Func<string, FormattedText> format(Brush brush) =>
// str => format_text(str, brush);
// void _on_focus_move(DIYGrid.ColumnGeometry? new_column) {
// if (new_column != null) {
// enable_edit(new_column.Value);
// }
// InvalidateVisual();
// }
// private async void enable_edit(DIYGrid.ColumnGeometry column) {
// await Task.Yield();
// var col = (VolSurfaceSimplifiedColumn)column.column;
// var tud = _grid.get_edit_control<TickUpDown>();
// if (col.type == VolSurfaceSimplifiedColumnType.ReadWriteTick) {
// tud.DataContext = _row.get_edit_context(col.volsurface_column);
// PositionControlOver(tud, column);
// var fe = tud.Content as TextBox;
// if (fe != null) {
// await Task.Yield();
// fe.Focus();
// fe.SelectAll();
// }
// } else {
// tud.DataContext = null;
// tud.Visibility = Visibility.Collapsed;
// var item = this.find_ancestor_of_type<ListBoxItem>();
// if (item != null) {
// item.Focus();
// }
// }
// }
// protected override void OnMouseRightButtonUp(MouseButtonEventArgs e) {
// // _grid.column
// var cell = DIYGrid.RowUtils.column_at(_column_layout, e.GetPosition(this));
// if (ContextMenu != null) {
// if (cell.HasValue && DIYGrid.RowUtils.column_index(_column_layout, cell.Value) > (_grid.FrozenColumnCount - 1)) {
// ContextMenu.DataContext = _row.get_context_menu_context((VolSurfaceSimplifiedColumn)cell.Value.column, cell.Value.super_column);
// } else {
// e.Handled = true;
// ContextMenu.DataContext = null;
// }
// }
// base.OnMouseRightButtonDown(e);
// }
// FormattedText format_text(string content, Brush fg) {
// var ft = new FormattedText(content, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
// TextUtils.StandardTypeface, FontSizes.Regular, Brushes.Black, HighDpiExtensions.dpi_ratio);
// return ft;
// }
// }
// }
// using mbts;
// using mbts.ui;
// using mbts.ui.controls;
// using mbts.ui.dpi;
// using mbts.ui.utility.text;
// using System;
// using System.Collections.Generic;
// using System.Diagnostics;
// using System.Globalization;
// using System.Linq;
// using System.Text;
// using System.Threading.Tasks;
// using System.Windows;
// using System.Windows.Controls;
// using System.Windows.Input;
// using System.Windows.Media;
// namespace mbts.spark.VolSurface {
// class VolSurfaceRowControl : DIYGrid.RowBase {
// VolSurfaceRowContext _row;
// FormattedText _exp_text;
// readonly double _padding = 5.0;
// readonly Pen _dirty_pen = new Pen(Brushes.Red, 1.0);
// readonly Pen _selection_pen = new Pen(Brushes.Black, 1.0) { DashStyle = DashStyles.Dot };
// public VolSurfaceRowControl() {
// DataContextChanged += OnDataContextChanged;
// Loaded += OnLoaded;
// }
// #region SelectedCell
// public static readonly DependencyProperty SelectedCellProperty =
// WPFHelper.create_dp<object, VolSurfaceRowControl>(nameof(SelectedCell));
// public object SelectedCell {
// get => GetValue(SelectedCellProperty);
// set => SetValue(SelectedCellProperty, value);
// }
// #endregion
// void OnLoaded(object sender, RoutedEventArgs e) {
// ContextMenu = TryFindResource("context_menu") as ContextMenu;
// }
// void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) {
// var oldrow = e.OldValue as VolSurfaceRowContext;
// if (oldrow != null) {
// oldrow.on_invalidate -= InvalidateVisual;
// }
// _row = DataContext as VolSurfaceRowContext;
// _row.on_invalidate += InvalidateVisual;
// }
// //private static int _render_count = 0;
// protected override void OnRender(DrawingContext dc) {
// if (_grid == null) return;
// // Fill with transparent for hit testing
// dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, new Rect(0, 0, _column_geometry.Sum(g => g.width), RenderSize.Height));
// string last_group = "";
// foreach (var cell in _column_geometry) {
// // Render collapsed columns
// if (cell.column.is_collapsed) {
// dc.DrawRectangle(BTSColors.AW2.collapsed, null, new Rect(cell.left, 0, cell.width, Height));
// continue;
// }
// var column = (VolSurfaceSimplifiedColumn)cell.column;
// VolSurfaceCellState celldata;
// // Get the cell text
// if (column.type == VolSurfaceSimplifiedColumnType.RowHeader) {
// // Fill in this one
// dc.DrawRectangle(BTSColors.header, null, new Rect(cell.left, 0, cell.width, Height));
// var exp = _row.expiration;
// if (_exp_text == null) {
// _exp_text = format_text(exp.ToString(), exp.is_slave ? Brushes.LightGray : Brushes.White);
// }
// celldata = new VolSurfaceCellState() { text = _exp_text };
// } else if (column.type == VolSurfaceSimplifiedColumnType.ReadOnly) {
// celldata = _row.get_value(column.volsurface_column, format(Brushes.Blue), _ => null);
// } else {
// celldata = _row.get_value(column.volsurface_column, format(Brushes.Black), format(Brushes.DarkGray));
// }
// // If the cell is partly hidden clip the draw
// if (cell.is_partially_obscured) {
// dc.PushClip(new RectangleGeometry(
// new Rect(cell.left, 0, cell.width, Height)));
// }
// // draw text centered vertially
// double total_width = _padding + celldata.text.Width;
// double pad_top = (Height - celldata.text.Height) / 2.0;
// dc.DrawText(celldata.text, new Point(cell.unclipped_left + _padding, pad_top));
// if (celldata.committed_text != null) {
// pad_top = (Height - celldata.committed_text.Height) / 2.0;
// dc.DrawText(celldata.committed_text, new Point(cell.unclipped_left + total_width, pad_top));
// total_width += celldata.committed_text.Width;
// }
// // draw grid lines
// double line_width; Brush fill;
// if (last_group == cell.group) { line_width = 1; fill = _grid.VerticalGridLineBrush; } else { line_width = 2; fill = _grid.VerticalGroupSeparatorBrush; }
// dc.DrawRectangle(fill, null, new Rect(cell.left - line_width, 0, line_width, Height));
// Rect cell_border_rect = new Rect(cell.unclipped_left + 1, 0, Math.Max(0, cell.width - (line_width * 2)), Height - 1);
// // draw dirty rect
// if (celldata.is_dirty) {
// dc.DrawRectangle(null, _dirty_pen, cell_border_rect);
// }
// // Draw selection
// if (_selected_cell == column_index(cell) && column.type != VolSurfaceSimplifiedColumnType.ReadWriteTick) {
// dc.DrawRectangle(null, _selection_pen, cell_border_rect);
// }
// if (cell.is_partially_obscured) { dc.Pop(); }
// total_width += _padding;//right padding
// if (!celldata.is_dirty && column.type == VolSurfaceSimplifiedColumnType.ReadWriteTick) total_width += 22; // size of buttons on a tickupdown
// if (total_width > cell.width) {
// Dispatcher.BeginInvoke(((Action)(() => cell.column.Width = total_width)));
// }
// last_group = cell.group;
// }
// //++_render_count;
// //dc.DrawText(
// // format_text(_render_count.ToString(), Brushes.Black),
// // new Point(0, 0));
// }
// private Func<string, FormattedText> format(Brush brush) {
// return str => format_text(str, brush);
// }
// protected override void OnClick(DIYGrid.ColumnGeometry column) {
// }
// protected override void OnFocusMove(DIYGrid.ColumnGeometry? new_column) {
// if (new_column != null) enable_edit(new_column.Value);
// InvalidateVisual();
// }
// private async void enable_edit(DIYGrid.ColumnGeometry column) {
// await Task.Yield();
// var col = (VolSurfaceSimplifiedColumn)column.column;
// var tud = _grid.get_edit_control<TickUpDown>();
// if (col.type == VolSurfaceSimplifiedColumnType.ReadWriteTick) {
// tud.DataContext = _row.get_edit_context(col.volsurface_column);
// PositionControlOver(tud, column);
// var fe = tud.Content as TextBox;
// if (fe != null) {
// await Task.Yield();
// fe.Focus();
// fe.SelectAll();
// }
// } else {
// tud.DataContext = null;
// tud.Visibility = Visibility.Collapsed;
// var item = this.find_ancestor_of_type<ListBoxItem>();
// if (item != null) {
// item.Focus();
// }
// }
// }
// protected override void OnMouseRightButtonUp(MouseButtonEventArgs e) {
// var cell = column_at(e.GetPosition(this));
// if (ContextMenu != null) {
// if (cell.HasValue && column_index(cell.Value) > (_grid.FrozenColumnCount - 1)) {
// ContextMenu.DataContext = _row.get_context_menu_context((VolSurfaceSimplifiedColumn)cell.Value.column, cell.Value.super_column);
// } else {
// e.Handled = true;
// ContextMenu.DataContext = null;
// }
// }
// base.OnMouseRightButtonDown(e);
// }
// FormattedText format_text(string content, Brush fg) {
// var ft = new FormattedText(content, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
// TextUtils.StandardTypeface, FontSizes.Regular, Brushes.Black, HighDpiExtensions.dpi_ratio);
// return ft;
// }
// }
// }
@@ -1,153 +0,0 @@
#nullable enable
namespace mbts.spark.VolSurface {
using bts;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using Spark.Core;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
class VolSurfaceRowVM : DisposableViewModelBase, ISelectable, IFormatCellsToClipboard, DIYGrid.IRowVM {
readonly VolSurfaceGridVM _parent;
readonly VolSurfaceDisplayGridExpiration _exp;
readonly Dictionary<string, CellData> _cells = new();
readonly UserTuple _user;
readonly DeferrableAction _request_calculate;
VolSurfaceRowContextMenuVM? _context_menu;
bool _is_selected;
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp, UserTuple user) {
_parent = parent;
_exp = exp;
_user = user;
_request_calculate = new(() => request_calculate?.Invoke());
_dispose = Disposable.Create(() => {
_cells.Values.DisposeAll();
_cells.Clear();
_context_menu?.Dispose();
});
}
public event Action<bool>? is_selected_changed;
public event Action? invalidated;
public event Action? request_calculate;
public VolSurfaceDisplayGridExpiration expiration => _exp;
public bool is_selected {
get => _is_selected;
set {
if (Set(ref _is_selected, value)) {
is_selected_changed?.Invoke(value);
_parent.signal_selection_changed();
}
}
}
public VolSurfaceRowContextMenuVM context_menu =>
_context_menu ??= new(_parent, this, _user);
public VolSurfaceCellVM get_cell(VolSurfaceDisplayGridColumn column) =>
_cells.GetOrAdd(column.unique_col_name, _ => _create_cell(column)).vm;
public void invalidate() {
using (_request_calculate.supress()) {
foreach (var cell in _cells.Values) {
_sync_cell(cell.vm);
}
}
invalidated?.Invoke();
}
public string[] get_cell_clipboard_strings() =>
new[] { _exp.ToString() }.Concat(
_parent.columns.OfType<VolSurfaceColumn>().Skip(1).Select(c => {
var val = _parent.current_grid.read(_exp, c.model.unique_col_name);
return c.model.format(val);
}))
.ToArray();
public override string ToString() =>
_exp.ToString();
void DIYGrid.IRowVM.raise_invalidated() =>
invalidated?.Invoke();
CellData _create_cell(VolSurfaceDisplayGridColumn column) {
var cell = column.type switch {
VolSurfaceColumnType.ReadWriteTick => create_tick_cell(),
_ => new VolSurfaceCellVM(_exp, column)
};
_sync_cell(cell);
return new(cell, _connect_cell(cell));
VolSurfaceCellVM create_tick_cell() {
var cell = new VolSurfaceTickCellVM(_exp, column);
cell.request_tick += is_up => {
var col = _parent.get_column(column.unique_col_name);
if (col.model?.tick_move is var tm) {
cell.candidate_value = tm?.Invoke(cell.candidate_value, is_up);
return;
}
throw new InvalidOperationException($"tick_move is not defined");
};
return cell;
}
}
void _sync_cell(VolSurfaceCellVM cell) {
if (cell is null) {
return;
}
using var _ = _request_calculate.supress();
cell.reset(
committed_value: _parent.committed_grid.read(_exp, cell.column_id),
candidate_value: _parent.current_grid.read(_exp, cell.column_id));
}
IDisposable? _connect_cell(VolSurfaceCellVM cell) {
return cell.column.type switch {
VolSurfaceColumnType.ReadWriteTick => connect_input(),
_ => null
};
IDisposable connect_input() {
cell.candidate_value_changed += on_candidate_value_changed;
return Disposable.Create(() =>
cell.candidate_value_changed -= on_candidate_value_changed);
void on_candidate_value_changed(object? oldval, object? newval) {
var curr = _parent.current_grid.read(expiration, cell.column);
if (cell.column.comparer.Equals(newval, curr)) {
return;
}
var grid = _parent.current_grid;
grid.write(expiration, cell.column, newval);
_request_calculate.Invoke();
}
}
}
class CellData : IDisposable {
IDisposable? _dispose;
public CellData(VolSurfaceCellVM vm, IDisposable? dispose) {
this.vm = vm;
_dispose = Disposable.Create(vm, dispose);
}
public VolSurfaceCellVM vm { get; }
public void Dispose() =>
Disposable.dispose(ref _dispose);
}
}
}
@@ -1,27 +0,0 @@
#nullable enable
namespace mbts.spark {
using mbts.ui;
using mbts.ui.controls;
using System;
using System.Windows.Input;
class VolSurfaceTickCellVM : VolSurfaceCellVM {
ICommand? _tick_up_cmd;
ICommand? _tick_down_cmd;
public VolSurfaceTickCellVM(
VolSurfaceDisplayGridExpiration expiration, VolSurfaceDisplayGridColumn column)
: base(expiration, column) { }
public event Action<bool>? request_tick;
public ICommand tick_up_cmd =>
_tick_up_cmd ??= new DelegateCommand(() =>
request_tick?.Invoke(true));
public ICommand tick_down_cmd =>
_tick_down_cmd ??= new DelegateCommand(() =>
request_tick?.Invoke(false));
}
}
@@ -3,8 +3,6 @@ using mbts.ui;
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +12,7 @@ namespace mbts.spark {
class VolSurfaceAdvancedSettingsEditorVM : ViewModelBase {
readonly VolSurfaceAdvancedSettings _settings;
public VolSurfaceAdvancedSettingsEditorVM(VolSurfaceAdvancedSettings settings, string vs_name) {
public VolSurfaceAdvancedSettingsEditorVM(VolSurfaceAdvancedSettings settings, string vs_name, Action<VolSurfaceAdvancedSettings> commit) {
_settings = settings;
_settings.theo_handle_measure = VolSurfaceEnums.GeneralChangeMeasure.Absolute;
_settings.slide_handle_measure = VolSurfaceEnums.GeneralChangeMeasure.Percentage;
@@ -23,7 +21,7 @@ namespace mbts.spark {
if (_settings.precomputed_theo_handles == null)
_settings.precomputed_theo_handles = new List<double>();
vol_surface_name = vs_name;
commit = new DelegateCommand(() => request_commit?.Invoke(ProtobufSerializationHelper.clone(_settings)));
this.commit = new DelegateCommand(() => request_commit?.Invoke(ProtoBufSerializationHelper.clone_protobuf(_settings)));
}
public event Action<VolSurfaceAdvancedSettings> request_commit;
@@ -1,89 +0,0 @@
#nullable enable
namespace mbts.spark {
using bts.api.domain;
using mbts.analytics;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using VolCurveFitRegion = analytics.VolSurfaceEnums.VolCurveFitRegion;
public class VolSurfaceAutoFitSettingsVM : DisposableViewModelBase {
readonly Property<VolCurveFitRegion> _fit_region;
readonly Property<uint?> _period_secs;
readonly Property<double?> _driver_points_moved;
readonly Property<double?> _atm_points_moved;
readonly Property<bool> _is_auto_fit_enabled;
public VolSurfaceAutoFitSettingsVM() {
_fit_region = backing(nameof(fit_region), VolCurveFitRegion.None);
_is_auto_fit_enabled = backing(
nameof(is_auto_fit_enabled),
_fit_region.observe_current(true),
r => r != VolCurveFitRegion.None);
_period_secs = backing<uint?>(nameof(period_secs), 10);
_driver_points_moved = backing<double?>(nameof(driver_points_moved));
_atm_points_moved = backing<double?>(nameof(atm_points_moved));
_fit_region.Changed += (o, n) => RaisePropertyChanged(nameof(is_auto_fit_enabled));
}
public IEnumerable<VolCurveFitRegion> fit_regions =>
EnumHelper.get_values<VolCurveFitRegion>();
public VolCurveFitRegion fit_region {
get => _fit_region.Value;
set => _fit_region.set(value);
}
public bool is_auto_fit_enabled => _is_auto_fit_enabled.Value;
public uint? period_secs {
get => _period_secs.Value;
set => _period_secs.set(value);
}
public double? driver_points_moved {
get => _driver_points_moved.Value;
set => _driver_points_moved.set(value);
}
public TickDisplaySettingsImmutable? driver_points_display =>
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
public double? atm_points_moved {
get => _atm_points_moved.Value;
set => _atm_points_moved.set(value);
}
public TickDisplaySettingsImmutable? atm_points_display =>
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
public void load_from(VolSurfaceAutoFitSettings value) {
fit_region = value?.auto_fit_region ?? VolCurveFitRegion.None;
period_secs = value?.time_based_settings?.period_seconds;
driver_points_moved = value?.driver_move_based_settings?.points_moved;
atm_points_moved = value?.atm_implied_vol_based_settings?.points_moved;
}
public void assign_to(VolSurfaceAutoFitSettings value) {
if (value is null) {
throw new ArgumentNullException(nameof(value));
}
value.auto_fit_region = fit_region;
value.time_based_settings = new() {
period_seconds = period_secs
};
value.driver_move_based_settings = new() {
points_moved = driver_points_moved
};
value.atm_implied_vol_based_settings = new() {
points_moved = atm_points_moved
};
}
}
}
@@ -96,7 +96,7 @@ namespace mbts.spark {
get => _core_settings.font_weight_override;
set {
using var _ = detect_property_change();
_core_settings.with(font_weight_override: value.HasValue ? new(value) : null);
_core_settings.with(font_weight_override: value.HasValue ? new NullableFontWeightProto(value) : null);
}
}
@@ -0,0 +1,72 @@
using mbts.ui;
using mbts.ui.controls.graph;
using System;
using System.Windows.Input;
using System.Windows.Media;
namespace mbts.spark
{
class VolSurfaceGraphSettingsEditorVM
{
public VolSurfaceGraphSettingsEditorVM(string vol_surface_name, VolSurfaceGraphSettings settings, Action<VolSurfaceGraphSettings> commit) {
this.vol_surface_name = vol_surface_name;
_settings = settings;
this.commit = new DelegateCommand(() => commit(_settings));
}
private readonly VolSurfaceGraphSettings _settings;
public string vol_surface_name { get; private set; }
public VolSurfaceScatterSeriesSettingsVM implied_puts { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.implied_puts); } }
public VolSurfaceScatterSeriesSettingsVM implied_atm { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.implied_atm); } }
public VolSurfaceScatterSeriesSettingsVM implied_calls { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.implied_calls); } }
public VolSurfaceScatterSeriesSettingsVM put_control_points { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.put_control_points); } }
public VolSurfaceScatterSeriesSettingsVM atm { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.atm); } }
public VolSurfaceScatterSeriesSettingsVM call_control_points { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.call_control_points); } }
public VolSurfaceLineSeriesSettingsVM put_wing { get { return new VolSurfaceLineSeriesSettingsVM(_settings.put_wing); } }
public VolSurfaceLineSeriesSettingsVM inner_curve { get { return new VolSurfaceLineSeriesSettingsVM(_settings.inner_curve); } }
public VolSurfaceLineSeriesSettingsVM call_wing { get { return new VolSurfaceLineSeriesSettingsVM(_settings.call_wing); } }
public VolSurfaceLineSeriesSettingsVM vol_path { get { return new VolSurfaceLineSeriesSettingsVM(_settings.vol_path); } }
public ICommand commit { get; private set; }
}
class VolSurfaceScatterSeriesSettingsVM
{
public VolSurfaceScatterSeriesSettingsVM(ScatterSeriesSettings settings) {
_settings = settings;
}
private readonly ScatterSeriesSettings _settings;
public MarkerShape shape { get { return _settings.shape; } set { _settings.shape = value; } }
public double size { get { return _settings.size; } set { _settings.size = value; } }
public Color fill {
get => _settings.fill;
set => _settings.fill = value;
}
public Color stroke {
get => _settings.stroke;
set => _settings.stroke = value;
}
public Array shapes { get { return Enum.GetValues(typeof(MarkerShape)); } }
}
class VolSurfaceLineSeriesSettingsVM
{
public VolSurfaceLineSeriesSettingsVM(LineSeriesSettings settings) {
_settings = settings;
}
private readonly LineSeriesSettings _settings;
public Color color { get { return (Color)_settings.color; } set { _settings.color = (BTSColor)value; } }
public double thickness { get { return _settings.stroke_thickness; } set { _settings.stroke_thickness = value; } }
}
}
@@ -9,9 +9,6 @@ namespace mbts.spark {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core.Disposables;
using Spark.Core.Protobuf;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -26,9 +23,6 @@ namespace mbts.spark {
readonly VolSurfaceSettings _settings;
readonly VolSurfaceCoreSettings _core_settings;
string _selected_section;
bool _can_edit_name = true;
public VolSurfaceSettingsEditorVM(IBus bus, VolSurfaceSettings settings, Action<VolSurfaceSettings> commit) {
if (settings is null) {
throw new ArgumentNullException(nameof(settings));
@@ -40,7 +34,6 @@ namespace mbts.spark {
vol_model_cubic_spline_vm.load_from(settings.spline_model_settings);
vol_model_dual_quadratic_vm.load_from(settings.dualquadratic_model_settings);
vol_path_parabolic_vm.load_from(settings.core_settings.volpath_settings.parabolic_vol_path_settings);
auto_fit_vm.load_from(settings.core_settings.market_fit_settings.vs_auto_fit_settings);
init_rotation_vms(call_rotations, _core_settings.call_rotation.strike_vol_movement_pairs);
init_rotation_vms(put_rotations, _core_settings.put_rotation.strike_vol_movement_pairs);
@@ -106,7 +99,6 @@ namespace mbts.spark {
}).ToList();
_core_settings.strangle.strike_vol_movement_pairs = strangles.SelectMany(r => new[] { Tuple.Create(r.target_put_strike, r.put_move), Tuple.Create(r.target_call_strike, r.call_move) }).ToList();
_core_settings.atm_shift.atm_strike_vol_movement = atm_shifts.Count == 1 ? atm_shifts[0].move : 0;
auto_fit_vm.assign_to(_core_settings.market_fit_settings.vs_auto_fit_settings);
commit(_settings);
}
@@ -121,23 +113,7 @@ namespace mbts.spark {
}
}
public string vol_surface_name {
get => _core_settings.vol_surface_name;
set {
using var _ = detect_property_change();
_core_settings.vol_surface_name = value;
}
}
public bool can_edit_name {
get => _can_edit_name;
set => Set(ref _can_edit_name, value);
}
public string selected_section {
get => _selected_section;
set => Set(ref _selected_section, value);
}
public string vol_surface_name { get { return _core_settings.vol_surface_name; } }
public VSSymbolStructureVM symbol_structure_vm { get; set; }
public VolModelPolynomialVM vol_model_polynomial_vm { get; } = new();
@@ -235,7 +211,23 @@ namespace mbts.spark {
}
}
public VolSurfaceAutoFitSettingsVM auto_fit_vm { get; } = new();
public bool enable_auto_fit {
get => _core_settings.market_fit_settings.enable_auto_fit;
set {
using var _ = detect_property_change();
_core_settings.market_fit_settings.enable_auto_fit = value;
}
}
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region {
get => _core_settings.market_fit_settings.auto_fit_region;
set { _core_settings.market_fit_settings.auto_fit_region = value; }
}
public double auto_fit_time_ms {
get => _core_settings.market_fit_settings.auto_fit_time_ms;
set { _core_settings.market_fit_settings.auto_fit_time_ms = value; }
}
public bool auto_decay_vol_time {
get => _core_settings.time_settings.time_mode == VolSurfaceEnums.VolSurfaceTimeMode.AutoDecay;
@@ -361,7 +353,7 @@ namespace mbts.spark {
public ICommand commit { get; }
public VolSurfaceSettings get_settings() {
var rv = ProtobufSerializationHelper.clone(_settings);
var rv = ProtoBufSerializationHelper.clone_protobuf(_settings);
var symbols = symbol_structure_vm.driver_grids
.SelectMany(dg => dg.symbols.Where(sym => sym.is_selected))
.ToList();
@@ -526,22 +518,17 @@ namespace mbts.spark {
public ObservableCollection<VSSymbol> symbols { get; private set; }
public void Dispose() =>
symbols.DisposeAll(true);
Disposable.dispose(symbols);
}
public class VSSymbolStructureVM : DisposableViewModelBase {
readonly Property<VSSymbolGridVM> _selected_driver_grid;
public VSSymbolStructureVM(IBus bus, VolSurfaceSettings settings) {
driver_grids = new ObservableCollection<VSSymbolGridVM>();
_selected_driver_grid = backing<VSSymbolGridVM>(nameof(selected_driver_grid));
settings.core_settings.master_slave_relationships ??= new List<VolSurfaceSymbolRelationship>();
settings.core_settings.implied_vol_marketdata_relationships ??= new List<VolSurfaceSymbolRelationship>();
_dispose = Disposable.Create(
driver_grids.Subscribe((s, e) =>
selected_driver_grid ??= driver_grids.FirstOrDefault()),
bus.subscribe(SparkTags.ExpirationParametersInputTag).OnDispatcher().Subscribe(on_exp_params_input),
Disposable.Create(() => driver_grids.DisposeAll(true)));
_dispose = new DisposableCombo(
bus.subscribe(SparkTags.ExpirationParametersInputTag).OnDispatcher().Subscribe(on_exp_params_input)) +
new Action(() => Disposable.dispose(driver_grids));
void on_exp_params_input(AnalyticsExpirationParametersMap data) {
var driver_map = data.get_option_driver_map();
@@ -599,10 +586,6 @@ namespace mbts.spark {
public IReadOnlyList<VolSurfaceEnums.VolSurfacePricingModel> pricing_models => EnumHelper.get_values<VolSurfaceEnums.VolSurfacePricingModel>();
public ObservableCollection<VSSymbolGridVM> driver_grids { get; private set; }
public VSSymbolGridVM selected_driver_grid {
get => _selected_driver_grid.Value;
set => _selected_driver_grid.set(value);
}
}
public class RotationVM : ViewModelBase, IEquatable<RotationVM> {
@@ -1,15 +1,14 @@
namespace mbts.spark {
using bts;
using bts.utils;
using mbts.ui;
using mbts.ui.shell;
using Spark.Core.Reflection;
using Spark.ViewModel.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
class VolSurfaceMacroActionsVM : ViewModelBase {
readonly VolSurfaceDisplayModel _model;
@@ -77,8 +76,8 @@
public IRaiseCanExecuteChangedCommand vol_shift { get; }
IEnumerable<IRaiseCanExecuteChangedCommand> _commands =>
ReflectionHelper.property_getters<VolSurfaceMacroActionsVM, IRaiseCanExecuteChangedCommand>()
.Select(pg => pg.get(this));
ObjectSnapshot.property_delegates_of_type<VolSurfaceMacroActionsVM, IRaiseCanExecuteChangedCommand>()
.Select(g => g(this));
public void selection_changed(IEnumerable<VolSurfaceDisplayGridExpiration> selected) {
_selected_exps = selected.ToList();
@@ -7,8 +7,6 @@
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.ViewModel.Commands;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+110 -157
View File
@@ -1,4 +1,3 @@
#nullable enable
namespace mbts.spark {
using bts;
using bts.api;
@@ -9,202 +8,137 @@ namespace mbts.spark {
using mbts.spark.VolSurface;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
using mbts.ui.shell;
using Spark.Core.Disposables;
using Spark.ViewModel.Commands;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
class VolSurfaceVM : DisposableViewModelBase {
class VolSurfaceVM : ViewModelBase, IDisposable {
readonly int _vs_id;
readonly IBus _bus;
readonly string _request_id = Guid.NewGuid().ToString();
readonly VolSurfaceDisplayModel _model;
readonly UserTuple _user;
readonly IRaiseCanExecuteChangedCommand _revert_cmd;
readonly IRaiseCanExecuteChangedCommand _commit_cmd;
readonly IDialogCommand _main_settings_cmd;
readonly IDialogCommand _advanced_settings_cmd;
readonly IDialogCommand _grid_settings_cmd;
string? _selected_state = string.Empty;
VolSurfaceDisplayModel _model;
VolSurfaceMacroGridSettings _grid_settings;
bool _ignore_implied_vol_settings;
public VolSurfaceVM(
VolSurfaceDisplayModel model,
int id,
IBus bus,
VolSurfaceWindowSettings startup_data,
UserTuple user
) {
public VolSurfaceVM(VolSurfaceDisplayModel model, int id, IBus bus, VolSurfaceWindowSettings startup_data) {
Log.notnull(startup_data);
_model = model;
var demux = new VolSurfaceEventDemux(model);
_vs_id = id;
_bus = bus;
_user = user;
macro_actions = new VolSurfaceMacroActionsVM(_model);
globals = new VolSurfaceGlobalParameterVM(_model);
// grid
_grid_settings = startup_data.macro_grid_settings ?? new VolSurfaceMacroGridSettings();
var hidden_exps_set = startup_data.hidden_exps.empty_if_null().ToHashSet();
grid = new VolSurfaceGridVM(
this, _model, _grid_settings.hidden_cols(), hidden_exps_set, _user);
grid.on_selection_changed += _on_grid_selection_changed;
_model.publish_grid_settings(_grid_settings);
// graphs
var demux = new VolSurfaceEventDemux(model);
vol_curve_vm = new VolSurfaceGraphVM(
_model, demux, startup_data, (show_ivol, exp) =>
_model.publish_new_grid_selection(_request_id, exp, show_ivol));
_model, demux, startup_data, (show_ivol, exp) => _model.publish_new_grid_selection(_request_id, exp, show_ivol));
vol_terms_vm = new VolTermsGraphVM(_model, demux);
// command
populate_empty_exps_cmd = new Command(() => { _model.populate_empty_expirations(); });
_revert_cmd = new Command(o => _model.revert(), o => _model.is_dirty);
_commit_cmd = new Command(o => _model.commit(), o => _model.is_dirty);
load_previous_cmd = new Command(
load_previous, () => !selected_previous_state.is_null_or_empty());
_main_settings_cmd = new DialogCommand(mk_main_settings_win);
_advanced_settings_cmd = new DialogCommand(mk_advanced_settings_win);
_grid_settings_cmd = new DialogCommand(mk_grid_settings_win);
_model.on_dirty_change += () => {
RaisePropertyChanged(nameof(is_dirty));
_revert_cmd.RaiseCanExecuteChanged();
_commit_cmd.RaiseCanExecuteChanged();
};
demux.on_candidate_grid_change += () =>
previous_states.sync_with(_model.get_previous_states());
demux.on_schema_change += () => {
var existing_cols = _grid_settings.column_settings.ToDictionary(c => c.id);
_grid_settings.set_columns(
model.get_active_grid()
.get_ordered_columns()
.Select(col => existing_cols.GetValueOrDefault(col.unique_col_name) ??
VolSurfaceColumnSettings.from_column_model(col)));
};
_dispose = Disposable.Create(
vol_curve_vm,
vol_terms_vm,
globals,
Disposable.Create(() => grid?.Dispose()),
_main_settings_cmd,
_advanced_settings_cmd,
_grid_settings_cmd,
_model);
Window? mk_main_settings_win() {
var settings = _model.get_active_settings();
if (settings is null) {
return null;
}
return new VolSurfaceSettingsEditor {
DataContext = new VolSurfaceSettingsEditorVM(
_bus, settings, _model.set_candidate_settings) {
can_edit_name = false
}
};
}
Window? mk_advanced_settings_win() {
revert_command = new DelegateCommand(o => _model.revert(), o => _model.is_dirty);
commit_command = new DelegateCommand(o => _model.commit(), o => _model.is_dirty);
load_previous_command = new DelegateCommand(_ => { var cur = selected_previous_state; selected_previous_state = null; _model.load_previous_state(cur); }, _ => !string.IsNullOrEmpty(selected_previous_state));
main_settings_command = new DialogCommand(() => {
var settings = _model.get_active_settings();
if (settings == null) {
return null;
}
var vm = new VolSurfaceAdvancedSettingsEditorVM(
VolSurfaceUtilityFunctions.clone_proto(settings.advanced_settings),
settings.core_settings.vol_surface_name);
vm.request_commit += commit_advanced_settings;
return new VolSurfaceAdvancedSettingsEditor {
DataContext = vm
};
async void commit_advanced_settings(VolSurfaceAdvancedSettings value) {
var current_settings = await _get_volsurface_settings();
if (current_settings == null) {
BTSMessageBox.Show("Error changing settings", "VS Settings");
}
else {
current_settings.advanced_settings = value;
_model.set_candidate_settings(current_settings);
}
}
}
Window? mk_grid_settings_win() {
return new VolSurfaceSettingsEditor { DataContext = new VolSurfaceSettingsEditorVM(_bus, settings, _model.set_candidate_settings) };
});
advanced_settings_command = new DialogCommand(() => {
var settings = _model.get_active_settings();
if (settings is null) {
if (settings == null) {
return null;
}
var vm = new VolSurfaceGridSettingsEditorVM(
settings.core_settings.vol_surface_name, _grid_settings.Clone());
// return new VolSurfaceAdvancedSettingsEditor {
// DataContext = new VolSurfaceAdvancedSettingsEditorVM(
// VolSurfaceUtilityFunctions.clone_proto(settings.advanced_settings),
// settings.core_settings.vol_surface_name,
// advanced_settings => {
// var current_settings = _get_volsurface_settings();
// if (current_settings == null) BTSMessageBox.Show("Error changing settings", "VS Settings", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error, true, true, true);
// else {
// current_settings.advanced_settings = advanced_settings;
// _model.set_candidate_settings(current_settings);
// }
// }
// )
// };
return null;
});
grid_settings_command = new DialogCommand(() => {
var settings = _model.get_active_settings();
if (settings == null) {
return null;
}
var vm = new VolSurfaceGridSettingsEditorVM(settings.core_settings.vol_surface_name, _grid_settings.Clone());
vm.request_commit += on_grid_settings;
return new VolSurfaceGridSettingsEditor {
DataContext = vm
};
});
_model.on_dirty_change += raise_dirty_changed;
macro_actions = new VolSurfaceMacroActionsVM(_model);
globals = new VolSurfaceGlobalParameterVM(_model);
demux.on_candidate_grid_change += () => previous_states.sync_with(_model.get_previous_states());
demux.on_schema_change += () => {
// _grid_settings.clear_columns();
var existing_cols = _grid_settings.column_settings.ToDictionary(c => c.id);
_grid_settings.set_columns(
model.get_active_grid()
.get_ordered_columns()
.Select(col => {
if (!existing_cols.TryGetValue(col.unique_col_name, out var rv)) {
rv = VolSurfaceColumnSettings.from_column_model(col);
}
return rv;
}));
};
grid = new VolSurfaceGridVM(this, _model, _grid_settings.hidden_cols(), new HashSet<VolSurfaceExpiration>(startup_data.hidden_exps ?? Enumerable.Empty<VolSurfaceExpiration>()));
grid.on_selection_changed += _on_grid_selection_changed;
void on_grid_settings(VolSurfaceMacroGridSettings new_settings) {
using var _ = grid.defer_invalidate_rows();
_grid_settings = new_settings;
grid.load_from(_grid_settings);
grid.invalidate_rows();
_model.publish_grid_settings(_grid_settings);
}
}
void load_previous() {
var cur = selected_previous_state;
selected_previous_state = null;
_model.load_previous_state(cur);
void on_grid_settings(VolSurfaceMacroGridSettings new_settings) {
using var _ = grid.defer_invalidate_rows();
_grid_settings = new_settings;
grid.load_from(_grid_settings);
grid.invalidate_rows();
_model.publish_grid_settings(_grid_settings);
}
}
public VolSurfaceGridVM grid { get; }
public VolSurfaceGraphVM vol_curve_vm { get; }
public VolTermsGraphVM vol_terms_vm { get; }
public VolSurfaceMacroActionsVM macro_actions { get; }
public VolSurfaceGlobalParameterVM globals { get; }
public VolSurfaceGridVM grid { get; private set; }
public VolSurfaceGraphVM vol_curve_vm { get; private set; }
public VolTermsGraphVM vol_terms_vm { get; private set; }
public VolSurfaceMacroActionsVM macro_actions { get; private set; }
public VolSurfaceGlobalParameterVM globals { get; private set; }
public bool ignore_implied_vol_settings {
get => _ignore_implied_vol_settings;
set {
if (Set(ref _ignore_implied_vol_settings, value)) {
_model.publish_ignore_implied_vol_settings(_request_id, value);
}
}
}
private bool _ignore_implied_vol_settings;
public bool ignore_implied_vol_settings { get { return _ignore_implied_vol_settings; } set { _model.publish_ignore_implied_vol_settings(_request_id, _ignore_implied_vol_settings = value); } }
public bool is_dirty { get { return _model.is_dirty; } }
public IRaiseCanExecuteChangedCommand revert_command { get; private set; }
public IRaiseCanExecuteChangedCommand commit_command { get; private set; }
public IDialogCommand main_settings_command { get; private set; }
public IDialogCommand advanced_settings_command { get; private set; }
public IDialogCommand grid_settings_command { get; private set; }
public ICommand details_command => new DelegateCommand(_ => _show_details());
public ICommand load_previous_command { get; private set; }
public ObservableCollection<string> previous_states { get; } = new();
public string? selected_previous_state {
get => _selected_state;
private string _selected_state = string.Empty;
public string selected_previous_state {
get { return _selected_state; }
set {
if (Set(ref _selected_state, value)) {
load_previous_cmd.TryRaiseCanExecuteChanged();
}
_selected_state = value;
load_previous_command.TryRaiseCanExecuteChanged();
RaisePropertyChanged();
}
}
public bool is_dirty => _model.is_dirty;
public ICommand revert_cmd => _revert_cmd;
public ICommand commit_cmd => _commit_cmd;
public ICommand populate_empty_exps_cmd { get; }
public ICommand main_settings_cmd => _main_settings_cmd;
public ICommand advanced_settings_cmd => _advanced_settings_cmd;
public ICommand grid_settings_cmd => _grid_settings_cmd;
public ICommand details_command => new Command(_show_details);
public ICommand load_previous_cmd { get; }
public VolSurfaceWindowSettings get_startup_data() {
var rv = new VolSurfaceWindowSettings {
id = _vs_id,
@@ -218,20 +152,39 @@ namespace mbts.spark {
return rv;
}
public void Dispose() {
vol_curve_vm.Dispose();
vol_terms_vm.Dispose();
globals.Dispose();
if (grid != null) grid.Dispose();
main_settings_command.Dispose();
advanced_settings_command.Dispose();
grid_settings_command.Dispose();
Disposable.dispose(ref _model);
}
void raise_dirty_changed() {
RaisePropertyChanged("is_dirty");
revert_command.RaiseCanExecuteChanged();
commit_command.RaiseCanExecuteChanged();
}
void _on_grid_selection_changed(IEnumerable<VolSurfaceDisplayGridExpiration> selection) {
if (selection.TryGetFirst(out var selected)) {
var selected = selection.FirstOrDefault();
if (selected != null) {
vol_curve_vm.set_visible_expiration(selected);
_model.publish_new_grid_selection(_request_id, selected, vol_curve_vm.show_implied_vol);
}
macro_actions.selection_changed(selection);
}
void _show_details() =>
_model.show_details();
async Task<VolSurfaceSettings?> _get_volsurface_settings() =>
async Task<VolSurfaceSettings> _get_volsurface_settings() =>
(await _bus.subscribe(SparkTags.VolSurfaceCommittedOutputTag(_vs_id)).FirstOrDefaultAsync())
?.volsurface_input
?.surface_settings;
void _show_details() =>
_model.show_details();
}
}
@@ -1,22 +1,19 @@
namespace mbts.spark {
using bts.api;
using bts.collections;
using bts.api;
using bts.utils;
using mbts.analytics;
using mbts.collections;
using mbts.ui;
using mbts.ui.shell;
using mbts.analytics;
using mbts.collections;
using mbts.ui;
using mbts.ui.shell;
using Spark.Core;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Windows.Media;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Windows.Media;
namespace mbts.spark {
class VolTermsGraphVM : DisposableViewModelBase {
readonly VolSurfaceDisplayModel _model;
readonly DateTimeAxisVM _exp_terms_x_axis;
+9 -24
View File
@@ -5,9 +5,6 @@
using mbts.analytics;
using mbts.ui.controls;
using Spark.Core.Disposables;
using Spark.Core.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -127,20 +124,6 @@
_log_bad_transition(_state, "set candidate grid");
}
public void populate_empty_expirations() {
bts.Log.Ui.info(String.Format("VolSurfaceDisplayModel.populate_expirations called with state: {0}",
_state.ToString()));
if (_state == DisplayModelState.Idle || _state == DisplayModelState.ResponseReady) {
DisplayModelData curr_data = _get_current_data();
VolSurfaceInput vs_inp =
VolSurfaceDataFunctions.populate_empty_expirations(curr_data.vs_output.volsurface_input);
_change_state(DisplayModelState.WaitingForCalcResponse);
_bus.post(SparkTags.VolSurfaceCalculationRequestTag(_vs_id),
new VolSurfaceCalculationRequest(vs_inp, _request_version_no++));
} else
_log_bad_transition(_state, "populate_expirations");
}
#endregion
#region Globals
@@ -160,7 +143,6 @@
return (_committed_data != null && _committed_data.vs_output != null) ? _committed_data.vs_output.volsurface_input.surface_parameters.vol_time : DateTime.MinValue;
}
public void set_candidate_vol_time(DateTime vol_time) {
bts.Log.Ui.info($"VolSurfaceDisplayModel.set_candidate_vol_time vs_id: {_vs_id} state: {_state.ToString()} vol_time: {vol_time}");
if (_state == DisplayModelState.Idle || _state == DisplayModelState.ResponseReady) {
DisplayModelData curr_data = _get_current_data();
if (curr_data != null) {
@@ -314,7 +296,7 @@
bool succ = false;
VolSurfaceInputSnapshot vs_sn = _input_snapshot_list.ToList().Find(v => v.save_marker == state_name);
if (vs_sn != null) {
var vs_inp = ProtobufSerializationHelper.deserialize<VolSurfaceInput>(vs_sn.data);
VolSurfaceInput vs_inp = VolSurfaceUtilityFunctions.deserialize_proto<VolSurfaceInput>(vs_sn.data);
if (vs_inp != null) {
_change_state(DisplayModelState.WaitingForCalcResponse);
_bus.post(SparkTags.VolSurfaceCalculationRequestTag(_vs_id),
@@ -337,7 +319,7 @@
public byte[] get_current_state() {
DisplayModelData curr_data = _get_current_data();
if (curr_data != null && curr_data.vs_output != null && curr_data.vs_output.volsurface_input != null)
return ProtobufSerializationHelper.serialize(curr_data.vs_output.volsurface_input);
return VolSurfaceUtilityFunctions.serialize_proto(curr_data.vs_output.volsurface_input);
else
return null;
}
@@ -345,7 +327,7 @@
/// Sets the state that is passed in bytes form. Returns
/// true is state is set successfully
public bool set_state(byte[] file_data) {
var vs_input = ProtobufSerializationHelper.deserialize<VolSurfaceInput>(file_data);
VolSurfaceInput vs_input = VolSurfaceUtilityFunctions.deserialize_proto<VolSurfaceInput>(file_data);
if (vs_input != null) {
if (_state == DisplayModelState.Idle || _state == DisplayModelState.ResponseReady) {
_change_state(DisplayModelState.WaitingForCalcResponse);
@@ -584,7 +566,9 @@
private void _change_state(DisplayModelState new_state) {
bool dirty_before = _translate_to_dirty(_state),
dirty_after = _translate_to_dirty(new_state);
bts.Log.Ui.info($"VolSurfaceDisplayModel._change_state vs_id: {_vs_id} {_state.ToString()}->{new_state.ToString()}");
// mbts.Logger.log(bts.log.Level.Info, bts.log.Subsystem.ui,
// String.Format("State change {0}->{1}", _state.ToString(), new_state.ToString()));
bts.Log.Ui.info(String.Format("State change {0}->{1}", _state.ToString(), new_state.ToString()));
_state = new_state;
if (dirty_before != dirty_after && on_dirty_change != null) {
_bus.post(SparkTags.VolSurfaceDirtyStateTag(_vs_id), dirty_after);
@@ -627,8 +611,9 @@
}
}
private void _log_bad_transition(DisplayModelState state, string action) {
bts.Log.Ui.error($"VoLSurfaceDisplayModel bad_transaction vs_id: {_vs_id} {state}:{action}");
private static void _log_bad_transition(DisplayModelState state, string action) {
//mbts.Logger.log(bts.log.Level.Error, bts.log.Subsystem.ui, String.Format("{0}:{1}", state, action));
bts.Log.Ui.error(String.Format("{0}:{1}", state, action));
}
private static bool _translate_to_dirty(DisplayModelState state) {
+3 -106
View File
@@ -1,4 +1,4 @@
namespace mbts.spark {
namespace mbts.spark {
using bts.api;
using bts.api.analytics;
using bts.utils;
@@ -34,9 +34,6 @@ namespace mbts.spark {
public static HashSet<string> hidden_cols(this VolSurfaceMacroGridSettings gs) =>
new(gs.column_settings.Where(cc => !cc.is_visible).Select(cc => cc.id));
public static bool is_editable(this VolSurfaceColumnType vs_col_type) =>
vs_col_type is VolSurfaceColumnType.ReadWriteTick;
public static analytics.VolSurfaceExpiration ToVSExp(this VolSurfaceDisplayGridExpiration vsdge) =>
new() { expiration = vsdge.expiration, symbol = vsdge.symbol };
@@ -83,10 +80,10 @@ namespace mbts.spark {
_ => false,
};
public static IEnumerable<VolSurfaceExpiration> select_exp(this IEnumerable<VolSurfaceRowVM> rows) =>
public static IEnumerable<VolSurfaceExpiration> select_exp(this IEnumerable<VolSurfaceRowContext> rows) =>
rows.Select(r => r.expiration.ToVSExp());
public static IEnumerable<VolSurfaceDisplayGridExpiration> select_display_exp(this IEnumerable<VolSurfaceRowVM> rows) =>
public static IEnumerable<VolSurfaceDisplayGridExpiration> select_display_exp(this IEnumerable<VolSurfaceRowContext> rows) =>
rows.Select(r => r.expiration);
public static IEnumerable<VolSurfaceDisplayGridExpiration> where_master(this IEnumerable<VolSurfaceDisplayGridExpiration> rows) =>
@@ -116,106 +113,6 @@ namespace mbts.spark {
public static IEnumerable<(double x, double y)> real_all_points(this VolCurveDisplayGraph graph) =>
_select_real_points(graph.all_points);
public static bool is_delta_greek(this VolSurfaceGreeks g) =>
g switch {
VolSurfaceGreeks.Delta or
VolSurfaceGreeks.Bsmdelta or
VolSurfaceGreeks.FlatVolDelta or
VolSurfaceGreeks.ExpirationDelta or
VolSurfaceGreeks.Charm or
VolSurfaceGreeks.Vanna => true,
_ => false,
};
public static bool is_gamma_greek(this VolSurfaceGreeks g) =>
g switch {
VolSurfaceGreeks.Gamma or
VolSurfaceGreeks.Bsmgamma => true,
_ => false
};
public static bool is_oev_col(this VolSurfaceGreeks g) =>
g switch {
VolSurfaceGreeks.Oev or
VolSurfaceGreeks.Bsmoev => true,
_ => false
};
public static VolSurfaceExpiration to_vs_exp(this SecurityExpiration sec_exp) =>
new() { security_type = sec_exp.security_type, symbol = sec_exp.symbol, expiration = sec_exp.expiration };
public static DriverCalculationType to_driver_calc_type(
this ExpirationDriver.ModeOneofCase driver_mode) =>
driver_mode switch {
ExpirationDriver.ModeOneofCase.Security => DriverCalculationType.Direct,
ExpirationDriver.ModeOneofCase.TimeWeightedInterpolated => DriverCalculationType.TimeWeightedInterp,
_ => throw new NotImplementedException()
};
public static SecurityExpiration to_sec_exp(
this bts.api.domain.SecurityExpiration api_sec_exp) =>
to_sec_exp(new bts.api.domain.SecurityExpirationImmutable(api_sec_exp));
public static SecurityExpiration to_sec_exp(
this bts.api.domain.SecurityExpirationImmutable api_sec_exp) =>
new(
api_sec_exp.Exchange.to_venue(),
api_sec_exp.SecurityType.contract_case(),
api_sec_exp.Symbol,
api_sec_exp.Expiration.date_time());
public static SecurityExpiration to_sec_exp(this Security sec) =>
to_sec_exp(new SecurityImmutable(sec));
public static SecurityExpiration to_sec_exp(this SecurityImmutable sec) {
var exp_date = sec.security_expiration() ?? throw new InvalidOperationException();
return new(sec.Exchange, sec.ContractCase, sec.Symbol, exp_date, sec.Id);
}
public static SecurityTuple security_tuple(this Security sec) {
return security_tuple(new SecurityImmutable(sec));
}
public static SecurityTuple security_tuple(this SecurityImmutable sec) {
bts.api.SecurityTuple rv = new SecurityTuple();
rv.Symbol = sec.Symbol;
rv.Type = sec.security_type();
rv.Venue = sec.Exchange;
return rv;
}
public static ExpirationDriver to_exp_driver(this SecurityExpiration sec_exp) =>
new() {
Security = new() {
Symbol = sec_exp.symbol,
Expiration = new() {
ExpirationDay = (uint)sec_exp.expiration.Day,
ExpirationMonth = (uint)sec_exp.expiration.Month,
ExpirationYear = (uint)sec_exp.expiration.Year,
},
Exchange = sec_exp.exchange.to_api_venue(),
}
};
public static ExpirationDriverImmutable to_exp_driveri(this SecurityExpiration sec_exp) =>
new(to_exp_driver(sec_exp));
public static bts.api.domain.SecurityExpiration to_api_sec_exp(this SecurityExpiration sec_exp) =>
new() {
Exchange = sec_exp.exchange.to_api_venue(),
SecurityType = sec_exp.security_type,
Symbol = sec_exp.symbol,
Expiration = new() {
ExpirationDay = (uint)sec_exp.expiration.Day,
ExpirationMonth = (uint)sec_exp.expiration.Month,
ExpirationYear = (uint)sec_exp.expiration.Year
}
};
public static bts.api.domain.SecurityExpirationImmutable to_api_sec_expi(
this SecurityExpiration sec_exp) =>
new(to_api_sec_exp(sec_exp));
static IEnumerable<(double x, double y)> _select_real_points(IEnumerable<Tuple<double, double>> points) =>
points.Where(pt => pt.Item1.is_real() && pt.Item2.is_real()).Select(t => (t.Item1, t.Item2));
}
@@ -22,7 +22,7 @@ namespace mbts.spark {
// ii. IntepGreeks for each vs
public class VolSurfaceInterpGreeksMultiplexer : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly ITag<VolSurfaceUsageListImmutable?> _in_bb_vs_usage_tag;
private readonly ITag<VolSurfaceUsageListImmutable> _in_bb_vs_usage_tag;
private IDictionary<int, ITag<VSTheos>> _vs_theo_tags =
new Dictionary<int, ITag<VSTheos>>();
private VolSurfaceUsageListImmutable _vs_usage_map =
@@ -48,8 +48,8 @@ namespace mbts.spark {
}
private void _analyze_usages() {
_vs_usage_map = _bb.read_state(_in_bb_vs_usage_tag) ??
new VolSurfaceUsageList().immutable();
_vs_usage_map = _bb.read_state(_in_bb_vs_usage_tag,
new VolSurfaceUsageListImmutable(new VolSurfaceUsageList()));
bts.Log.Gen.info($"VolSurfaceInterpGreeksMultiplexer._analyze_usages " +
$"count: {_vs_usage_map.UsageList.Count()}");
foreach (int vs_id in _vs_usage_map.vss()) {
+5 -6
View File
@@ -24,14 +24,13 @@ Value: OptionDriverPriceMap which is a wrapper around
IDictionary<SecurityExpiration, Tuple<Security, VolSurfaceBBOUnit>>
----------------------------------------------------------------
SingleVolSurfaceInterpGreeksPublisher (for one vs) listens to all inputs and publishes
IDictionary<VolSurfaceExpiration, IEnumerable<Theos>>
SingleVolSurfaceInterpGreeksPublisher listens to all inputs and publishes
exp vs InterpGreeks
VolSurfaceInterpGreeksMultiplexer (for multiple option symbols) listens to all vol
surfaces, resolves symbols and for each option_symbol publishes
OptionTheoInterpolationData*
VolSurfaceInterpGreeksMultiplexer listens to all vol surfaces, resolves symbols and
publishes symbol vs InterpGreeks
SymbolAggregationPublisher is a thin generic class that combines individual symbol data and
SymbolAggregationPublisher is a thin class that combines individual symbol data and
publishes symbol vs data map
LiveTheoPublisher:
+6 -17
View File
@@ -1,12 +1,8 @@
using bts;
using bts.api;
using bts.api.admin;
using bts.api.core;
using bts.collections;
using bts.utils;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Composition;
@@ -22,10 +18,10 @@ namespace mbts.spark {
IReadOnlyDictionary<uint, string> _account_map = DictionaryHelper.empty<uint, string>();
[ImportingConstructor]
public AdminModel(IBus bus, AdminService service, Lazy<UserTuple> user)
: this(bus, service, user.Value) { }
public AdminModel(AdminService service, Lazy<UserTuple> user)
: this(service, user.Value) { }
AdminModel(IBus bus, AdminService service, UserTuple user) {
AdminModel(AdminService service, UserTuple user) {
this.user = user;
// accounts
on_accounts = service.subscribei_admin_data()
@@ -43,7 +39,7 @@ namespace mbts.spark {
on_users = Observable.Return(DictionaryHelper.wrap_value(user.id, user.name));
all_user_tuples = user.permissions == UserTuple.Permissions.Admin ?
service.subscribei_admin_data()
.Select(u => u.all.FirstOrNull())
.Select(u => u.all.TryFirst())
.Where(ad => ad.HasValue)
.Select(ad => ad.Value.Users
.Where(u => user.can_access_user_data(u.Id))
@@ -52,14 +48,7 @@ namespace mbts.spark {
.Table(UserTuple.key_fn)
.Cache() :
Observable.Return(TableUpdate<UserTuple, uint>.mk_empty(UserTuple.key_fn).with_added(new[] { user }));
_dispose = Disposable.Create(
all_user_tuples.Subscribe(t => bus.post(SparkTags.Users, t.state)),
on_accounts.Subscribe(map => {
_account_map = map;
var account_tuple_map = MappedDictionary.create(map, (id, name) => new AccountTuple(id, name));
bus.post(SparkTags.AccountsTag, account_tuple_map);
}));
bus.post(SparkTags.CurrentUser, user);
_dispose = on_accounts.Subscribe(map => _account_map = map);
}
public IObservable<IReadOnlyDictionary<uint, string>> on_users { get; }
@@ -81,7 +70,7 @@ namespace mbts.spark {
}));
public AccountTuple? get_default_account() =>
_account_map.Select(kv => new AccountTuple(kv.Key, kv.Value)).FirstOrNull();
_account_map.Select(kv => new AccountTuple(kv.Key, kv.Value)).TryFirst();
public void Dispose() =>
Disposable.dispose(ref _dispose);
@@ -44,7 +44,7 @@ namespace mbts.spark {
new SecurityAlgoTypeSetImmutable(
new SecurityCriteriaClause(
exchanges: new[] { Venue.Glbx },
security_types: new[] { SecurityType.Future, SecurityType.Spread, SecurityType.Option }),
security_types: new[] { SecurityType.Future, SecurityType.Spread }),
new AlgoTypeSetImmutable(
full_algo_set.factory_types.Except(new[] { AlgoType.Spreader }),
full_algo_set.dynamic_types)),
@@ -52,7 +52,7 @@ namespace mbts.spark {
new SecurityAlgoTypeSetImmutable(
new SecurityCriteriaClause(
exchanges: EnumHelper.get_values_except(Venue.Glbx, Venue.Unset).ToArray(),
security_types: new[] { SecurityType.Future, SecurityType.Spread, SecurityType.Option }),
security_types: new[] { SecurityType.Future, SecurityType.Spread }),
new AlgoTypeSetImmutable(
full_algo_set.factory_types.Except(new[] { AlgoType.Spreader, AlgoType.GlbxIceberg }),
full_algo_set.dynamic_types)),
+2 -2
View File
@@ -82,7 +82,7 @@ namespace mbts.spark {
this AlgoTypeSetImmutable algo_set,
DynamicOrderHelper dynamic_helper,
Func<CustomOrderDefinitionImmutable, CustomOrderDefinitionImmutable> fit_to_type = null,
double display_qty_divisor = 1) =>
uint display_qty_divisor = 1) =>
//
algo_set.factory_types
.Select(algo_type => {
@@ -109,7 +109,7 @@ namespace mbts.spark {
SecurityCriteriaClause clause,
DynamicOrderHelper dynamic_helper,
Func<CustomOrderDefinitionImmutable, CustomOrderDefinitionImmutable> fit_to_type = null,
double display_qty_divisor = 1) {
uint display_qty_divisor = 1) {
//
ImmutableDictionary<AlgoKey, AlgoData> rv = null;
foreach (var set in security_algo_sets) {
+21 -27
View File
@@ -1,5 +1,4 @@
using bts;
using bts.api;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.utils;
@@ -51,7 +50,9 @@ namespace mbts.spark {
/// <see cref="subscribe_algos"/>,
/// <see cref="subscribe_algos(SecurityCriteriaClause)"/>,
/// <see cref="subscribe_algos(SecurityImmutable)"/>
_studio_algos_source = studio_model.definitions.Cache();
_studio_algos_source = studio_model.definitions
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
.Cache();
}
public AlgoTypeSetImmutable get_permitted_algos(SecurityImmutable security) =>
@@ -66,11 +67,10 @@ namespace mbts.spark {
public IObservable<TableUpdate<AlgoData, AlgoKey>> subscribe_algos() =>
_studio_algos_source
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
.AppendTable(_security_algo_types
.Select(s => s.algo_type_set)
.Aggregate(AlgoTypeSetImmutable.empty, (a, b) => a.union_with(b))
.define_algos(_dynamic_helper));
.AppendTable(_security_algo_types
.Select(s => s.algo_type_set)
.Aggregate(AlgoTypeSetImmutable.empty, (a, b) => a.union_with(b))
.define_algos(_dynamic_helper));
public IObservable<TableUpdate<AlgoData, AlgoKey>> subscribe_algos(SecurityCriteriaClause security_clause) {
var system_algos = _security_algo_types.match_algos(
@@ -78,13 +78,11 @@ namespace mbts.spark {
_dynamic_helper,
def => _fit_to_type(def, security_clause, _security_algo_types));
return _studio_algos_source
.Select(
a => (algo: a, fitted_def: _fit_to_type(a.value, security_clause, _security_algo_types)),
tup => tup.algo.key)
.Select(tup => AlgoData.create(tup.fitted_def, tup.algo.name, tup.algo.key), a => a.key)
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
.AppendTable(system_algos.Values);
return _studio_algos_source.Select(
a => (algo: a, fitted_def: _fit_to_type(a.value, security_clause, _security_algo_types)),
tup => tup.algo.key)
.Select(tup => AlgoData.create(tup.fitted_def, tup.algo.name, tup.algo.key), a => a.key)
.AppendTable(system_algos.Values);
}
public IObservable<TableUpdate<AlgoData, AlgoKey>> subscribe_algos(SecurityImmutable security) {
@@ -98,18 +96,14 @@ namespace mbts.spark {
def => _fit_to_type(def, security, _security_algo_types),
_security_auxiliary_model.get_display_quantity_divisor(security));
var filtered_studio_algos = _studio_algos_source
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
.Select(
a => (algo: a, fitted_def: _fit_to_type(a.value, security, _security_algo_types)),
tup => tup.algo.key)
// filter out algos that use an unsupported root, because this would have the effect of duplicating the default algo
// (e.g., GlbxIceberg orders would be converted to Limit/Spreader orders for non-Glbx securities).
.Filter(tup => tup.fitted_def.actual().CustomCase == tup.algo.value.actual().CustomCase, tup => tup.algo.key)
.Select(tup => AlgoData.create(tup.fitted_def, tup.algo.name, tup.algo.key), AlgoData.key_fn);
return filtered_studio_algos
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
.AppendTable(system_algos.Values);
return _studio_algos_source.Select(
a => (algo: a, fitted_def: _fit_to_type(a.value, security, _security_algo_types)),
tup => tup.algo.key)
// filter out algos that use an unsupported root, because this would have the effect of duplicating the default algo
// (e.g., GlbxIceberg orders would be converted to Limit/Spreader orders for non-Glbx securities).
.Filter(tup => tup.fitted_def.actual().CustomCase == tup.algo.value.actual().CustomCase, tup => tup.algo.key)
.Select(tup => AlgoData.create(tup.fitted_def, tup.algo.name, tup.algo.key), AlgoData.key_fn)
.AppendTable(system_algos.Values);
}
protected virtual void assign_fallback_algo(CustomOrderDefinition mdef, SecurityCriteriaClause security_clause) {
@@ -10,8 +10,7 @@ namespace mbts.analytics {
double gamma = double.NaN,
double vega = double.NaN,
double theta = double.NaN,
double rho = double.NaN,
double normalized_theo = double.NaN) {
double rho = double.NaN) {
this.vol = vol;
this.driver = driver;
this.theo = theo;
@@ -20,7 +19,6 @@ namespace mbts.analytics {
this.vega = vega;
this.theta = theta;
this.rho = rho;
this.normalized_theo = normalized_theo;
}
public static GreeksTuple empty => new();
@@ -33,7 +31,6 @@ namespace mbts.analytics {
public double vega { get; }
public double theta { get; }
public double rho { get; }
public double normalized_theo { get; }
public bool Equals(GreeksTuple other) =>
vol.Equals(other.vol) &&
@@ -68,8 +65,7 @@ namespace mbts.analytics {
double? gamma = null,
double? vega = null,
double? theta = null,
double? rho = null,
double? normalized_theo = null) =>
double? rho = null) =>
new(
vol ?? greeks_tuple.vol,
driver ?? greeks_tuple.driver,
@@ -78,7 +74,12 @@ namespace mbts.analytics {
gamma ?? greeks_tuple.gamma,
vega ?? greeks_tuple.vega,
theta ?? greeks_tuple.theta,
rho ?? greeks_tuple.rho,
normalized_theo ?? greeks_tuple.normalized_theo);
rho ?? greeks_tuple.rho);
public static double normalized_theo(this in GreeksTuple greeks_tuple) =>
greeks_tuple.theo / greeks_tuple.driver;
public static double normalized_delta(this in GreeksTuple greeks_tuple) =>
greeks_tuple.delta / greeks_tuple.driver;
}
}
@@ -1,7 +1,7 @@
namespace mbts.analytics {
using bts;
using Spark.Core.Protobuf;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
@@ -40,7 +40,7 @@
new Dictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData>());
public VolSurfaceOutput Clone() => new(
ProtobufSerializationHelper.clone(volsurface_input),
ProtoBufSerializationHelper.clone_protobuf(volsurface_input),
new Dictionary<VolSurfaceExpiration, VolCurveOutput>(exp_output_map),
new Dictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData>(aux_data_map));
@@ -1,191 +1,112 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
using bts.api;
using bts.settings;
using bts.spark;
using mbts.spark;
namespace mbts {
using bts.api;
using bts.utils;
using mbts.ui.utility;
using mbts.utils;
using ProtoBuf;
using Spark.Core;
using Spark.Core.Collections;
using Spark.Core.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
[ProtoContract]
[ProtoBuf.ProtoContract()]
[Serializable]
public class SecurityExpiration : IProtobuf<SecurityExpiration> {
public readonly static SecurityExpiration empty = new(
SecurityTuple.empty.security_exchange,
SecurityTuple.empty.security_type,
SecurityTuple.empty.security_name,
DateTime.MinValue);
public class SecurityExpiration : IEquatable<SecurityExpiration> {
[ProtoBuf.ProtoMember(1)]
private readonly SecurityTuple _sec_tup;
[ProtoBuf.ProtoMember(2)]
public readonly DateTime expiration;
[ProtoBuf.ProtoMember(2)]
private readonly Nullable<uint> _id;
public readonly static SecurityExpiration empty = new SecurityExpiration(SecurityTuple.empty.security_exchange, SecurityTuple.empty.security_type, SecurityTuple.empty.security_name, DateTime.MinValue);
// for deserialization
SecurityExpiration() =>
security_tuple = SecurityTuple.empty;
// for ProtoBuf deserialization
private SecurityExpiration() { }
public SecurityExpiration(
Venue exchange,
Security.ContractOneofCase type,
string symbol,
DateTime exp,
uint? id = null)
public SecurityExpiration(Venue exchange, Security.ContractOneofCase type, string symbol, DateTime exp, Nullable<uint> id = null)
: this(exchange, type.security_type(), symbol, exp, id) { }
public SecurityExpiration(
Venue exchange, SecurityType type, string symbol, DateTime expiration, uint? id = null)
: this(new SecurityTuple(type, symbol, exchange), expiration, id) { }
public SecurityExpiration(Venue exchange, SecurityType type, string symbol, DateTime exp, Nullable<uint> id = null)
: this(new SecurityTuple(type, symbol, exchange), exp, id) { }
public SecurityExpiration(
SecurityTuple security_tuple, DateTime expiration, uint? id = null) {
this.security_tuple = security_tuple;
this.expiration = expiration;
this.id = id;
public SecurityExpiration(SecurityTuple sec_tup, DateTime exp, Nullable<uint> id = null) {
_sec_tup = sec_tup;
expiration = exp;
_id = id;
}
[ProtoMember(1)]
public SecurityTuple security_tuple { get; }
public SecurityTuple security_tuple { get { return _sec_tup; } }
[ProtoMember(2)]
public DateTime expiration { get; }
public SecurityType security_type { get { return _sec_tup.security_type; } }
[ProtoMember(3)]
public uint? id { get; }
public string symbol { get { return _sec_tup.security_name; } }
public SecurityType security_type => security_tuple?.security_type ?? SecurityType.Unset;
public string symbol => security_tuple?.security_name ?? string.Empty;
public Venue exchange => security_tuple?.security_exchange ?? Venue.Unset;
public Venue exchange { get { return _sec_tup.security_exchange; } }
[Flags]
public enum Properties {
Type = 1,
Symbol = 1 << 1,
Expiration = 1 << 2,
Venue = 1 << 3,
Id = 1 << 4
public bool is_empty() { return Equals(empty); }
public Nullable<uint> id() { return _id; }
public SecurityExpiration unset_exchange() {
return new SecurityExpiration(Venue.Unset, security_type, symbol, expiration);
}
public static IEqualityComparer<SecurityExpiration> get_eq_comparer() =>
get_eq_comparer(
Properties.Type, Properties.Symbol, Properties.Expiration, Properties.Venue);
public static IEqualityComparer<SecurityExpiration> get_eq_comparer(Properties props) {
return EqualityComparerBuilder.create_comparer(
EnumHelper.split_flags(props).Select(get_prop_comparer));
static IEqualityComparer<SecurityExpiration> get_prop_comparer(Properties single_prop) =>
single_prop switch {
Properties.Type => wrap(s => s.security_type),
Properties.Symbol => wrap(s => s.symbol).to_safe(),
Properties.Expiration => wrap(s => s.expiration),
Properties.Venue => wrap(s => s.exchange),
Properties.Id => wrap(s => s.id).to_safe(),
_ => throw new NotImplementedException()
};
static IEqualityComparer<SecurityExpiration> wrap<T>(
Func<SecurityExpiration, T> selector, IEqualityComparer<T>? comparer = null) =>
EqualityComparer.Create(selector, comparer ?? EqualityComparer<T>.Default);
public bool Equals(SecurityExpiration other) {
if (other != null)
return _sec_tup.Equals(other._sec_tup) && expiration.Equals(other.expiration);
else
return false;
}
public static IEqualityComparer<SecurityExpiration> get_eq_comparer(params Properties[] hierarchy) =>
EqualityComparerBuilder.create(hierarchy.Select(get_eq_comparer)).to_comparer();
#region Object overrides
public static IComparer<SecurityExpiration> get_comparer() =>
get_comparer(Properties.Venue, Properties.Type, Properties.Symbol, Properties.Expiration);
public override int GetHashCode() { return _sec_tup.GetHashCode() ^ expiration.GetHashCode(); }
public static IComparer<SecurityExpiration> get_comparer(Properties prop) {
return prop switch {
Properties.Type => ComparerWrapper<SecurityExpiration>.wrap(s => s.security_type),
Properties.Symbol => ComparerWrapper<SecurityExpiration>.wrap(s => s.symbol),
Properties.Expiration => ComparerWrapper<SecurityExpiration>.wrap(s => s.expiration),
Properties.Venue => ComparerWrapper<SecurityExpiration>.wrap(s => s.exchange),
Properties.Id => ComparerWrapper<SecurityExpiration>.wrap(s => s.id),
_ => throw new NotImplementedException()
};
public override bool Equals(object obj) {
return Equals(obj as SecurityExpiration);
}
public static IComparer<SecurityExpiration> get_comparer(params Properties[] hierarchy) =>
ComparerBuilder.create(hierarchy.Select(get_comparer)).to_comparer();
public static bool operator ==(SecurityExpiration x, SecurityExpiration y) {
if (Object.ReferenceEquals(x, null)) return Object.ReferenceEquals(y, null);
if (Object.ReferenceEquals(y, null)) return false;
return x.Equals(y);
}
public static bool operator !=(SecurityExpiration x, SecurityExpiration y) { return !(x == y); }
public static IComparer<SecurityExpiration> get_exp_based_comparer() =>
get_comparer(Properties.Expiration, Properties.Venue, Properties.Type, Properties.Symbol);
public override string ToString() {
return String.Format("{0} {1}", _sec_tup.ToString(), expiration.ToShortDateString());
}
#endregion
public bool is_empty() => Equals(empty);
public static int ExpBasedCompare(SecurityExpiration x, SecurityExpiration y) {
if (y == null) return 1;
if (x == null) return -1;
int rv = x.expiration.CompareTo(y.expiration);
if (rv == 0)
rv = x._sec_tup.CompareTo(y._sec_tup);
return rv;
}
public SecurityExpiration unset_exchange() =>
new(Venue.Unset, security_type, symbol, expiration);
public SecurityExpiration Clone() =>
new(security_tuple.Clone(), expiration, id);
public bool Equals(SecurityExpiration? other) =>
other is not null &&
security_tuple.safe_equals(other.security_tuple) &&
expiration == other.expiration;
public override bool Equals(object? obj) =>
Equals(obj as SecurityExpiration);
public override int GetHashCode() =>
HashCode.Combine(security_tuple, expiration);
object ICloneable.Clone() =>
Clone();
public static bool operator ==(SecurityExpiration? x, SecurityExpiration? y) =>
x is null ? y is null : x.Equals(y);
public static bool operator !=(SecurityExpiration x, SecurityExpiration y) =>
!(x == y);
public override string ToString() =>
$"{security_tuple} {SecurityFormatter.format_expiration(expiration)}";
}
[ProtoContract]
public class SecurityExpirationProto : IProtobuf<SecurityExpirationProto> {
[ProtoMember(1)]
public SecurityTuple? security { get; set; }
[ProtoMember(2)]
public double expiration_oa_date { get; set; }
public SecurityExpirationProto Clone() =>
new() { security = security, expiration_oa_date = expiration_oa_date};
public bool Equals(SecurityExpirationProto? other) =>
other is not null &&
security.safe_equals(other.security) &&
expiration_oa_date.Equals(other.expiration_oa_date);
public override bool Equals(object? obj) =>
Equals(obj as SecurityExpirationProto);
public override int GetHashCode() =>
HashCode.Combine(security, expiration_oa_date);
object ICloneable.Clone() {
throw new NotImplementedException();
public static int SecBasedCompare(SecurityExpiration x, SecurityExpiration y) {
if (y == null) return 1;
if (x == null) return -1;
int rv = x._sec_tup.CompareTo(y._sec_tup);
if (rv == 0)
rv = x.expiration.CompareTo(y.expiration);
return rv;
}
}
public static class SecurityExpirationExtensions {
public static SecurityExpiration with(
this SecurityExpiration sec_exp,
Venue? exchange = null,
SecurityType? security_type = null,
string? symbol = null,
DateTime? expiration = null,
Optional<uint> id = default) => new(
exchange ?? sec_exp.exchange, security_type ?? sec_exp.security_type,
symbol ?? sec_exp.symbol,
expiration ?? sec_exp.expiration,
id.has_value ? id : sec_exp.id);
[ProtoBuf.ProtoContract]
public class SecurityExpirationProto {
[ProtoBuf.ProtoMember(1)]
public SecurityTuple security;
[ProtoBuf.ProtoMember(2)]
public double expiration_oa_date;
}
}
@@ -1,50 +1,58 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
using bts.api;
using bts.settings;
using bts.spark;
using mbts.spark;
namespace mbts {
using bts.api;
using Spark.Core.Data;
using System;
public class SecurityStrike : IEquatable<SecurityStrike> {
public SecurityStrike(
SecurityExpiration expiration, double strike, CallPutTuple<uint?> call_put_id = default) {
public readonly SecurityExpiration expiration;
public readonly double strike;
public readonly Tuple<UInt32?, UInt32?> call_put_id;
public SecurityStrike(SecurityExpiration expiration, double strike,
Tuple<UInt32?, UInt32?> call_put_id = null) {
this.expiration = expiration;
this.strike = strike;
this.call_put_id = call_put_id;
}
public SecurityStrike(
string symbol, DateTime exp, double strike, CallPutTuple<uint?> call_put_id = default)
: this(new(Venue.Unset, SecurityType.Option, symbol, exp), strike, call_put_id) { }
public SecurityStrike(string symbol, DateTime exp, double strike,
Tuple<UInt32?, UInt32?> call_put_id = null)
: this(new SecurityExpiration(
Venue.Unset, SecurityType.Option, symbol, exp), strike, call_put_id) { }
public SecurityExpiration expiration { get; }
public double strike { get; }
public CallPutTuple<uint?> call_put_id { get; }
public bool Equals(SecurityStrike other) {
if (other != null)
return expiration.Equals(other.expiration) && strike.Equals(other.strike);
else
return false;
}
/// TODO: figure out why <see cref="call_put_id"/> is excluded from equality logic
/// (was it an accident, or intentional? if the latter, why?)
public bool Equals(SecurityStrike? other) =>
other is not null &&
expiration.Equals(other.expiration) &&
strike.Equals(other.strike);
public override int GetHashCode() { return expiration.GetHashCode() ^ strike.GetHashCode(); }
public override int GetHashCode() =>
HashCode.Combine(expiration, strike);
public override bool Equals(object obj) {
return Equals(obj as SecurityStrike);
}
public override bool Equals(object? obj) =>
Equals(obj as SecurityStrike);
public static bool operator ==(SecurityStrike x, SecurityStrike y) {
if ((object)x == null) return (object)y == null;
return x.Equals(y);
}
public static bool operator ==(SecurityStrike x, SecurityStrike y) =>
x is null ? y is null : x.Equals(y);
public static bool operator !=(SecurityStrike x, SecurityStrike y) { return !(x == y); }
public static bool operator !=(SecurityStrike x, SecurityStrike y) =>
!(x == y);
public override string ToString() {
return String.Format("{0} {1}", expiration.ToString(), strike.ToString());
}
public override string ToString() =>
$"{expiration} {strike}";
public SecurityStrike unset_exchange() =>
new(expiration.with(exchange: Venue.Unset), strike);
public SecurityStrike unset_exchange() { return new SecurityStrike(expiration.unset_exchange(), strike); }
}
}
}
@@ -9,34 +9,26 @@ namespace mbts.analytics
[ProtoBuf.ProtoContract(Name = "VolSurfaceAdvancedSettings")]
public class VolSurfaceAdvancedSettings
{
public static VolSurfaceAdvancedSettings Default =>
new() {
number_of_cashed_saved_days = double.NaN,
precomputed_theo_handles = new(0),
theo_handle_measure = VolSurfaceEnums.GeneralChangeMeasure.Absolute,
precomputed_slide_handles = new(0),
slide_handle_measure = VolSurfaceEnums.GeneralChangeMeasure.Percentage,
publish_from_client = false,
publish_from_client_user = string.Empty,
strike_normalization_axis = VolSurfaceEnums.VolCurveStrikeAxis.RawStrike,
};
[ProtoBuf.ProtoMember(1)]
[System.ComponentModel.DefaultValue(Double.NaN)]
public double number_of_cashed_saved_days;
[ProtoBuf.ProtoMember(10)]
public List<double> precomputed_theo_handles;
[ProtoBuf.ProtoMember(15)]
[System.ComponentModel.DefaultValue(VolSurfaceEnums.GeneralChangeMeasure.Absolute)]
public VolSurfaceEnums.GeneralChangeMeasure theo_handle_measure;
[ProtoBuf.ProtoMember(20)]
public List<double> precomputed_slide_handles;
[ProtoBuf.ProtoMember(25)]
[System.ComponentModel.DefaultValue(VolSurfaceEnums.GeneralChangeMeasure.Percentage)]
public VolSurfaceEnums.GeneralChangeMeasure slide_handle_measure;
[ProtoBuf.ProtoMember(30)]
//[System.ComponentModel.DefaultValue(false)]
public bool publish_from_client;
[ProtoBuf.ProtoMember(40)]
@@ -46,11 +38,5 @@ namespace mbts.analytics
[ProtoBuf.ProtoMember(50)]
[System.ComponentModel.DefaultValue(VolSurfaceEnums.VolCurveStrikeAxis.RawStrike)]
public VolSurfaceEnums.VolCurveStrikeAxis strike_normalization_axis;
public void ensure_no_nulls() {
precomputed_theo_handles ??= new(0);
precomputed_slide_handles ??= new(0);
publish_from_client_user ??= string.Empty;
}
}
}
@@ -4,11 +4,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.analytics {
namespace mbts.analytics
{
/// These are the VolSurface settings that are common
/// to any Vol Model
[ProtoBuf.ProtoContract(Name = "VolSurfaceCoreSettings")]
public class VolSurfaceCoreSettings {
public class VolSurfaceCoreSettings
{
[ProtoBuf.ProtoMember(1)]
public string vol_surface_name;
[ProtoBuf.ProtoMember(2)]
@@ -85,7 +87,5 @@ namespace mbts.analytics {
get => _time_settings ??= new();
set => _time_settings = value;
}
[ProtoBuf.ProtoMember(240)]
public string vs_manager { get; set; } = string.Empty;
}
}
@@ -5,30 +5,6 @@ namespace mbts.analytics {
/// they all in a way "derive" from core
[ProtoContract]
public class VolSurfaceSettings {
public static VolSurfaceSettings Default(string name) => new() {
core_settings = new VolSurfaceCoreSettings {
vol_surface_name = name,
vol_surface_model = VolSurfaceEnums.VolSurfaceModel.Polynomial,
call_rotation = new() { strike_vol_movement_pairs = new() },
put_rotation = new() { strike_vol_movement_pairs = new() },
strangle = new() { strike_vol_movement_pairs = new() },
full_rotation = new() { strike_vol_movement_pairs = new() },
atm_shift = new() { strike_vol_movement_pairs = new() },
symbols = new(),
symbol_structure_list = new(),
wing_settings = new() {
explicit_slope_wing_settings = new(),
implicit_slope_wing_settings = new(),
onepoint_slope_wing_settings = new()
},
unset_expiration_calculation_settings = new(),
volpath_settings = new(),
safety_limits = new(),
},
polynomial_model_settings = new(),
advanced_settings = VolSurfaceAdvancedSettings.Default
};
[ProtoMember(10)]
public VolSurfaceCoreSettings core_settings;
[ProtoMember(15)]
@@ -76,12 +52,9 @@ namespace mbts.analytics {
if (core_settings.market_fit_settings == null) {
core_settings.market_fit_settings = new VolSurfaceMarketFitSettings();
}
core_settings.market_fit_settings.vs_auto_fit_settings ??= new();
if (core_settings.time_settings == null) {
core_settings.time_settings = new VolSurfaceTimeSettings();
}
advanced_settings ??= VolSurfaceAdvancedSettings.Default;
advanced_settings.ensure_no_nulls();
return this;
}
}
@@ -141,7 +141,6 @@ namespace mbts.analytics
public enum VolCurveFitRegion
{
None,
ATM,
All
}
@@ -231,4 +230,4 @@ namespace mbts.analytics
USE_CALENDAR_FOR_BUSINESS
}
}
}
}
@@ -1,19 +1,19 @@
using bts.api;
using bts.settings;
using bts.spark;
using bts.utils;
using mbts.spark;
using mbts.ui.converters;
using ProtoBuf;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
using bts.api;
using bts.settings;
using bts.spark;
using mbts.spark;
using mbts.ui.converters;
using bts.utils;
namespace mbts.analytics {
/// <summary>
/// An expiration in the Vol Surface is expressed
@@ -26,13 +26,7 @@ namespace mbts.analytics {
public class VolSurfaceExpiration : IEquatable<VolSurfaceExpiration>, IComparable<VolSurfaceExpiration> {
public VolSurfaceExpiration() : this(string.Empty, default(DateTime)) { }
public VolSurfaceExpiration(string symbol, DateTime expiration)
:
this(bts.api.SecurityType.Option, symbol, expiration) {
}
public VolSurfaceExpiration(bts.api.SecurityType security_type, string symbol, DateTime expiration) {
this.security_type = security_type;
public VolSurfaceExpiration(string symbol, DateTime expiration) {
this.symbol = symbol;
this.expiration = expiration;
}
@@ -42,31 +36,28 @@ namespace mbts.analytics {
expiration = exp.expiration;
}
public SecurityExpiration to_security_expiration(
Venue exchange = Venue.Unset, SecurityType type = SecurityType.Unset) =>
new(exchange, security_type, symbol, expiration);
public SecurityExpiration to_security_expiration() {
// TODO
return new SecurityExpiration(Venue.Unset, SecurityType.Option, symbol, expiration);
}
[ProtoBuf.ProtoMember(10)]
public string symbol;
[ProtoBuf.ProtoMember(20)]
public DateTime expiration;
[ProtoBuf.ProtoMember(30)]
public bts.api.SecurityType security_type = bts.api.SecurityType.Option;
public bool Equals(VolSurfaceExpiration other) {
return other != null && symbol.Equals(other.symbol) && expiration.Equals(other.expiration);
}
public override int GetHashCode() { return security_type.GetHashCode() ^ symbol.GetHashCode() ^ expiration.GetHashCode(); }
public override int GetHashCode() { return symbol.GetHashCode() ^ expiration.GetHashCode(); }
public override bool Equals(object obj) {
return Equals(obj as VolSurfaceExpiration);
}
public override string ToString() {
return String.Format("{0} {1} {2}",
security_type.ToString().ToUpper().First(), symbol, expiration.ToShortDateString());
return String.Format("{0} {1}", symbol, expiration.ToShortDateString());
}
public int CompareTo(VolSurfaceExpiration other) {
@@ -74,19 +65,18 @@ namespace mbts.analytics {
int rv = expiration.CompareTo(other.expiration);
return rv != 0 ? rv : symbol.CompareTo(other.symbol);
}
public double diff_days(VolSurfaceExpiration exp_other) {
TimeSpan ts = expiration - exp_other.expiration;
return ts.TotalDays;
}
}
public static class VolSurfaceExpirationExtensions {
public static SecurityExpiration ToSecurityExp(
this VolSurfaceExpiration vs_exp, SecurityType type) =>
new(Venue.Unset, type, vs_exp.symbol, vs_exp.expiration);
public static SecurityExpiration ToSecurityExp(this VolSurfaceExpiration vs_exp) {
var type = SecurityType.Unset;
if (!string.IsNullOrWhiteSpace(vs_exp.symbol)) {
if (Char.IsLower(vs_exp.symbol.First())) type = SecurityType.Option;
else if (Char.IsUpper(vs_exp.symbol.First())) type = SecurityType.Future;
}
return new SecurityExpiration(Venue.Unset, type, vs_exp.symbol, vs_exp.expiration);
}
}
public class VolSurfaceStrikeMap {
public readonly IDictionary<VolSurfaceExpiration, IEnumerable<double>> strike_map = null;
public VolSurfaceStrikeMap(IEnumerable<string> option_names, SystemContractData sc_data) {
Loaded 100 of 1689 files, more files were not shown because too many files have changed in this diff. Show more