Compare commits

...
Author SHA1 Message Date
Michael Harding 883272b05b Merged master into SP-54-pnl 2020-01-24 09:17:51 -05:00
Michael Harding a5c37712cf SP-54: Fix sort direction 2020-01-23 17:38:35 -05:00
Michael Harding fd5ea2f864 SP-54: Apply filters after md change 2020-01-23 16:49:04 -05:00
Michael Harding 415d03a156 SP-54: Save sorting 2020-01-22 15:53:56 -05:00
Michael Harding 36eadea650 SP-54: colorful text 2020-01-22 15:13:46 -05:00
Michael Harding 7d45de95f6 SP-54: Save Filters 2020-01-22 13:29:12 -05:00
Michael Harding 03b7031af2 Safer number fields in Spark admin 2020-01-21 15:07:17 -05:00
Michael Harding 368e07ede4 SP-54: Save Column settings to PersistenceService 2020-01-17 12:44:41 -05:00
Michael Harding ce7f347256 Add eric's unsub hack 2020-01-14 15:09:09 -05:00
Michael Harding 344c201a94 SP-54: Clear filters and ui refinement 2020-01-14 12:20:48 -05:00
Michael Harding e9a4edda41 SP-54: filterbox and header style 2020-01-13 16:36:27 -05:00
Michael Harding 1c92171165 Merged master into SP-54-pnl 2020-01-10 11:11:52 -05:00
Michael Harding 240e2e57bf Merged master into SP-54-pnl 2020-01-03 14:59:09 -05:00
Michael Harding 24d68f28da SP-54: PnL filters 2020-01-03 14:57:47 -05:00
Michael Harding 2eb8f4f1e0 fix local host port issue 2020-01-03 14:57:47 -05:00
Kevin Darby 23cb40f83a update dev keycodes to 3/31/2020 2020-01-03 14:57:47 -05:00
Taha Afzal 9ee3eca0ba pos neutral spreads's fuures are not checked for pos limits 2020-01-03 14:57:47 -05:00
John Colvin c84961452b infinity ticks be gone 2020-01-03 14:57:47 -05:00
John Colvin 202e08f3d0 remove ugly border around expander buttons in grid settings dialog 2020-01-03 14:57:47 -05:00
Kevin Darby 2e0780f5e8 downgrade log 2020-01-03 14:57:47 -05:00
John Colvin 43f4b33366 fix synthetic import qty & side 2020-01-03 14:57:47 -05:00
Jake Norbut 4bcc73ece9 updating release notes 2020-01-03 14:57:47 -05:00
John Colvin 015ed0849c added create/clone synthetics test 2020-01-03 14:57:47 -05:00
Pedro Pinto 79e0713349 Skipped lines that do no match a FIX prefix in FIX logs.
This is needed at the ICE where some messages can have new lines. We
use the new line separator to separate FIX messages so the parser was
getting confused when it saw multi-line messages.
2020-01-03 14:57:47 -05:00
Kevin Darby 7785bd1538 we are trying to log in for a deleted user 2020-01-03 14:57:47 -05:00
Kevin Darby 2dff9799bc build 2020-01-03 14:57:47 -05:00
Kevin Darby 1f794c9f80 ice login 2020-01-03 14:57:47 -05:00
Taha Afzal a8d2df01ca Reports transition to end state right away (vs on a timer) 2020-01-03 14:57:47 -05:00
John Colvin 5ceeadf01e SyntheticSecurityParser trims whitespace from leg type text 2020-01-03 14:57:47 -05:00
John Colvin 31baa56223 DeleteSyntheticDaemon - improve logging, support batch delete 2020-01-03 14:57:47 -05:00
John Colvin 268f8a16a7 DiscretePropertyChangeDetector uses generic/strong types rather than boxing to object 2020-01-03 14:57:47 -05:00
John Colvin 3232f010b9 SP-273 Fills are unselected when order tree changes 2020-01-03 14:57:47 -05:00
John Colvin 251a835411 FillsVM state doesn't need to be observable (every usage is equiv to Observable.Return) 2020-01-03 14:57:46 -05:00
John Colvin e815cbcdaa ExecutionStrategyEditor 'Remove Leg' tooltip 2020-01-03 14:57:46 -05:00
John Colvin 5571d73365 SP-271 Synthetic cloning 2020-01-03 14:57:46 -05:00
John Colvin e70e3d2a2f SP-270 Order Book - PIQ Bug (fix OrderSecurityAlgo.Equals impl) 2020-01-03 14:57:46 -05:00
Jake Norbut 96016d427d formatting 2020-01-03 14:57:46 -05:00
Jake Norbut 8c9ab5743c hooking up message Visibility to new vm property 2020-01-03 14:57:46 -05:00
Jake Norbut ca3bb8b99b adding status message visibility property to ladder vm 2020-01-03 14:57:46 -05:00
Jake Norbut 2e45216e24 adding status message visibility to setting vm 2020-01-03 14:57:46 -05:00
Jake Norbut 8d1761c5d8 adding status_message_visible to settings 2020-01-03 14:57:46 -05:00
John Colvin 32b2b8a702 Import Synthetics manual test 2020-01-03 14:57:46 -05:00
John Colvin 76a489cc67 SP-266 synthetic csv parser/import dialog 2020-01-03 14:57:46 -05:00
John Colvin 81488824db INotifyDataErrorInfo 'subscribe_has_errors' extension only notifies observers when HasErrors changes 2020-01-03 14:57:45 -05:00
John Colvin c47575067f EnumHelper - generic add/remove_flags methods 2020-01-03 14:57:45 -05:00
John Colvin 26a4f5d2ed ArrayHelper.trim was not actually reassigning ref with trimmed result 2020-01-03 14:57:45 -05:00
John Colvin 1bbe4fcbdf TickFactorsPicker - disable auto complete behavior 2020-01-03 14:57:45 -05:00
John Colvin 3859528670 MDDaemon - check that synthetic metadata.HasStrategy before adding to map 2020-01-03 14:57:45 -05:00
John Colvin d212383f56 DIYGridRowPresenter - don't perform drag selection when SelectionMode=Single 2020-01-03 14:57:45 -05:00
John Colvin dad7a65e92 add WindowMaxSizeConverter to implement 'SizeToContent' max dimensions 2020-01-03 14:57:45 -05:00
Michael Harding acb2d1dfc5 Merged master into SP-54-pnl 2019-12-17 15:55:27 -05:00
Michael Harding d8d72d2052 SP-54: Sort fixes 2019-12-10 13:39:20 -05:00
Michael Harding f1b50b4160 SP-54: Column sorting 2019-12-10 13:25:45 -05:00
Michael Harding 6a90e71efe SP-54: Show/Hide PnL columns 2019-12-09 10:15:31 -05:00
Michael Harding c98144daa0 Hide PnL columns 2019-12-06 10:37:28 -05:00
Michael Harding a08eb90b1a Add security to the PnL form and make table lazy 2019-12-03 14:58:07 -05:00
Michael Harding 215740a011 Merged master into SP-54-pnl 2019-11-25 15:04:52 -05:00
Michael Harding 3bf6e81a4d Subscribe to and display PnL data 2019-11-22 15:21:19 -05:00
Michael Harding 0112bdbc3d Clean up warnings in Spark Admin 2019-11-14 14:53:31 -05:00
Michael Harding 547e06c33f SP-54: Add Reports Module 2019-11-12 14:55:36 -05:00
Michael Harding 35d53391da Update Copyright in About 2019-11-12 14:53:40 -05:00
22 changed files with 1256 additions and 82 deletions

No files matched your search

+1
View File
@@ -33,4 +33,5 @@ trace_data/
/src/lib/orderman/perf_analysis/log.csv
/doc/user/public/*
.ionide/*
*.ionide
/.ignore
+4 -4
View File
@@ -1,8 +1,8 @@
<Window
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Width="400" Height="265"
xmlns:ui="clr-namespace:mbts.ui"
xmlns:mbts="clr-namespace:mbts.ui" WindowStartupLocation="CenterScreen"
@@ -25,7 +25,7 @@
<Hyperlink Command="ApplicationCommands.Open" CommandParameter="/doc/manual/index.html">Manual</Hyperlink-->
<Hyperlink Command="ApplicationCommands.Open" CommandParameter="https://www.bluetradingsystems.com">www.bluetradingsystems.com</Hyperlink>
</TextBlock>
<TextBlock Grid.ColumnSpan="2" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,10">&#169; Blue Trading Systems 2013 - 2018</TextBlock>
<TextBlock Grid.ColumnSpan="2" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,10">&#169; Blue Trading Systems</TextBlock>
<Button HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.ColumnSpan="2" Margin="10" IsDefault="True" Command="ApplicationCommands.Close" MinWidth="60">OK</Button>
</Grid>
</Window>
+7 -7
View File
@@ -105,7 +105,7 @@ KeyModel = namedtuple("KeyModel", "type field")
DefaultKeyModel = KeyModel("int", "id")
def transform_service(service, keymap):
return ServiceModel(
service.name,
service.name,
option(service.options, apioptions_pb2.service_description, ""),
[transform_method(m, keymap.get(m.output_type, DefaultKeyModel)) for m in service.method],
service.name.lower())
@@ -158,7 +158,7 @@ def to_type_model(field, msg):
elif field.type == types.TYPE_DOUBLE: return TypeModel("double", islist, "double?", jstype='number')
elif field.type == types.TYPE_FLOAT: return TypeModel("float", islist, "float?", jstype='number', fstype='float32')
elif field.type == types.TYPE_STRING: return TypeModel("string", islist, cpptype="std::string")
elif field.type == types.TYPE_BYTES: return TypeModel("ByteString", islist, cpptype="std::string", jstype='Uint8Array', fstype='Fable.Import.JS.Uint8Array')
elif field.type == types.TYPE_BYTES: return TypeModel("ByteString", islist, cpptype="std::string", jstype='Uint8Array', fstype='uint8[]')
elif field.type == types.TYPE_BOOL: return TypeModel("bool", islist, "bool?", jstype='boolean', natural_key=True)
elif field.type == types.TYPE_ENUM:
nested_enums = [x.name for x in msg.enum_type]
@@ -185,8 +185,8 @@ def to_type_model(field, msg):
map_value = to_type_model([f for f in t.field if f.name == 'value'][0], msg)
itype = map_value.cstype+immutable_postfix
return TypeModel(
map_value.cstype,
cpptype=map_value.cpptype,
map_value.cstype,
cpptype=map_value.cpptype,
jstype=map_value.jstype,
fstype=map_value.fstype,
isMessage=True,
@@ -234,7 +234,7 @@ def enum_short_name(enum_name, entry):
return camel_case(entry[len(enum_name)+1:])
return camel_case(entry)
def camel_case(item):
def camel_case(item):
if item.isupper() and not '_' in item: return item
return item.title().replace('_', '')
return d
@@ -307,7 +307,7 @@ def generate_code(request, response):
#### MAIN #########
if __name__ == "__main__":
if DEBUG:
if DEBUG:
data = open("data", mode='rb').read()
else:
data = sys.stdin.read()
@@ -317,7 +317,7 @@ if __name__ == "__main__":
response = plugin.CodeGeneratorResponse()
generate_code(request, response)
if DEBUG:
if DEBUG:
for f in response.file:
print f.content
else:
@@ -6,12 +6,12 @@ open google.protobuf
{{#each enum_type}}
[<StringEnum>]
type {{name}} =
type {{name}} =
{{#each values}}
| [<CompiledName("{{name}}")>] {{short_name}}
{{/each}}
module {{name}} =
let values =
let values =
[|
{{#each values}}
{{../name}}.{{short_name}}
@@ -51,7 +51,7 @@ type {{name}} =
module {{name}} =
{{#each nested_enum}}
[<StringEnum>]
type {{name}} =
type {{name}} =
{{#each values}}
| [<CompiledName("{{name}}")>] {{short_name}}
{{/each}}
@@ -95,7 +95,7 @@ module {{name}} =
"{{name}}", {{@index}}., "{{output_type_full}}"
{{/if}}
{{/each}}
|]
|]
)
|> unbox
+1 -1
View File
@@ -31,7 +31,7 @@ function gen_method(service:IService, method_id : number, request_type : string,
assertValid(request_proto, r)
const buffer = request_proto.encode(r).finish()
const result = await service.context.call(service.name, service.qualifier, method_id, buffer)
let temp = result_proto.decode(result)
// let temp = result_proto.decode(result)
// There is a built in library function do do this but it doesn't work recursively
// https://github.com/dcodeIO/protobuf.js/issues/1041
// result_proto.toObject(temp, { enums: String, default: true })
+9 -9
View File
@@ -4,19 +4,19 @@ import { assert, assertValid } from 'bts_assert'
function default_ws_uri() {
if (typeof window == 'undefined') return 'ws://sparkvm:8000/'
const loc = window.location
if (loc.hostname === "localhost" && loc.port === '8000') {
if (loc.hostname === "localhost" && loc.port === '8080') {
return "ws://sparkvm:8000/"
}
let ws_uri = "ws:";
if (loc.protocol === "https:") {
ws_uri = "wss:";
}
}
ws_uri += "//" + loc.host;
ws_uri += loc.pathname + "/";
return ws_uri;
}
// Message types
// Message types
const GatewayLogin = root.lookupType("bts.api.gateway.GatewayLogin")
const GatewayRequest = root.lookupType("bts.api.gateway.GatewayRequest")
const GatewayResponse = root.lookupType("bts.api.gateway.GatewayResponse")
@@ -105,7 +105,7 @@ class PrivateContext {
// },3000);
}
onmessage(e){
const raw_buffer = new Uint8Array(e.data)
const raw_buffer = new Uint8Array(e.data)
const response = GatewayResponse.decode(raw_buffer)
if (response && response.callResult && response.callResult.length > 0) {
const call_buffer = response.callResult
@@ -176,7 +176,7 @@ export class ServiceContext {
});
}
subscribe(serviceType: string, serviceQualifier:string,
subscribe(serviceType: string, serviceQualifier:string,
methodId: number, methodName : string,
streamQualifier : string, callback: (x:RawUpdate[])=>void) : number {
// Send a subscription request to the server
@@ -209,7 +209,7 @@ export class ServiceContext {
unsubscribe(subscriptionId : number, serviceType : string, serviceQualifier : string) {
let req = GatewayUnsubscribe.create({subscriptionId, serviceType, serviceQualifier});
let message = GatewayRequest.create({
unsubscribe: req
unsubscribe: req
});
this.send(message);
}
@@ -239,10 +239,10 @@ export class ServiceContext {
if (update.id && this.subscriptions) {
const s = this.subscriptions[update.id];
if (s) {
console.log("on tick");
console.log(update);
// console.log("on tick");
// console.log(update);
assert(update.id == s.id);
s.callback(update.updates);
s.callback(update.updates);
}
}
}
+39
View File
@@ -108,6 +108,8 @@ body {
}
.is-launcher-sidebar-menu {
position:relative;
height:100vh;
padding: 0;
background: $primary;
max-width: 7rem;
@@ -123,6 +125,11 @@ body {
}
}
.logout-icon {
position:absolute;
bottom:0;
}
.launcher-item {
text-align:center;
width:100%;
@@ -376,3 +383,35 @@ body {
.is-100-width {
width: 100px
}
.filter-card {
position:absolute;
top:10px;
left:10px;
width: 13em;
.delete {
float:right;
margin: -10px -10px 0 0;
}
}
.clear-background {
background: inherit;
}
.table-header .header-icon-hidden {
visibility: hidden;
}
.table-header:hover .header-icon-hidden {
visibility: visible;
}
.yellow {
color:yellow;
}
.header-filter-icon {
margin-left:.5ex;
}
+14 -16
View File
@@ -123,13 +123,13 @@ let init (context:ServiceContext.ServiceContext) =
this_username = context.username
ShowLoading = false
ManualFillData = {
fills = { manualFill = (Array.empty: bts.api.position.ManualFill [])}
newFills = list.Empty
updatedFills = list.Empty
deletedFills = { manualFill = (Array.empty: bts.api.position.ManualFill [])}
tempSODFillData = emptyTempManualFill
tempDayFillData = emptyTempManualFill
}
fills = { manualFill = (Array.empty: bts.api.position.ManualFill [])}
newFills = list.Empty
updatedFills = list.Empty
deletedFills = { manualFill = (Array.empty: bts.api.position.ManualFill [])}
tempSODFillData = emptyTempManualFill
tempDayFillData = emptyTempManualFill
}
}, Access.getAdminData admin UpdateAdminData NoAdminData
let positionSub model dispatch =
@@ -182,7 +182,6 @@ let selectSub (tab:Tab option) model =
match model.unsub with
| Some (unsub) -> unsub.Dispose()
| None ->
System.Console.WriteLine "...?"
()
Cmd.none
@@ -409,14 +408,13 @@ let update msg (model:Model) : Model * Cmd<Msg> =
| SelectAccount account ->
{ (clearSelection model) with previousScratch = model.scratch
scratch = account
ManualFillData = {
fills = { manualFill = (Array.empty: bts.api.position.ManualFill [])}
newFills = list.Empty
updatedFills = list.Empty
deletedFills = { manualFill = (Array.empty: bts.api.position.ManualFill [])}
tempSODFillData = emptyTempManualFill
tempDayFillData = emptyTempManualFill
}
ManualFillData = { fills = { manualFill = (Array.empty: bts.api.position.ManualFill [])}
newFills = list.Empty
updatedFills = list.Empty
deletedFills = { manualFill = (Array.empty: bts.api.position.ManualFill [])}
tempSODFillData = emptyTempManualFill
tempDayFillData = emptyTempManualFill
}
marginScratch = emptyMargin
}, selectSub None model
| GetMarginData ->
+16 -15
View File
@@ -287,12 +287,12 @@ let clipLimitsView model dispatch =
isHeader = false
colSpan = 5
}
TableControl.Cell (Fields.text
TableControl.Cell (Fields.uncheckedUint32
""
(sprintf "%i" (match (List.tryHead model.SelectedClipLimits) with
| Some r -> r.clipSize
| None -> 0u
))
((match (List.tryHead model.SelectedClipLimits) with
| Some r -> r.clipSize
| None -> 0u
))
[ ]
((fun value -> (value, (Array.ofList model.SelectedClipLimits))) >> ChangeMultipleClipLimits >> dispatch)
)
@@ -467,21 +467,21 @@ let productLimits model dispatch =
isHeader = false
colSpan = 5
}
TableControl.Cell (Fields.text
TableControl.Cell (Fields.uncheckedUint32
""
(sprintf "%i" (match (List.tryHead model.SelectedPositionLimits) with
| Some r -> r.longPositionLimit
| None -> 0u
))
((match (List.tryHead model.SelectedPositionLimits) with
| Some r -> r.longPositionLimit
| None -> 0u
))
[ ]
((fun value -> ("long", value, (Array.ofList model.SelectedPositionLimits))) >> ChangeMultipleProductLimits >> dispatch)
)
TableControl.Cell (Fields.text
TableControl.Cell (Fields.uncheckedUint32
""
(sprintf "%i" (match (List.tryHead model.SelectedPositionLimits) with
| Some r -> r.shortPositionLimit
| None -> 0u
))
( (match (List.tryHead model.SelectedPositionLimits) with
| Some r -> r.shortPositionLimit
| None -> 0u
))
[ ]
((fun value -> ("short", value, (Array.ofList model.SelectedPositionLimits))) >> ChangeMultipleProductLimits >> dispatch)
)
@@ -789,6 +789,7 @@ let venueToString (venue:bts.api.Venue) =
| bts.api.Venue.CBOECFE -> "CBOECFE"
| bts.api.Venue.GLBX -> "GLBX"
| bts.api.Venue.ICE -> "ICE"
| bts.api.Venue.SIMICE -> "SIMICE"
let exchange_display expirationNameList =
let s = sprintf "%O" expirationNameList.product.exchange
+3 -2
View File
@@ -34,6 +34,7 @@ let root model dispatch =
match model.page with
| UsersModel m -> Users.View.root m (UsersMsg>>dispatch) |> topLevel model.context model.page dispatch
| AccountsModel m -> Accounts.View.root m (AccountsMsg>>dispatch) |> topLevel model.context model.page dispatch
| ReportsModel m -> Reports.View.root m (ReportsMsg>>dispatch) |> topLevel model.context model.page dispatch
open Elmish.React
open Elmish.Debug
@@ -42,8 +43,8 @@ open Elmish.HMR
// App
Program.mkProgram init update root
#if DEBUG
|> Program.withConsoleTrace
|> Program.withDebugger
//|> Program.withConsoleTrace
//|> Program.withDebugger
|> Program.withHMR
#endif
|> Program.withReactUnoptimized "elmish-app"
+31 -8
View File
@@ -31,7 +31,22 @@ let text (lbl:string) (dv:string) (attrs:IHTMLProp list) onchange =
ClassName "input"
Type "text"
Value dv
OnChange (fun ev -> !!ev.target?value |> onchange)
OnChange (fun ev -> !!ev.target?value |> string |> onchange)
] @ attrs)
]
]
let dvText (lbl:string) (dv:string) (attrs:IHTMLProp list) onchange =
// floating point numbers require using DefaultValue instead of Value
div [ ClassName "field" ]
[
Label lbl
div [ ClassName "control" ]
[ input ([
ClassName "input"
Type "text"
DefaultValue dv
OnChange (fun ev -> !!ev.target?value |> string |> onchange)
] @ attrs)
]
]
@@ -150,7 +165,7 @@ let password (lbl:string) (dv:string) (attrs:IHTMLProp list) onchange =
] @ attrs)
]
]
let number (lbl:string) (dv:string) (attrs:IHTMLProp list) (validate: string -> bool) onchange =
let errorClass = match dv |> validate with
@@ -180,17 +195,25 @@ let positiveIntNumber (lbl:string) (dv:string) (attrs:IHTMLProp list) onchange =
let validate value =
let itp = System.Int32.TryParse value
fst itp && snd itp >= 0
number lbl dv attrs validate onchange
let number_novalidate (lbl:string) (dv) (attrs:IHTMLProp list) onchange =
text lbl (string dv) attrs (unbox>>onchange)
let uncheckedInt (lbl:string) (dv:int) (attrs:IHTMLProp list) onchange =
text lbl (string dv) attrs (int>>onchange)
let double (lbl:string) (dv:double) (attrs:IHTMLProp list) onchange =
text lbl (string dv) attrs (double>>onchange)
let uncheckedUint32 (lbl:string) (dv:uint32) (attrs:IHTMLProp list) onchange =
text lbl (string dv) attrs (uint32>>onchange)
let uncheckedInt64 (lbl:string) (dv:int64) (attrs:IHTMLProp list) onchange =
text lbl (string dv) attrs (int64>>onchange)
let uncheckedDouble (lbl:string) (dv:double) (attrs:IHTMLProp list) onchange =
match dv with
| System.Double.MinValue | System.Double.MaxValue ->
dvText lbl "" attrs (double>>onchange)
| _ ->
dvText lbl (string dv) attrs (double>>onchange)
let enumSelect<'a> (lbl:string) (dv:'a) (values:'a[]) (attrs:IHTMLProp list) (onchange:'a->unit) =
let vals = match (Array.tryFindIndex (fun elm -> unbox elm = unbox dv) values) with
+23 -1
View File
@@ -109,4 +109,26 @@ let createSelectorTableWithStyle (headers:row option) (rl:row list) className st
If a callback is provided add a checkbox as the first column and set a background color if it is checked... in addition to the callback
*)
let createSelectorTable (headers:row option) (rl:row list) = createSelectorTableWithStyle headers rl "" []
let createSelectorTable (headers:row option) (rl:row list) = createSelectorTableWithStyle headers rl "" []
let showFilter (content:ReactElement) (onApplyClick: MouseEvent -> Unit) (onClearClick: MouseEvent -> Unit) (onCloseClick: MouseEvent -> Unit) top left =
div [][
div [ Class "modal-background clear-background"; OnClick onCloseClick ][ ]
div [ Class "card filter-card"; Style [ Top top; Left left ] ]
[ div [ Class "card-content" ]
[ content
]
footer [ Class "card-footer" ]
[ p [ Class "card-footer-item"; OnClick onApplyClick ]
[ span [ ]
[ a [ ]
[ str "Apply" ] ] ]
p [ Class "card-footer-item"; OnClick onClearClick ]
[ span [ ]
[ a [ ]
[ str "Clear" ] ] ] ] ]
]
+18
View File
@@ -0,0 +1,18 @@
module Unsub
open System
open Fable.Helpers.React
open Fable.Helpers.React.Props
open Fable.Import.React
// This is kind of a hack...
// Use the react lifecycle methods to implement a deterministric dispose of a subscription
type Unsubscribe(props) =
inherit Component<IDisposable,unit>(props)
override this.componentWillUnmount() =
printfn "Disposing subscription"
this.props.Dispose()
override this.render() =
null
let unsub props =
ofType<Unsubscribe,IDisposable,unit> props []
+10 -3
View File
@@ -24,6 +24,7 @@ let isCurrentPage page currentPage =
match page, currentPage with
| UsersModel _, UsersModel _ -> true
| AccountsModel _, AccountsModel _ -> true
| ReportsModel _, ReportsModel _ -> true
| _ -> false
let navItem label icon state currentPage dispatch =
@@ -48,16 +49,22 @@ let root (currentPage:Page) (sc:ServiceContext) dispatch =
li [ Class "launcher-item" ]
[ navItem "Accounts" "server" (tmap (Accounts.State.init sc) (AccountsModel,Cmd.map AccountsMsg)) currentPage dispatch ]
li [ Class "launcher-item" ]
[ navItem "Reports" "line-chart" (tmap (Reports.State.init sc) (ReportsModel,Cmd.map ReportsMsg)) currentPage dispatch ]
li [ Class "launcher-item logout-icon" ]
[ a [ Href "/" ]
[ div [ ClassName "icon is-large" ] [
i [ ClassName "fa fa-3x fa-sign-out" ] []
i [ ClassName "fa fa-2x fa-sign-out" ] []
]
div [] [str ("Logout \n" + sc.username)]
div [] [
str "Logout"
br []
str sc.username
]
]
]
]
]
]
let (navRoot: Page -> ServiceContext -> Dispatch<Msg> -> ReactElement) =
let (navRoot: Page -> ServiceContext -> Dispatch<Msg> -> ReactElement) =
lazyView3With (fun b c -> isCurrentPage (fst b) (fst c)) root
+691
View File
@@ -0,0 +1,691 @@
module Reports.State
open Elmish
open Types
open JSMap
open System
open System.Runtime.InteropServices
open Fable.Import
open Fable.Core
open Fable.Core.JsInterop
open System.Xml
open Fable.PowerPack.PromiseImpl
open bts.api.admin
open bts.api.position
open bts.api.persistence
open bts.co2
open Global
let getLoggedInID model =
match model.adminData with
| Some adata -> (Array.find (fun (usr:BTSUser) -> usr.name = model.this_username) adata.users).id
| None -> 0u
let venueToString (venue:bts.api.Venue) =
match venue with
| bts.api.Venue.UNSET -> "Unset"
| bts.api.Venue.BTS -> "BTS"
| bts.api.Venue.SIM -> "SIM"
| bts.api.Venue.CBOE -> "CBOE"
| bts.api.Venue.CBOECFE -> "CBOECFE"
| bts.api.Venue.GLBX -> "GLBX"
| bts.api.Venue.ICE -> "ICE"
| bts.api.Venue.SIMICE -> "SIMICE"
let securityTypeToString (venue:bts.api.SecurityType) =
match venue with
| bts.api.SecurityType.UNSET -> "Unset"
| bts.api.SecurityType.STOCK -> "Stock"
| bts.api.SecurityType.OPTION -> "Option"
| bts.api.SecurityType.FUTURE -> "Future"
| bts.api.SecurityType.SPREAD -> "Spread"
| bts.api.SecurityType.SYNTHETIC -> "Synthetic"
let getAdminData (admin:IAdminService) (token:string) =
Cmd.ofPromise
admin.get_filtered_admin_data ()
UpdateAdminData
(fun _ -> RpcResponse "Error getting admin data")
let getPersistenceData model =
let nameSpace = sprintf "%i%s" (model |> getLoggedInID) model.persisenceBaseNamespace
Cmd.ofPromise
model.persistenceService.retrieve {ns = nameSpace ; key = [|"pnlColumns"; "pnlFilterValues"; "pnlSorting" |]}
UpdatePersistenceData
(fun _ -> RpcResponse "Error getting Persistence data")
let PnLSub model dispatch =
match model.unsub with
| Some (unsub) -> unsub.Dispose()
| None -> ()
let svc = model.positionService
svc.subscribe_pnl_data "" (
fun (subUpdate:StreamUtils.IUpdate<bts.api.position.PNLUnit>) ->
match subUpdate.added |> isEmpty, subUpdate.changed |> isEmpty with
| true, true -> ()
| _, _ ->
subUpdate.all
|> JSMap.toArray
|> Array.map snd
|> SetPositionData
|> dispatch
)
|> SetUnsub
|> dispatch
let selectSub (tab:Tab option) model =
match tab, model.currentTab with
| Some PnL, _ | None, PnL -> Cmd.ofSub (PnLSub model)
// | _ ->
// match model.unsub with
// | Some (unsub) -> unsub.Dispose()
// | None ->
// ()
// Cmd.none
let requestProducts (mdRelay:IMarketDataRelay) = promise {
let! result = mdRelay.request_products()
return UpdateProducts result.products
}
let requestExpNames (mdRelay:IMarketDataRelay) = promise {
let! result = mdRelay.request_names()
return UpdateExpirationNames result.securities
}
let encodeUint8 (source:Object) =
[| for ch in (toJson source) -> (uint8 ch) |]
let inline decodeUint8<'a> (source:uint8 []) =
[| for ch in (source) -> (char ch) |]
|> String
|> ofJson<'a>
let saveSettings (persServ:IPersistenceService) (model:Model) = promise {
let! result = persServ.store
{ ns = sprintf "%i%s" (getLoggedInID model) model.persisenceBaseNamespace
record = [|
{ key = "pnlColumns"
value = (model.PnLColumns |> encodeUint8) }
{ key = "pnlFilterValues"
value = (model.PnLFilterValues |> encodeUint8) }
{ key = "pnlSorting"
value = (model.PnLSortDirection |> encodeUint8) }
|]
}
match result.result with
| StoreResponse.Result.OK ->
return PersistenceUpdateSuccess result
| _ ->
return ErrorOccured (sprintf "Error saving settings" )
}
let defaultPnLColumns = [
("Account", true)
("Symbol", false)
("Exchange", false)
("Type", true)
("Security", true)
("Security Key", false)
("Total PnL", false)
("Open PnL", false)
("Day PnL", true)
("Total Pos", false)
("Total Long", false)
("Total Long Avg", false)
("Total Short", false)
("Total Short Avg", false)
("Open Pos", false)
("Open Long", false)
("Open Long Avg", false)
("Open Short", false)
("Open Short Avg", false)
("Day Pos", true)
("Day Long", false)
("Day Long Avg", false)
("Day Short", false)
("Day Short Avg", false)
("Raw Day Long", false)
("Raw Day Short", false)
("Raw Day Pos", false)
("Traded Today", true)
("Bid", false)
("Ask", false)
("Mid", false)
("Last", false)
("Settlement", false)
("CPP", false)]
let getSec (securityList:Map<uint32, PositionSecurity>) ndx =
match securityList.Count with
| 0 -> {
securityName = ""
venue = bts.api.Venue.UNSET
symbol = ""
securityType = bts.api.SecurityType.UNSET
}
| _ -> securityList.[ndx]
let init (context:ServiceContext.ServiceContext) : Model * Cmd<Msg> =
let admin = AdminService.Create context ""
let posServ = PositionService.Create context ""
let basePosServ = BasePositionService.Create context ""
let persServ = PersistenceService.Create context "settings"
let mdr = MarketDataRelay.Create context ""
{
currentTab = Tab.PnL
admin = admin
adminData = None
positionData = [||]
basePositionService = basePosServ
positionService = posServ
persistenceService = persServ
persisenceBaseNamespace = "#admin_ui"
unsub = None
ConfirmModal = "", (Msg.SwitchTab Tab.PnL)
token = context.token
this_username = context.username
mdRelay = mdr
mdRelayExpNameData = Array.empty<ExpirationNameList>
mdRelayProductData = [||]
securityList = Map.empty
PnLColumns = defaultPnLColumns
PnLSortFun = (fun (left:PNLUnit) (right:PNLUnit) -> (left.pos.securityId - right.pos.securityId) |> int)
PnLSortDirection = "Security Key", true
PnLFilter = Map.empty
PnLFilterValues = Map.empty
FilterOpen = false, Location (0.0,0.0)
TempFilter = "", Empty
GridSettingsOpen = false
}, getAdminData admin context.token
let calcPnL (bid:double) (ask:double) (longPrice:double) (shortPrice:double) longPos shortPos (cashValue:double) =
match bid, ask with
| 0.0, _ | _,0.0 -> 0.0
| _ ,_ ->
let mid = (bid + ask) / 2.0
let longDelta:double = mid - longPrice
let shortDelta:double = shortPrice - mid
(
(
(longDelta * longPos)
+ ( shortDelta * shortPos)
)
* cashValue
)
let calcTotalPnL data =
(calcPnL
data.bidPrice
data.askPrice
data.pos.sod.longAvgPrice
data.pos.sod.shortAvgPrice
data.pos.sod.longPos
data.pos.sod.shortPos
data.cashValue)
+ (calcPnL
data.bidPrice
data.askPrice
data.pos.day.longAvgPrice
data.pos.day.shortAvgPrice
data.pos.day.longPos
data.pos.day.shortPos
data.cashValue)
let calcSODPnL data =
(calcPnL
data.bidPrice
data.askPrice
data.pos.sod.longAvgPrice
data.pos.sod.shortAvgPrice
data.pos.sod.longPos
data.pos.sod.shortPos
data.cashValue)
let calcDayPnL data =
calcPnL
data.bidPrice
data.askPrice
data.pos.day.longAvgPrice
data.pos.day.shortAvgPrice
data.pos.day.longPos
data.pos.day.shortPos
data.cashValue
let getPnLFilterType header =
match header = "Symbol" || header = "Account"|| header = "Exchange" || header = "Type" || header = "Security" || header = "Traded Today" with
| true -> StringSearch ""
| false -> NumberRange (Double.MinValue, Double.MaxValue)
let getValFromPNLUnit header (data:PNLUnit) (securityList: Map<uint32, PositionSecurity> option) =
let sec = match securityList with
| Some secL -> getSec <| secL <| uint32 data.pos.securityId
| None -> { securityName = ""; venue = bts.api.Venue.UNSET; symbol = ""; securityType = bts.api.SecurityType.UNSET}
match header with
| "Symbol" -> sec.symbol, 0.0
| "Account" -> data.pos.account, 0.0
| "Exchange" -> venueToString sec.venue, 0.0
| "Type" -> securityTypeToString <| sec.securityType, 0.0
| "Security" -> sec.securityName, 0.0
| "Security Key" -> "", data.pos.securityId |> double
| "Total PnL" -> "", calcTotalPnL data
| "Open PnL" -> "", calcSODPnL data
| "Day PnL" -> "", calcDayPnL data
| "Total Pos" -> "", ((data.pos.day.longPos + data.pos.sod.longPos) - (data.pos.day.shortPos + data.pos.sod.shortPos))
| "Total Long" -> "", (data.pos.day.longPos + data.pos.sod.longPos)
| "Total Long Avg" -> "", ((data.pos.day.longAvgPrice + data.pos.sod.longAvgPrice) / 2.0 )
| "Total Short" -> "", data.pos.day.shortPos + data.pos.sod.shortPos
| "Total Short Avg" -> "", (data.pos.day.shortAvgPrice + data.pos.sod.shortAvgPrice) / 2.0
| "Open Pos" -> "", data.pos.sod.longPos - data.pos.sod.shortPos
| "Open Long" -> "", data.pos.sod.longPos
| "Open Long Avg" -> "", data.pos.sod.longAvgPrice
| "Open Short" -> "", data.pos.sod.shortPos
| "Open Short Avg" -> "", data.pos.sod.shortAvgPrice
| "Day Pos" -> "", data.pos.day.longPos - data.pos.day.shortPos
| "Day Long" -> "", data.pos.day.longPos
| "Day Long Avg" -> "", data.pos.day.longAvgPrice
| "Day Short" -> "", data.pos.day.shortPos
| "Day Short Avg" -> "", data.pos.day.shortAvgPrice
| "Raw Day Long" -> "", data.pos.rawDay.longPos
| "Raw Day Short" -> "", data.pos.rawDay.shortPos
| "Raw Day Pos" -> "", data.pos.rawDay.longPos - data.pos.rawDay.shortPos
| "Traded Today" -> (if (data.pos.day.longPos > 0.0) || (data.pos.day.shortPos > 0.0) then "yes" else "no"), 0.0
| "Bid" -> "", data.bidPrice
| "Ask" -> "", data.askPrice
| "Mid" -> "", (data.askPrice + data.bidPrice) / 2.0
| "Last" -> "", data.lastPrice
| "Settlement" -> "", data.settlementPrice
| "CPP" -> "", data.cashValue
| _ -> "", 0.0
let filterIsDefault filter =
match filter with
| StringSearch s -> s = ""
| NumberRange (min, max) -> min = Double.MinValue && max = Double.MaxValue
| Empty -> true
let update msg (model:Reports.Types.Model) : Model * Cmd<Msg> =
match msg with
| UpdateAdminData adm ->
let newModel =
{ model with
adminData = Some adm
}
newModel, Cmd.batch [
selectSub (Some PnL) model
Cmd.ofExistingPromise (requestProducts model.mdRelay) id (box>>ErrorOccured)
getPersistenceData newModel
]
| SaveSettings ->
model, Cmd.ofExistingPromise (saveSettings model.persistenceService model) id (box>>ErrorOccured)
| PersistenceUpdateSuccess sr ->
model, Cmd.Empty
| UpdatePersistenceData pd ->
match pd.result with
| RetrieveResponse.Result.OK ->
let pnlCols = Array.tryFind (fun (v:bts.api.persistence.Record) -> v.key = "pnlColumns") pd.record
let pnlFilterValues = Array.tryFind (fun (v:bts.api.persistence.Record) -> v.key = "pnlFilterValues") pd.record
let pnlSorting = Array.tryFind (fun (v:bts.api.persistence.Record) -> v.key = "pnlSorting") pd.record
let newCols =
match pnlCols with
| None -> model.PnLColumns
| Some record -> (decodeUint8<List<string * bool>> record.value)
let newFilterValues =
match pnlFilterValues with
| None -> model.PnLFilterValues
| Some record -> (decodeUint8<Map<string, ColFilter>> record.value)
let newSorting =
match pnlSorting with
| None -> model.PnLSortDirection
| Some record ->
(decodeUint8<string * bool> record.value)
{ model with PnLColumns = newCols; PnLFilterValues = newFilterValues; PnLSortDirection = newSorting },
Cmd.batch (
(Cmd.ofMsg (SetPnLSort <| newSorting ))::[ for f in newFilterValues do yield Cmd.ofMsg (SetPnLFilter (f.Key, f.Value))])
| _ ->
model, Cmd.Empty
| RpcResponse str ->
printf "%s" str
model, Cmd.Empty
| SwitchTab tab ->
match tab with
| PnL ->
{ model with currentTab = tab }, selectSub (Some PnL) model
| ErrorOccured msg ->
Global.alert Global.Error (string msg)
model,Cmd.Empty
| OpenConfirmModal (title, okMessage) ->
{model with ConfirmModal = title, okMessage}, Cmd.Empty
| SetUnsub u ->
{model with unsub = Some u }, Cmd.Empty
| SetPositionData data ->
{ model with positionData = data }, Cmd.none
| RequestExpirationNames ->
model, Cmd.ofExistingPromise (requestExpNames model.mdRelay) id (box>>ErrorOccured)
| UpdateExpirationNames (data:ExpirationNameList[]) ->
let secs:(uint32 * PositionSecurity) list =
data
|> List.ofArray
|> List.collect (fun i ->
JSMap.toList i.names
|> List.map (fun x ->
(uint32 <| fst x),
({ securityName = (snd x)
venue = i.product.exchange
symbol = i.product.symbol
securityType = i.product.``type``
}: PositionSecurity)
)
)
{ model with mdRelayExpNameData = data; securityList = secs |> Map.ofList },
Cmd.batch (
selectSub None model ::
(Cmd.ofMsg (SetPnLSort <| model.PnLSortDirection ))::
[ for f in model.PnLFilterValues do yield Cmd.ofMsg (SetPnLFilter (f.Key, f.Value))])
| UpdateProducts data ->
{ model with mdRelayProductData = data}, Cmd.ofExistingPromise (requestExpNames model.mdRelay) id (box>>ErrorOccured)
| ChangePnLColumnVisibility (header, visibility) ->
let newData = [ for colHead, colVis
in model.PnLColumns do yield
( match colHead = header with
| false -> (colHead, colVis)
| true -> (colHead, visibility)
)]
model, Cmd.ofMsg (SetPnLColumns <| newData)
| SetPnLColumns data ->
{ model with PnLColumns = data}, Cmd.ofMsg SaveSettings
| ShowGridSettings data ->
{model with GridSettingsOpen = data}, Cmd.Empty
| SetPnLTempFilter (header, filter) ->
let newFilter = match filter with
| Empty ->
match Map.tryFindKey (fun k _ -> (k = header)) model.PnLFilterValues with
| None -> getPnLFilterType header
| Some k -> model.PnLFilterValues.Item k
| _ -> filter
{model with TempFilter = header, newFilter}, Cmd.Empty
| ShowFilterBox (header, isOpen, loca) ->
let newFilter = match Map.containsKey header model.PnLFilterValues with
| false -> getPnLFilterType header
| true -> model.PnLFilterValues.Item header
{model with FilterOpen = (isOpen, loca); TempFilter = (header, newFilter)}, Cmd.ofMsg (SetPnLTempFilter (header, newFilter))
| SetPnLFilter (header, filter) ->
match filterIsDefault filter with
| true ->
let newFilterValues =
match Map.containsKey header model.PnLFilterValues with
| true -> (Map.remove header model.PnLFilterValues)
| false -> model.PnLFilterValues
let newMap =
match Map.containsKey header model.PnLFilter with
| true -> Map.remove header model.PnLFilter
| false -> model.PnLFilter
{ model with PnLFilter = newMap; PnLFilterValues = newFilterValues }, Cmd.batch [
("", false, Wherever) |> ShowFilterBox |> Cmd.ofMsg
Cmd.ofMsg SaveSettings
]
| false ->
let newFilterValues =
match Map.containsKey header model.PnLFilterValues with
| true -> (Map.remove header model.PnLFilterValues).Add(header, filter)
| false -> model.PnLFilterValues.Add(header, filter)
let newFilter =
match filter with
| StringSearch s ->
function pnl ->
let v = (getValFromPNLUnit header pnl (Some model.securityList)) |> fst
if s.Trim() = "" || v.ToLower().Contains (s.Trim().ToLower()) then Some pnl else None
| NumberRange (min, max) ->
function pnl ->
let curVal = (getValFromPNLUnit header pnl None) |> snd
if (min <= curVal && curVal <= max) then Some pnl else None
| Empty -> Some
let newMap = model.PnLFilter.Add (header, newFilter)
{ model with PnLFilter = newMap; PnLFilterValues = newFilterValues }, Cmd.batch [
("", false, Wherever) |> ShowFilterBox |> Cmd.ofMsg
Cmd.ofMsg SaveSettings
]
| ClearPnLFilter header ->
match Map.containsKey header model.PnLFilterValues with
| true -> model, Cmd.ofMsg (SetPnLFilter (header, Empty))
| false -> model, Cmd.Empty
| SwitchPnLSort header ->
let direction = fst <| model.PnLSortDirection <> header || not <| snd model.PnLSortDirection
model, Cmd.ofMsg <| SetPnLSort (header, direction)
| SetPnLSort (header, direction) ->
let gSec = getSec <| model.securityList << uint32
let compStr (str1, str2) =
match direction with
| true -> String.Compare (str1, str2)
| false -> String.Compare (str2, str1)
let compNum (num1:double) num2 =
match direction with
| true -> num1 - num2
| false -> num2 - num1
let sortFn = match header with
| "Symbol" -> (fun (left:PNLUnit) (right:PNLUnit) -> compStr (
(gSec left.pos.securityId).symbol,
(gSec right.pos.securityId).symbol
)
)
| "Account" -> (fun (left:PNLUnit) (right:PNLUnit) -> compStr(left.pos.account, right.pos.account )
)
| "Exchange" -> (fun (left:PNLUnit) (right:PNLUnit) -> compStr (
(venueToString (gSec left.pos.securityId).venue),
(venueToString (gSec right.pos.securityId).venue)
)
)
| "Type" -> (fun (left:PNLUnit) (right:PNLUnit) -> compStr (
(securityTypeToString <| (gSec left.pos.securityId).securityType),
(securityTypeToString <| (gSec right.pos.securityId).securityType)
)
)
| "Security" -> (fun (left:PNLUnit) (right:PNLUnit) -> compStr (
(gSec left.pos.securityId).securityName,
(gSec right.pos.securityId).securityName
)
)
| "Security Key" -> (fun (left:PNLUnit) (right:PNLUnit) ->
match direction with
| true -> left.pos.securityId - right.pos.securityId
| false -> right.pos.securityId - left.pos.securityId
|> int)
| "Total PnL" ->
(fun (left:PNLUnit) (right:PNLUnit) ->
let liSod = ((calcPnL
left.bidPrice
left.askPrice
left.pos.sod.longAvgPrice
left.pos.sod.shortAvgPrice
left.pos.sod.longPos
left.pos.sod.shortPos
left.cashValue))
let liDay = (calcPnL
left.bidPrice
left.askPrice
left.pos.day.longAvgPrice
left.pos.day.shortAvgPrice
left.pos.day.longPos
left.pos.day.shortPos
left.cashValue)
let riSod = (calcPnL
right.bidPrice
right.askPrice
right.pos.sod.longAvgPrice
right.pos.sod.shortAvgPrice
right.pos.sod.longPos
right.pos.sod.shortPos
right.cashValue)
let riDay = (calcPnL
right.bidPrice
right.askPrice
right.pos.day.longAvgPrice
right.pos.day.shortAvgPrice
right.pos.day.longPos
right.pos.day.shortPos
right.cashValue)
(compNum (liSod + liDay) (riSod + riDay)) * 1000.0 |> int
)//Total PNL
| "Open PnL" ->
(fun (left:PNLUnit) (right:PNLUnit) ->
let li = calcPnL
left.bidPrice
left.askPrice
left.pos.sod.longAvgPrice
left.pos.sod.shortAvgPrice
left.pos.sod.longPos
left.pos.sod.shortPos
left.cashValue
let ri = calcPnL
right.bidPrice
right.askPrice
right.pos.sod.longAvgPrice
right.pos.sod.shortAvgPrice
right.pos.sod.longPos
right.pos.sod.shortPos
right.cashValue
(compNum li ri) * 1000.0 |> int
)// SOD Pnl (Open PnL)
| "Day PnL" ->
(fun (left:PNLUnit) (right:PNLUnit) ->
let li = calcPnL
left.bidPrice
left.askPrice
left.pos.day.longAvgPrice
left.pos.day.shortAvgPrice
left.pos.day.longPos
left.pos.day.shortPos
left.cashValue
let ri = calcPnL
right.bidPrice
right.askPrice
right.pos.day.longAvgPrice
right.pos.day.shortAvgPrice
right.pos.day.longPos
right.pos.day.shortPos
right.cashValue
(compNum li ri) * 1000.0 |> int
)//Day PnL
| "Total Pos" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum ((left.pos.day.longPos + left.pos.sod.longPos) - (left.pos.day.shortPos + left.pos.sod.shortPos))
((right.pos.day.longPos + right.pos.sod.longPos) - (right.pos.day.shortPos + right.pos.sod.shortPos))) * 1000.0 |> int
)
| "Total Long" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum (left.pos.day.longPos + left.pos.sod.longPos)
(right.pos.day.longPos + right.pos.sod.longPos)) * 1000.0 |> int
)//Total Long
| "Total Long Avg" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum(left.pos.day.longAvgPrice + left.pos.sod.longAvgPrice)
(right.pos.day.longAvgPrice + right.pos.sod.longAvgPrice)
) * 1000.0 |> int
)//Total Long Avg
| "Total Short" ->
(fun (left:PNLUnit) (right:PNLUnit) -> ( compNum
(left.pos.day.shortPos + left.pos.sod.shortPos)
(right.pos.day.shortPos + right.pos.sod.shortPos)
) * 1000.0 |> int
)//Total Short
| "Total Short Avg" ->
(fun (left:PNLUnit) (right:PNLUnit) -> ( compNum
(left.pos.day.shortAvgPrice + left.pos.sod.shortAvgPrice)
(right.pos.day.shortAvgPrice + right.pos.sod.shortAvgPrice)
) * 1000.0 |> int)//Total Short Avg
| "Open Pos" ->
(fun (left:PNLUnit) (right:PNLUnit) -> ( compNum
(left.pos.sod.longPos - left.pos.sod.shortPos)
(right.pos.sod.longPos - right.pos.sod.shortPos)
) * 1000.0 |> int
) // SOD Net Pos
| "Open Long" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum
left.pos.sod.longPos
right.pos.sod.longPos
) * 1000.0 |> int
) // SOD Long Pos
| "Open Long Avg" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.sod.longAvgPrice right.pos.sod.longAvgPrice) * 1000.0 |> int ) //SOD Long Avg Price
| "Open Short" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.sod.shortPos right.pos.sod.shortPos) * 1000.0 |> int ) //SOD Short Pos
| "Open Short Avg" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.sod.shortAvgPrice right.pos.sod.shortAvgPrice) * 1000.0 |> int) //SOD Short Avg Price
| "Day Pos" ->
(fun (left:PNLUnit) (right:PNLUnit) -> ( compNum
(left.pos.day.longPos - left.pos.day.shortPos)
(right.pos.day.longPos - right.pos.day.shortPos)
) * 1000.0 |> int )//Day Pos
| "Day Long" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.day.longPos right.pos.day.longPos) * 1000.0 |> int)
| "Day Long Avg" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.day.longAvgPrice right.pos.day.longAvgPrice) * 1000.0 |> int)
| "Day Short" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.day.shortPos right.pos.day.shortPos) * 1000.0 |> int )
| "Day Short Avg" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.day.shortAvgPrice right.pos.day.shortAvgPrice) * 1000.0 |> int)
| "Raw Day Long" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.rawDay.longPos right.pos.rawDay.longPos) * 1000.0 |> int )
| "Raw Day Short" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.pos.rawDay.longPos right.pos.rawDay.longPos) * 1000.0 |> int )
| "Raw Day Pos" ->
(fun (left:PNLUnit) (right:PNLUnit) -> ( compNum
(left.pos.rawDay.longPos - left.pos.rawDay.shortPos)
(right.pos.rawDay.longPos - right.pos.rawDay.shortPos)
) * 1000.0 |> int)//Raw Day Pos
| "Traded Today" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (
let lefti = if ((left.pos.day.longPos > 0.0) || (left.pos.day.shortPos > 0.0)) then 1 else 0
let righti = if ((right.pos.day.longPos > 0.0) || (right.pos.day.shortPos > 0.0)) then 1 else 0
match direction with
| true -> lefti - righti
| false -> righti - lefti
)
)//Traded Today
| "Bid" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.bidPrice right.bidPrice) * 1000.0 |> int )//bid
| "Ask" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.askPrice right.askPrice) * 1000.0 |> int )//ask
| "Mid" ->
(fun (left:PNLUnit) (right:PNLUnit) -> ( compNum
((left.askPrice + left.bidPrice) / 2.0)
((right.askPrice + right.bidPrice) / 2.0)
) * 1000.0 |> int )//mid
| "Last" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.lastPrice right.lastPrice) * 1000.0 |> int)//last
| "Settlement" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.settlementPrice right.settlementPrice) * 1000.0 |> int )
| "CPP" ->
(fun (left:PNLUnit) (right:PNLUnit) -> (compNum left.cashValue right.cashValue) * 1000.0 |> int )// CPP/cash value
| _ -> (fun (left:PNLUnit) (right:PNLUnit) -> 0)
{ model with PnLSortFun = sortFn; PnLSortDirection = header, direction }, Cmd.ofMsg SaveSettings
+80
View File
@@ -0,0 +1,80 @@
module Reports.Types
open System
open bts.api.admin
open bts.api.position
open bts.api.persistence
open bts.api
open bts.co2
open JSMap
type Tab =
| PnL
type ColFilter =
| StringSearch of string
| NumberRange of min:double * max:double
| Empty
type PositionSecurity =
{
securityName: string
venue: bts.api.Venue
symbol: string
securityType: bts.api.SecurityType
}
type Location =
| Location of double*double
| Wherever
type Msg =
| RpcResponse of string
| UpdateAdminData of AdminData
| SaveSettings
| UpdatePersistenceData of RetrieveResponse
| PersistenceUpdateSuccess of StoreResponse
| SwitchTab of Tab
| ErrorOccured of obj
| OpenConfirmModal of string * Msg
| SetUnsub of System.IDisposable
| SetPositionData of PNLUnit []
| RequestExpirationNames
| UpdateExpirationNames of ExpirationNameList[]
| UpdateProducts of Product[]
| ChangePnLColumnVisibility of string * bool
| SetPnLColumns of (string * bool) List
| SwitchPnLSort of string
| SetPnLSort of string * bool
| SetPnLTempFilter of string * ColFilter
| SetPnLFilter of string * ColFilter
| ClearPnLFilter of string
| ShowGridSettings of bool
| ShowFilterBox of string * bool * Location
type Model = {
currentTab:Tab
admin : IAdminService
adminData : AdminData option
positionData : PNLUnit []
basePositionService: IBasePositionService
positionService: IPositionService
persistenceService: IPersistenceService
persisenceBaseNamespace: string
mdRelay : IMarketDataRelay
mdRelayProductData: Product[]
mdRelayExpNameData: ExpirationNameList[]
unsub : System.IDisposable option
securityList : Map<uint32, PositionSecurity>
PnLColumns: (string * bool) list
PnLSortFun: PNLUnit -> PNLUnit -> int
PnLSortDirection: string * bool
PnLFilter: Map<string, PNLUnit -> PNLUnit option>
PnLFilterValues: Map<string, ColFilter>
GridSettingsOpen: bool
FilterOpen: bool * Location
TempFilter: string * ColFilter
ConfirmModal: string * Msg
token: string
this_username: string
}
+279
View File
@@ -0,0 +1,279 @@
module Reports.View
open Fable.Core
open Fable.Core.JsInterop
open Fable.Helpers.React
open Fable.Helpers.React.Props
open Fable.Core.DynamicExtensions
open Elmish
open Elmish.React
open Fable.Import
open Global.Reflection
open Types
open Reports
open bts.api.admin
open bts.api
open bts.api.position
open EnumUtils
open Microsoft.FSharp.Collections;
open State
let isActiveTab tab currentTab =
let ia = "is-active"
match tab, currentTab with
| Tab.PnL, Tab.PnL -> ia
// | _ -> ""
let PnLTabView (model:Model) (dispatch:Msg->Unit) =
// Browser.window.localStorage.setItem("PnLVisibleCols", "some")
// let thing = Browser.window.localStorage.getItem("thing")
let settingsModal =
match model.GridSettingsOpen with
| false -> span [][]
| true ->
Modal.show
"PnL Grid Settings"
(div[][
h3 [Class "is-size-3"][str "Columns"]
div [][
for header, visible
in model.PnLColumns do yield (
Fields.checkbox header "" visible [] (fun _ -> (header, not visible) |> ChangePnLColumnVisibility |> dispatch)
)
]
])
(Button.button "Close" "" "times" Button.Enabled (fun _ -> false |> ShowGridSettings |> dispatch))
(fun _ -> false |> ShowGridSettings |> dispatch)
let filterBox =
let filterHeader, filterType = model.TempFilter
let filterIsOpen, whereItIs = model.FilterOpen
let where =
match whereItIs with
| Wherever -> 0.0,0.0
| Location (x, y) -> (x, y)
match filterType, filterIsOpen with
| _, false | Empty, _ -> span [][]
| StringSearch searchStr, true ->
TableControl.showFilter
(div[Id "filterbox"][
Fields.text "Search:" (searchStr) [] (fun v -> (filterHeader, StringSearch v) |> SetPnLTempFilter |> dispatch)
])
(fun _ -> (filterHeader, filterType) |> SetPnLFilter |> dispatch)
(fun _ -> filterHeader |> ClearPnLFilter |> dispatch)
(fun _ -> ("", false, Wherever) |> ShowFilterBox |> dispatch)
(sprintf "%fpx" (where |> snd)) (sprintf "%fpx" (where |> fst))
| NumberRange (min, max), true ->
TableControl.showFilter
(div[Id "filterbox"][
Fields.uncheckedDouble "Min" min [] (fun dv -> (filterHeader, NumberRange (dv, max)) |> SetPnLTempFilter |> dispatch)
Fields.uncheckedDouble "Max" max [] (fun dv -> (filterHeader, NumberRange (min, dv)) |> SetPnLTempFilter |> dispatch)
])
(fun _ -> (filterHeader, filterType) |> SetPnLFilter |> dispatch)
(fun _ -> filterHeader |> ClearPnLFilter |> dispatch)
(fun _ -> ("", false, Wherever) |> ShowFilterBox |> dispatch)
(sprintf "%fpx" (where |> snd)) (sprintf "%fpx" (where |> fst))
let sortIcon header visible =
match fst model.PnLSortDirection = header, snd model.PnLSortDirection with
| true, true -> i [ ClassName (sprintf "fa fa-sort-up")] [ ]
| true, false -> i [ ClassName (sprintf "fa fa-sort-down")] [ ]
| _, _ -> i [ ClassName (sprintf "fa fa-sort header-icon-hidden")] [ ]
let filterIcon header =
let classStr = match Map.containsKey header model.PnLFilterValues || (fst model.TempFilter) = header with
| true -> "fa fa-filter header-filter-icon yellow"
| false -> "fa fa-filter header-filter-icon header-icon-hidden"
let mutable origin = Location (0.0, 0.0)
i [
ClassName (classStr)
Ref (fun me ->
if me <> null then
let r = me.getBoundingClientRect()
origin <- Location (r.left, r.top + me.clientHeight) )
OnClick (fun _ -> (header, true, origin) |> ShowFilterBox |> dispatch)
] []
let headerRow:TableControl.row =
{
cells =
[ for header, visible in model.PnLColumns do
if visible then
yield ( TableControl.Cell
( div [ ClassName "table-header" ] [
span [ OnClick (fun _ -> header |> SwitchPnLSort |> dispatch)] [str <| sprintf "%s " header]
sortIcon header visible
filterIcon header
])
) ]
className = ""
header = None
onClick = None
}
let fmtNum number = sprintf "%.2f" number
let rgSpan (s:string) =
match s, s.Substring (0, 1) with
| _, "-" -> span [Style [Color "#C41212"; FontWeight "bold"]] [str s]
| "0.00", _ -> span [] [str s]
| _ -> span [Style [Color "#008000"; FontWeight "bold"]] [str s]
let bidAskSpan (s:string) =
match s, s.Substring (0, 1) with
| _, "-" -> span [Style [Color "#A00000"]] [str s]
| "0.00", _ -> span [] [str s]
| _ -> span [Style [Color "#167ED3"]] [str s]
let boolToYesNo yes =
match yes with
| true -> "Yes"
| false -> "No"
let bind a b i =
match a i with
| Some i -> b i
| None -> None
let uberfilter = if model.PnLFilter.Count > 0 then model.PnLFilter |> Map.toArray |> Array.map snd |> Array.reduce bind else Some
let filteredPositionData = (model.positionData |> Array.choose uberfilter)
let sortedPositionData = filteredPositionData |> Array.sortWith model.PnLSortFun
let rws:TableControl.row list =
[
for data in sortedPositionData do yield {
cells =
[ for header, visible in
model.PnLColumns do
if visible then yield
(
let sec = getSec <| model.securityList <| uint32 data.pos.securityId
match header with
| "Symbol" -> TableControl.Cell (str (sprintf "%s" sec.symbol))
| "Account" -> TableControl.Cell (str data.pos.account)
| "Exchange" -> TableControl.Cell (str (sprintf "%s" (venueToString sec.venue)))
| "Type" -> TableControl.Cell (str (sprintf "%s" (securityTypeToString <| sec.securityType)))
| "Security" -> TableControl.Cell (str (sprintf "%s" sec.securityName))
| "Security Key" -> TableControl.Cell (str (sprintf "%i" data.pos.securityId))
| "Total PnL" -> TableControl.Cell (rgSpan (fmtNum (calcTotalPnL data)))
| "Open PnL" -> TableControl.Cell (rgSpan (fmtNum (calcSODPnL data)))
| "Day PnL" -> TableControl.Cell (rgSpan (fmtNum (calcDayPnL data)))
| "Total Pos" ->
TableControl.Cell (bidAskSpan (fmtNum ((data.pos.day.longPos + data.pos.sod.longPos) - (data.pos.day.shortPos + data.pos.sod.shortPos))))//Total Pos
| "Total Long" ->
TableControl.Cell (str (fmtNum (data.pos.day.longPos + data.pos.sod.longPos)))//Total Long
| "Total Long Avg" ->
TableControl.Cell (str (fmtNum ((data.pos.day.longAvgPrice + data.pos.sod.longAvgPrice))))//Total Long Avg
| "Total Short" ->
TableControl.Cell (str (fmtNum (data.pos.day.shortPos + data.pos.sod.shortPos)))//Total Short
| "Total Short Avg" ->
TableControl.Cell (str (fmtNum ((data.pos.day.shortAvgPrice + data.pos.sod.shortAvgPrice))))//Total Short Avg
| "Open Pos" ->
TableControl.Cell (bidAskSpan (fmtNum (data.pos.sod.longPos - data.pos.sod.shortPos))) // SOD Net Pos
| "Open Long" ->
TableControl.Cell (str (fmtNum data.pos.sod.longPos)) // SOD Long Pos
| "Open Long Avg" ->
TableControl.Cell (str (fmtNum data.pos.sod.longAvgPrice)) //SOD Long Avg Price
| "Open Short" ->
TableControl.Cell (str (fmtNum data.pos.sod.shortPos)) //SOD Short Pos
| "Open Short Avg" ->
TableControl.Cell (str (fmtNum data.pos.sod.shortAvgPrice)) //SOD Short Avg Price
| "Day Pos" ->
TableControl.Cell (str (fmtNum (data.pos.day.longPos - data.pos.day.shortPos)))//Day Pos
| "Day Long" ->
TableControl.Cell (str (fmtNum data.pos.day.longPos))
| "Day Long Avg" ->
TableControl.Cell (str (fmtNum data.pos.day.longAvgPrice))
| "Day Short" ->
TableControl.Cell (str (fmtNum data.pos.day.shortPos))
| "Day Short Avg" ->
TableControl.Cell (str (fmtNum data.pos.day.shortAvgPrice))
| "Raw Day Long" ->
TableControl.Cell (str (fmtNum data.pos.rawDay.longPos))
| "Raw Day Short" ->
TableControl.Cell (str (fmtNum data.pos.rawDay.longPos))
| "Raw Day Pos" ->
TableControl.Cell (str (fmtNum (data.pos.rawDay.longPos - data.pos.rawDay.shortPos)))//Raw Day Pos
| "Traded Today" ->
TableControl.Cell (str (boolToYesNo ((data.pos.day.longPos > 0.0) || (data.pos.day.shortPos > 0.0)) )) //Traded Today
| "Bid" ->
TableControl.Cell (span [Style [Color "#167ED3"]] [str (fmtNum data.bidPrice)])//bid
| "Ask" ->
TableControl.Cell (span [Style [Color "#A00000"]] [str (fmtNum data.askPrice)])//ask
| "Mid" ->
TableControl.Cell (str (fmtNum ((data.askPrice + data.bidPrice) / 2.0)))//mid
| "Last" ->
TableControl.Cell (str (fmtNum data.lastPrice))//last
| "Settlement" ->
TableControl.Cell (str (fmtNum data.settlementPrice))
| "CPP" ->
TableControl.Cell (str (fmtNum data.cashValue))//CPP/cash value
| _ -> TableControl.Cell (str "")
)]
className = ""
header = None
onClick = None
}
]
div [] [
TableControl.createDataTable (Some headerRow) rws (Button.fabutton "Settings" "cog" "" Button.Enabled ((fun _ -> true |> ShowGridSettings |> dispatch)))
settingsModal
filterBox
]
let PnLAreEqual (one:PNLUnit []) (two:PNLUnit []) =
match one.Length = two.Length with
| false -> false
| true ->
let mutable acc = true
for i = 0 to (one.Length - 1) do
acc <- acc
&& (int <| one.[i].pos.dayLongPos) = (int <| two.[i].pos.dayLongPos)
&& (int <| one.[i].pos.dayShortPos) = (int <| two.[i].pos.dayShortPos)
&& (int <| one.[i].pos.sodLongPos) = (int <| two.[i].pos.sodLongPos)
&& (int <| one.[i].pos.sodShortPos) = (int <| two.[i].pos.sodShortPos)
&& (int <| one.[i].askPrice * 100.0) = (int <| two.[i].askPrice * 100.0)
&& (int <| one.[i].bidPrice * 100.0) = (int <| two.[i].bidPrice * 100.0)
acc
let lazyPnLView model dispatch =
lazyView2With (fun a b ->
//LanguagePrimitives.PhysicalEquality a.positionData b.positionData
(PnLAreEqual a.positionData b.positionData)
&& a.GridSettingsOpen = b.GridSettingsOpen
&& a.PnLColumns = b.PnLColumns
&& a.PnLSortDirection = b.PnLSortDirection
&& a.FilterOpen = b.FilterOpen
&& a.TempFilter = b.TempFilter
)
PnLTabView
model dispatch
let tabContent (model:Model) (dispatch:Msg->Unit) =
div [ ClassName "is-tab-content"] [
// match model.currentTab with
// | Tab.PnL -> PnLTabView model dispatch
lazyPnLView model dispatch
]
let root (model:Model) (dispatch:Msg->Unit) =
(match model.adminData with
| None -> div[][str "Loading data..."]
| Some data ->
div [ ] [
match model.unsub with | Some d -> yield Unsub.unsub d | None -> ()
yield div [ Class "column is-main-content" ] [
div [ ClassName "tabs is-boxed" ] [
ul [ ] [
li [ ClassName (isActiveTab Tab.PnL model.currentTab) ] [
a [ OnClick (fun _ -> dispatch (SwitchTab Tab.PnL)) ] [ str "PnL" ]
]
]
]
tabContent model dispatch
]
]
)
+6 -3
View File
@@ -19,21 +19,24 @@ let updateLoggedIn msg model =
| AccountsMsg msg, AccountsModel m ->
let (acc, msg) = Accounts.State.update msg m
{ model with page = AccountsModel acc }, Cmd.map AccountsMsg msg
| ReportsMsg msg, ReportsModel m ->
let (reps, reportsCmd) = Reports.State.update msg m
{ model with page = ReportsModel reps }, Cmd.map ReportsMsg reportsCmd
| Navigate (page,pcmd), _ ->
{ model with page = page }, pcmd
| _ -> model, Cmd.Empty
let update msg model =
match model with
| PreLogin p ->
| PreLogin p ->
match msg with
| LoginMsg (Login.LoginSuccess ctx) ->
let (lmodel, lmsg) = Users.State.init ctx
LoggedIn { context = ctx; page = UsersModel lmodel }, Cmd.map UsersMsg lmsg
| LoginMsg m' ->
| LoginMsg m' ->
let (lmodel, lmsg) = Login.update m' p
PreLogin lmodel, Cmd.map LoginMsg lmsg
| _ -> model, Cmd.Empty
| LoggedIn m ->
| LoggedIn m ->
let lmodel, lmsg = updateLoggedIn msg m
LoggedIn lmodel, lmsg
+2
View File
@@ -7,12 +7,14 @@ open Imports
type Page =
| UsersModel of Users.Types.Model
| AccountsModel of Accounts.Types.Model
| ReportsModel of Reports.Types.Model
type Msg =
| UsersMsg of Users.Types.Msg
| LoginMsg of Login.Message
| Navigate of Page*Elmish.Cmd<Msg>
| AccountsMsg of Accounts.Types.Msg
| ReportsMsg of Reports.Types.Msg
type LoggedInModel = {
+3
View File
@@ -204,6 +204,9 @@ let applyUpdate (field:UserField) (user:BTSUser) =
| CanCreate cr -> { user with canCreate = cr }
| CMEOperatorID oid -> updateCMEExchangeUserData user { user.userData.exchangeUserData.cmeData with operatorId = oid }
| CMEATSOperatorID _ -> user
| MaxOrderRatePerConnection _ -> user
| ICECompanyID oid -> updateICEExchangeUserData user { user.userData.exchangeUserData.iceData with companyId = oid }
| ICEOperatorID oid -> updateICEExchangeUserData user { user.userData.exchangeUserData.iceData with operatorId = oid }
| ICEAuthorizedTrader oid -> updateICEExchangeUserData user { user.userData.exchangeUserData.iceData with authorizedTrader = oid }
+9 -9
View File
@@ -280,7 +280,7 @@ let orderRoutingTabView (model:Model) (dispatch:Msg->Unit) (readonly:bool) =
br []
h2 [Class "is-size-3"] [ str "ICE" ]
div [] [
Fields.number_novalidate "Company ID" iceData.companyId [ ] (ICECompanyID >> UpdateField >> dispatch)
Fields.uncheckedInt "Company ID" iceData.companyId [ ] (ICECompanyID >> UpdateField >> dispatch)
Fields.text "Operator ID" iceData.operatorId [ ] (ICEOperatorID >> UpdateField >> dispatch)
Fields.text "Authorized Trader" iceData.authorizedTrader [ ] (ICEAuthorizedTrader >> UpdateField >> dispatch)
Fields.text "Authorized Group" iceData.authorizedGroup [ ] (ICEAuthorizedGroup >> UpdateField >> dispatch)
@@ -289,14 +289,14 @@ let orderRoutingTabView (model:Model) (dispatch:Msg->Unit) (readonly:bool) =
h2 [Class "is-size-4"] [ str "MIFID" ]
div [Style [MarginLeft "2em"]] [
Fields.number_novalidate "Client Id Code" iceData.mifidClientIdCode [ ] (MifidClientIdCode >> UpdateField >> dispatch)
Fields.number_novalidate "Commodity Deriv Indicator" iceData.mifidCommodityDerivIndicator [ ] (MifidCommodityDerivIndicator >> UpdateField >> dispatch)
Fields.number_novalidate "Direct Electronic Access" iceData.mifidDirectElectronicAccess [ ] (MifidDirectElectronicAccess >> UpdateField >> dispatch)
Fields.number_novalidate "Execution Decision" iceData.mifidExecutionDecision [ ] (MifidExecutionDecision >> UpdateField >> dispatch)
Fields.number_novalidate "Id" iceData.mifidId [ ] (MifidId >> UpdateField >> dispatch)
Fields.number_novalidate "Investment Decision" iceData.mifidInvestmentDecision [ ] (MifidInvestmentDecision >> UpdateField >> dispatch)
Fields.number_novalidate "Liquidity Provision" iceData.mifidLiquidityProvision [ ] (MifidLiquidityProvision >> UpdateField >> dispatch)
Fields.number_novalidate "Trading Capacity" iceData.mifidTradingCapacity [ ] (MifidTradingCapacity >> UpdateField >> dispatch)
Fields.uncheckedInt64 "Client Id Code" iceData.mifidClientIdCode [ ] (MifidClientIdCode >> UpdateField >> dispatch)
Fields.uncheckedInt "Commodity Deriv Indicator" iceData.mifidCommodityDerivIndicator [ ] (MifidCommodityDerivIndicator >> UpdateField >> dispatch)
Fields.uncheckedInt "Direct Electronic Access" iceData.mifidDirectElectronicAccess [ ] (MifidDirectElectronicAccess >> UpdateField >> dispatch)
Fields.uncheckedInt64 "Execution Decision" iceData.mifidExecutionDecision [ ] (MifidExecutionDecision >> UpdateField >> dispatch)
Fields.uncheckedInt64 "Id" iceData.mifidId [ ] (MifidId >> UpdateField >> dispatch)
Fields.uncheckedInt64 "Investment Decision" iceData.mifidInvestmentDecision [ ] (MifidInvestmentDecision >> UpdateField >> dispatch)
Fields.uncheckedInt "Liquidity Provision" iceData.mifidLiquidityProvision [ ] (MifidLiquidityProvision >> UpdateField >> dispatch)
Fields.uncheckedInt "Trading Capacity" iceData.mifidTradingCapacity [ ] (MifidTradingCapacity >> UpdateField >> dispatch)
]
]
br []
+6
View File
@@ -34,10 +34,12 @@
<Compile Include="$(API)/fable/MarginService.fable.fs" />
<Compile Include="$(API)/fable/co2.fable.fs" />
<Compile Include="$(API)/fable/MarketDataRelay.fable.fs" />
<Compile Include="$(API)/fable/PersistenceService.fable.fs" />
<!-- Global to the app -->
<Compile Include="Lens.fs" />
<Compile Include="Global.fs" />
<Compile Include="Cmd.fs" />
<Compile Include="Controls/Unsub.fs" />
<Compile Include="Controls/nbsp.fs" />
<Compile Include="Controls/Button.fs" />
<Compile Include="Controls/ReactGrid.fs" />
@@ -59,6 +61,10 @@
<Compile Include="Users/Types.fs" />
<Compile Include="Users/State.fs" />
<Compile Include="Users/View.fs" />
<!-- Reports -->
<Compile Include="Reports/Types.fs" />
<Compile Include="Reports/State.fs" />
<Compile Include="Reports/View.fs" />
<!-- App -->
<Compile Include="Types.fs" />
<Compile Include="State.fs" />