Compare commits

..
Author SHA1 Message Date
John Colvin 1f685c7e95 WellKnownColumns.cs 2025-11-18 11:57:39 -05:00
John Colvin 5568c4051d TradeStreamParams.cs 2025-11-18 11:57:33 -05:00
John Colvin 389550e5cd Rm GroupByApplyStreamParams.cs 2025-11-18 11:57:23 -05:00
John Colvin d73d12ec31 GroupByApplyStreamFactory.cs 2025-11-18 11:57:08 -05:00
John Colvin 387891521b BookStreamParams.cs 2025-11-18 11:56:55 -05:00
John Colvin 1d329760f0 BookStreamFactory.cs 2025-11-18 11:56:46 -05:00
John Colvin f6fe4194d2 fix replace stream arg tuples with dedicated type 2025-11-18 11:56:14 -05:00
John Colvin f93f13048a Get rid of unused FilterStreamFactory 2025-11-18 11:55:48 -05:00
John Colvin 5995a00d69 fix replace stream arg tuples with dedicated type 2025-11-18 11:55:32 -05:00
John Colvin 226999ae5d Add ViewStreamFactory.cs 2025-11-18 11:55:00 -05:00
John Colvin 7b88b35b03 Add StreamFactoryLog "LogCreatingStream" 2025-11-18 11:54:51 -05:00
John Colvin fdf2cacd1d fix check pt misc changes 2025-11-18 11:54:24 -05:00
John Colvin 865940daf3 fix replace stream arg tuples with dedicated 2025-11-18 11:52:48 -05:00
John Colvin 10770c64e4 fix load IDataStreamFactory(s) - adds Enumerable ext TryGetExactCount 2025-11-18 11:50:49 -05:00
John Colvin c6a4b91c27 refactor: Add MaybeNullWhen(false) attr to TryGetSingle ext fn 2025-11-18 11:50:15 -05:00
John Colvin c0b7b39091 fix load IDataStreamFactory(s) (DataStreamFactory.cs) 2025-11-18 11:49:31 -05:00
John Colvin 4e198183eb Add SecType, Venue arg parser for streams 2025-11-17 10:34:46 -05:00
John Colvin eda34e0e3b check pt: misc changes 2025-11-17 10:34:13 -05:00
John Colvin 6532c329ac refactor: Replace stream arg tuples with dedicated type 2025-11-17 10:33:01 -05:00
John Colvin 588af742bf refactor: Rename ArrayHelper -> ArrayUtils
Also change snake_case fn names to PascalCase
2025-11-15 12:47:24 -05:00
John Colvin fe5fab1fa0 fix: Check for 0 length array when expanding for insert 2025-11-15 12:46:26 -05:00
John Colvin b8944da8ed chore: Add AsyncViewStream wrapper
Converts a Task<ViewStream> to a ViewStream.
2025-11-15 12:46:26 -05:00
John Colvin fca10887ce feat: Add general purpose ReplayStream
Takes a single time series table and emits the rows in real time
according to the intervals between rows.
2025-11-15 12:46:26 -05:00
John Colvin 8f53ff5ff9 vscode: Launch DSQA with config via DOTNET_ENVIRONMENT env var 2025-11-15 12:46:26 -05:00
John Colvin 1178aaf410 Add appsettings.Local.json to gitignore (DataStreamSuite) 2025-11-15 12:46:26 -05:00
John Colvin 62b790a156 feat: Add modulus operator to stream DSL 2025-11-15 12:46:25 -05:00
John Colvin 1fc77869e9 chore: Add a few DataStreamExtension fns
* AsDataPackets
  - converts IObservable<IBlueRowCollection> to IObservable<IDataPacket>
* Filter
* OrderBy
* Project
* TimeSeries
* SelectRow - transforms row
* SelectColumn - transforms column
2025-11-15 12:46:25 -05:00
John Colvin 3b91a697e9 improve: Support arbitrary columns in py table_formatter
Not just key/values
2025-11-15 12:46:25 -05:00
John Colvin 8908cc300a chore: Add IDataStreamFactory for FilterStream 2025-11-15 12:46:25 -05:00
John Colvin 5d039b54f5 More jupyter/pandas analysis for Brandon Chen 2025-11-15 12:46:25 -05:00
John Colvin 21f044cc3b refactor: Map TimeSpan to TypeCode.Int64 2025-11-15 12:46:25 -05:00
John Colvin 55c132a033 chore: Add OnWorkScheduler ext to recv IDataPackets on scheduler/thread 2025-11-15 12:46:25 -05:00
John Colvin ddd0de8eca perf: Reset grid when the schema changes in DSQA (not every update) 2025-11-15 12:46:25 -05:00
John Colvin f4451ad2f4 cleanup: Remove commented out code from DSQA MainForm.cs 2025-11-15 12:46:25 -05:00
John Colvin 12d9185b6e fix: Dispose last stream sub before evaluting next expression in DSQA
If the expression throws, we don't want the old stream to keep ticking.
2025-11-15 12:46:25 -05:00
John Colvin 84c3759d17 fix: Add null check to BlueDataPropertyDescriptor
So DSQA doesn't crash on a bad query/table impl. Mainly useful during development
when the stream might have a null row.
2025-11-15 12:46:25 -05:00
John Colvin 618a18b927 refactor: Automatically add id column in BlueTableSchemaBuilder 2025-11-15 12:46:25 -05:00
John Colvin 1e25099c62 cleanup: Enable nullable ref types in IBlueRowCollection.cs 2025-11-15 12:46:25 -05:00
John Colvin afeb5babd4 improve: Support enum types in ColumnInfo 2025-11-15 12:46:25 -05:00
John Colvin 0ff372a63e refactor: Add default impl for columnInfo indexers in IBlueTableSchema 2025-11-15 12:46:25 -05:00
John Colvin 197a81443c chore: Add SumTable (non cummulative)
This is more analgous to the DiffTable, as it computes the sum for
2 rows separated a fixed number of indices.
2025-11-15 12:46:25 -05:00
John Colvin ef98a7eda6 chore: Add prompt examples for BrandonChen 2025-11-15 12:46:24 -05:00
John Colvin 94e9c8c3f2 improve: Generate MLP at runtime with list of example files
Adds prompt file list to NaturalLanguageStreamFactoryConfig
2025-11-15 12:46:24 -05:00
John Colvin 0f9e515d01 chore: Add TableDropIndexEvaluator 2025-11-15 12:46:24 -05:00
John Colvin 342ecd9a52 chore: Add SelectorStream/Table 2025-11-15 12:46:24 -05:00
John Colvin d0c316b3fc chore: Add IndexStream/Table and DropIndex ext fn similar to pandas 2025-11-15 12:46:24 -05:00
John Colvin 44db6190a1 fix: Make PythonDataframeStream thread-safe
Input/output happen on different threads
2025-11-15 12:46:24 -05:00
John Colvin 9856e10eb6 fix: Support some mutations with convert_back delegate in SelectorList 2025-11-15 12:46:24 -05:00
John Colvin deda37b057 chore: Add arithmetic operators to PrimitiveVariant 2025-11-15 12:46:24 -05:00
John Colvin ce5095523b feat: Add a bunch of stream/table ext evaluators 2025-11-15 12:46:24 -05:00
John Colvin 7d374346f2 cleanup: Add NotNull attr to "assert_value" nullable ext 2025-11-15 12:46:24 -05:00
John Colvin ad0e9fa3c9 chore: Add BookDelta class 2025-11-15 12:46:24 -05:00
John Colvin befa17d29d chore: Add DoesNotReturnIf(false) attr to unit test Assert condition 2025-11-15 12:46:24 -05:00
John Colvin 3165ba1235 Add some half-baked Brandon Chen streams 2025-11-15 12:46:23 -05:00
John Colvin 53a9e6a62d fix: Support numeric conversions in ArgumentComparer 2025-11-15 12:46:23 -05:00
John Colvin ff816befb0 chore: Add MergeSorted ext fn to merge 2 already sorted enumerables 2025-11-15 12:46:23 -05:00
John Colvin a69c79b6a9 Add TableUpdate 'WithHistory' ext fn
A buffer of the last N updates is included with each update
2025-11-15 12:46:23 -05:00
John Colvin 7219ff5a95 Show IDataStreamFactory operators in DSQA 2025-11-15 12:46:23 -05:00
John Colvin 603f01699c Tweak databento_client.py 2025-11-15 12:46:23 -05:00
John Colvin 0ca318b222 refactor: Launch ThrottleStream via IDataStreamFactory
Not PrimitiveManager
2025-11-15 12:45:08 -05:00
John Colvin 4addb58743 feat: Add BboPriceSummaryStream for Brandon Chen 2025-11-15 12:45:08 -05:00
John Colvin c365330b08 feat: Add GroupByApplyStream 2025-11-15 12:45:08 -05:00
John Colvin 4bb4008ac6 feat: Add SliceTable/Stream 2025-11-15 12:45:07 -05:00
John Colvin 001892ab34 Jupyter/pandas analysis for brandon chen 2025-11-15 12:45:07 -05:00
John Colvin e77ee859a3 fix: Preserve clipboard (copy/paste) when DSQA exits 2025-11-15 12:45:07 -05:00
John Colvin c1a8702a47 ui: Improve dateTime formatting in DSQA 2025-11-15 12:45:07 -05:00
John Colvin 6045c602dc feat: Add pseudo member (extension) function calls to DSL 2025-11-15 12:45:07 -05:00
John Colvin 4a2a814af0 refactor: Move StreamQueryMetadata into StreamFactories namespace 2025-11-15 12:45:07 -05:00
John Colvin 08a655b1d5 Change WellKnownColumn names to match those in mlp
This is easier than changing them everywhere in the master learning prompt.
2025-11-15 12:45:07 -05:00
John Colvin 2830af5644 improve: Add config for natural language streams
They're created by a stream factory now, which has the config handy.
Config includes api key, model name, mlp file, etc.

fix add config for natural language streams
2025-11-15 12:45:07 -05:00
John Colvin 659eb83f4d refactor: Drop null type modifier in TryGetFirst ext
Replace w/ MaybeNullWhen attributes
2025-11-15 12:45:07 -05:00
John Colvin 5d9d364de3 cleanup: Remove unnecessary code 2025-11-15 12:45:07 -05:00
John Colvin 07c1ad3204 qol: Don't connect to sparkd by default in Spark.Console cli 2025-11-15 12:45:07 -05:00
John Colvin e61829f8e0 qol: Change default pubsub url from 'pubsub' to 127.0.0.1
The hostname 'pubsub' works when running pubsub and other services
via docker compose. In practice, it seems we usually just run them on a
single host, without using docker compose.
2025-11-15 12:45:07 -05:00
John Colvin 0cf44fcc40 feat: Add historic market data streams factory
Loads historic md from Databento files in structured paths. It is
designed to work with files downloaded via databento_client.py
2025-11-15 12:45:04 -05:00
John Colvin 2549e34a17 Create LiveBBOStream via BookStreamFactory
Instead of PrimitiveManager
2025-11-15 12:42:40 -05:00
John Colvin 693f2deb67 fix: Don't modify input env in BoundBooleanExpression.Evaluate 2025-11-15 12:42:40 -05:00
John Colvin 63665e9b10 refactor: Pass IMarketDataClient to streams via dependency injection
In general, the streams should not instantiate services, etc. themselves.
2025-11-15 12:42:40 -05:00
John Colvin a79f5ff860 improve: Support all integer types in IBlueRow Id ext fn 2025-11-15 12:42:40 -05:00
John Colvin dae75c507e cleanup: Remove unused code CSharpString.cs 2025-11-15 12:42:40 -05:00
John Colvin 4db9cdd357 feat: Add DiffStream to compute row diffs (like pandas) 2025-11-15 12:42:40 -05:00
John Colvin 18b19ac787 refactor: Rewrite SortStream now that IBlueRowCollection is IList 2025-11-15 12:42:40 -05:00
John Colvin b5ea918378 refactor: Add param for TimeSeriesStream timestamp col name
Default it to 'ts_event' instead of 'timestamp' (this is consistent w/
Databento schema fwiw)
2025-11-15 12:42:40 -05:00
John Colvin 9df4ecf6c8 fix: Trim backticks from nl generated dsl 2025-11-15 12:42:40 -05:00
John Colvin 957f6cb33f feat: Add FeatherReader stream
We don't need this right now, but I already wrote it...it might be
useful later.
2025-11-15 12:42:39 -05:00
John Colvin 03294ffe37 feat: Add function defs to DataStream DSL 2025-11-15 12:42:39 -05:00
John Colvin 0550078de0 chore: Support mutations in AggregateDictionary (optionally) 2025-11-15 12:42:39 -05:00
John Colvin 89129f726e chore: Implement IComparable in BookTuple 2025-11-15 12:42:39 -05:00
John Colvin 97eefb1a1e improve: Serialize to/from parquet in PythonDataframeStream 2025-11-15 12:42:39 -05:00
John Colvin 805693e170 feat: Add ParquetReader/Writer streams 2025-11-15 12:40:48 -05:00
John Colvin 3a7f3ac608 fix: Fire multiple rows at once in replay bbo/trade streams doWork fn
If necessary to keep pace with timestamps in the file.
2025-11-15 12:38:51 -05:00
John Colvin b82cc31d2d refactor: Launch LiveTradeStream via IDataStreamFactory 2025-11-15 12:38:51 -05:00
John Colvin 34859d7506 chore: Add id field to TradeRecord
Need this for streams
2025-11-15 12:38:51 -05:00
John Colvin 8a9bc1872e fix: Actually use sec filter in MarketDataClient.Trades 2025-11-15 12:38:51 -05:00
John Colvin 6b4ff90a56 style: Rename dispose_thread_safe in PascalCase 2025-11-15 12:38:51 -05:00
John Colvin d2a1b67717 refactor: Make IBlueRowCollection inherit IList<IBlueRow> 2025-11-15 12:38:51 -05:00
John Colvin 144b29c728 improvement: Lookup timestamp col by index rather than name 2025-11-15 12:38:51 -05:00
John Colvin e7625341e3 refactor: Remove hardcoded csv path from ReplayFutureLiveBBOStream
Add constructor overload to accept an input ViewStream
2025-11-15 12:38:51 -05:00
John Colvin fb15b3fb24 improvement: Don't mutate IBlueRowCollection after firing in FilterStream 2025-11-15 12:38:51 -05:00
John Colvin ea0246929a improvement: Make MultiRowRollingMathStream more performant
Avoid creating extra lists and o(n) enumeration
2025-11-15 12:38:50 -05:00
John Colvin 52f35cc421 refactor: Move IBlueTableSchema ext fns into DataStreamCore
The csproj in which IBlueTableSchema is defined.
2025-11-15 12:38:50 -05:00
John Colvin 7e968fec76 tool: Add databento_client.py to fetch (and store) databento data 2025-11-15 12:38:50 -05:00
John Colvin 188acc08ca chore: Copy/paste some log utils from py interop dir to tools/py 2025-11-15 12:38:50 -05:00
John Colvin 29be8c3ff5 refactor: Support dict and list of tuples in py fmt_table 2025-11-15 12:38:50 -05:00
John Colvin eb28e04bf1 refactor: Replace old NLStreamFactory w/ new BlueOrb one
Replace hardcoded file paths with proper config while we're at it
2025-11-15 12:38:50 -05:00
John Colvin 4ef2dbe4d7 refactor: Pass composite stream factory back to recursive factories as service 2025-11-15 12:38:50 -05:00
John Colvin 979051f147 build: Add appsettings.Local.json to DSQA 2025-11-15 12:38:50 -05:00
John Colvin 35a17b0fd0 refactor: Support random access (IList) to data in TableUpdate
We're doing the same thing to IBlueRowCollection. This will allow us to
implement column based operators, like python pandas.
2025-11-15 12:38:50 -05:00
John Colvin d93ce336f6 Add TestCommand back to Spark.Console cli 2025-11-15 12:38:50 -05:00
John Colvin 0d10c2ba5d new: Add GetColumn fn to IBlueRowCollection interface
This allows us to implement python/pandas functionality, much of which is based
on column operations.
2025-11-15 12:38:50 -05:00
John Colvin 2a4d62a1e9 chore: Add various collection classes
These will be useful in stream operators, row indexing, etc.

* AggregateList
* DeltaList
* DictionarySetWrapper
* ImmutableOrderedDictionary
* IMutableListFacade
  - inherits IReadOnlyList<T> and IList<T>
  - throws NotSupportedException in default impls for mutation ops
* IMutableCollectionFacade
  - inherits IReadOnlyCollection<T> and ICollection<T>
  - throws NotSupportedException in default impls for mutation ops
* MergedSortedList
* OrderedDictionary
* ReverseArrayList
* SliceList
* StepList
2025-11-15 12:38:50 -05:00
John Colvin b9e191eeb9 refactor: Drop nullable modifier for TValue in GetValueOrDefault ext fn 2025-11-15 12:38:50 -05:00
John Colvin 75f2678df6 cleanup: Fix build warnings
* BlueOrbNLStream.cs
* ChangeTableNameStream.cs
* FilterStream.cs
* FilterStream.cs
* GroupByOutputTable.cs
* GroupByStream.cs
* JoinForeignTable.cs
* MultiRowRollingMathStream.cs
* MutableAppendStream.cs
* PersistWindowState.cs
* PubsubEchoTestStream.cs
* RowDictionaryTable.cs
* SortedTable.cs
* TimeSeriesStream.cs

Also add Metadata prop to IDataPacket
2025-11-15 12:38:49 -05:00
John Colvin 8bbe850549 chore: Add 'Rows' prop to BlueTable to avoid accidentally iterating over KeyValuePairs 2025-11-15 12:38:49 -05:00
John Colvin 25860af7bf refactor: Use dependency injection in Spark.Console 2025-11-15 12:38:49 -05:00
John Colvin 68198a058e cleanup: Remove broken settings cmd from Spark.Console 2025-11-15 12:38:49 -05:00
John Colvin 7297d667a2 refactor: Use top-level statements for Spark.Console 'main' 2025-11-15 12:38:49 -05:00
John Colvin ace1b5f04d refactor: Consolidate SparkApp and ShellBootstrapper into single class 2025-11-15 12:38:49 -05:00
John Colvin bd252c251c feat: Add stream driven price lines to ladder 2025-11-15 12:38:49 -05:00
John Colvin 57b82cf10d fix: Use IDictionary.GetEnumerator explicitly to iterate keyvals
We really need to replace these old non-generic collections with generic ones.
BlueRow's IDictionaryEnumerator implementation iterates over values when passed
to a foreach loop, not key value pairs.
2025-11-15 12:38:49 -05:00
John Colvin f07d83e7bb refactor: Make CompositeDataStreamFactory class internal 2025-11-15 12:38:49 -05:00
John Colvin 619be37379 feat (ui): Add ConfigServerPicker dialog
So we can connect to different config servers without manually editing spark.conf,
or changing command line args. This will run before the Login dialog.
2025-11-15 12:38:49 -05:00
John Colvin 9348106585 refactor: Move ComboBox styles to ComboBoxes.xaml
I'm working on a ConfigServerPicker dialog, which will run before the main
application starts. This allows the dialog to use the same dropdown styles as
the main app without the overhead of loading generic.xaml.
2025-11-15 12:38:49 -05:00
John Colvin 760cdc0fba cleanup: Fix build warnings, remove unused/redundant code
* Make a few member vars and fns local to the constructor
* Replace a couple non-generic collections with generic ones
* Fix warnings and clean code in:
  - DictionaryExtensions.cs
    * Remove ext fns that are part of dotnet now
  - JoinNameResolver.cs
  - MainForm.cs

cleanup: Remove unused code in ladder

Some props in cs, and StaticResources in xaml were leftover after I combined
the panel price points into a single collection, bound to an ItemsControl.
2025-11-15 12:38:49 -05:00
John Colvin fcd1b0e799 refactor: Use generic collection types in PrimitiveManager 2025-11-15 12:38:49 -05:00
John Colvin eb102a5898 cleanup: Remove unused code in PrimitiveManager.cs 2025-11-15 12:38:48 -05:00
John Colvin fb3262edea cleanup: Use file-scoped namespace in PrimitiveManager.cs
Also sort using statements
2025-11-15 12:38:48 -05:00
John Colvin 6e1e5d4bbf refactor: Add IDataStreamFactory(s) for spatial arb streams 2025-11-15 12:38:48 -05:00
John Colvin 256f7025a2 tool: Run Spark.exe from its own directory
It looks in the working directory for runtime config appsettings.json files.
2025-11-15 12:38:48 -05:00
John Colvin 24892fb1a8 cleanup: Remove unused BooleanExpressionToken Negate 2025-11-15 12:38:48 -05:00
John Colvin e3c2efcfcb doc: Comment why MDConsumer must be kept alive
The language server suggests removing it because it isn't referenced.
However, it contains unmanaged resources that mustn't be prematurely
garbage collected.
2025-11-15 12:38:48 -05:00
John Colvin 29080795f7 refactor: Rename AddRemoteQueryManager -> AddRemoteQueryStreams 2025-11-15 12:38:48 -05:00
John Colvin cd291b7a0d refactor: Hide md client behind interface, create via Dep Injection 2025-11-15 12:38:48 -05:00
John Colvin 8af9d13802 Drop dump-books and dump-securities from spark_api_server cli
These were subcommands were out of place. This exe has one purpose: run
the Spark Api Server.
2025-11-15 12:38:48 -05:00
John Colvin 318d8fb275 cleanup: Remove unused MarketDataClient from OrderSample 2025-11-15 12:38:48 -05:00
John Colvin f33d19c185 build: Launch console for Spark Debug builds, otherwise hide it 2025-11-15 12:36:28 -05:00
John Colvin fb43d214f3 cleanup: Remove ancient winforms sdk ui example 2025-11-15 12:16:33 -05:00
John Colvin c4f12be3e4 cleanup: Fix a ton of build warnings (cs)
All these warnings have been bothering me for a while. The work isn't done, but
this commit chops them down from ~1000 to ~600.

* AppendCollection.cs
* AppendStreamModified.cs
* BenzingaNewsStream.cs
* BenzingaNewsSymbolStateStream.cs
* BookStream.cs
* CSVSaverStream.cs
* ChartsUtils.cs
* ColumnInfo.cs
* ContractUtils.cs
* ContractUtils.cs
* DiffStream.cs
* FuturesActiveContractStream.cs
* FuzzyJoinOverTimeStream.cs
* HistoricalBarsStream.cs
* HistoricalBarsStream.cs
* InstrumentsStream.cs
* JoinOperandRowIndex.cs
* JoinRow.cs
* JoinStream.cs
* JoinTable.cs
* JoinTableSchema.cs
* LatencyStream.cs
* LeftInternalJoinStream.cs
* LightForeignJoinRow.cs
* LightForeignJoinRowList.cs
* LightLeftForeignJoinRow.cs
* LiteralStream.cs
* NLStream.cs
* NLToDSLStream.cs
* NLToIndexedFewShotPromptStream.cs
* NonIncGroupByStream.cs
* NonIncJoinBookkeeper.cs
* NonIncJoinSpec.cs
* OHLCVStream.cs
* OpenAIPromptStream.cs
* PriceStatsStream.cs
* PriceStatsStream.cs
* ProjectHelper.cs
* ProjectStream.cs
* PubSubServerStateStream.cs
* PubsubHeartbeatStream.cs
* SimpleNode.cs
* SortRow.cs
* SpatialArbStatsStream.cs
* SpatialFuturePairsStream.cs
* SpreadPerformanceEvaluator.cs
* SpreadPerformanceStream.cs
* StreamIOCommon.cs
* SuperJoinStream.cs
* TableMetaDataStream.cs
* TextWriterStream.cs
* TimeAverageStream.cs
2025-11-15 12:16:33 -05:00
John Colvin 54d7279832 style: Normalize indentation in PubsubHeartbeatStream.cs 2025-11-15 12:16:33 -05:00
John Colvin 03df578a81 chore: Add GetValue, TryGetValue ext fns to IBlueRow 2025-11-15 12:16:32 -05:00
John Colvin 7a50ad2cc5 chore: Add 'Empty' fn for BlueTable/Schema (for convenience) 2025-11-15 12:16:32 -05:00
John Colvin 0cc4b83417 cleanup: Remove redundant code block in HistoricalBarsStream.cs 2025-11-15 12:16:32 -05:00
John Colvin 4f1efea066 cleanup: Fix indentation and build warnings in TestStream.cs 2025-11-15 12:16:32 -05:00
John Colvin ef4bcf4c44 fix (preemptive): Avoid StackOverflow in md client securities fn
One of the 'securities_incremental' fns was calling back into itself instead of
the other overload. This is a 'preemptive' fix because the problematic code path
wasn't running in practice (though it was a ticking time bomb).
2025-11-15 12:16:32 -05:00
John Colvin 5a6139f3c5 cleanup: Make MDConsumer.on_security nonnull
It's always initialized in the constructor.
2025-11-15 12:16:32 -05:00
John Colvin e2816aaa33 refactor: Decouple ViewStreamDatabase from TableStream, ISubscriberCore 2025-11-15 12:16:32 -05:00
John Colvin fc702db7fa check pt: PingPong algo
This is a work-in-progress; putting a pin in it for now to focus on
other priorities.
2025-11-15 12:16:32 -05:00
John Colvin 787cf0becd Add environment variable override for app data path
Historically, ~/sparkdata on linux, and ~/AppData/Roaming/bts on win.
Now that we may run multiple exes at once (api server, pubsub, sparkd, etc.),
we need to support multiple data dirs on the same host.
2025-11-15 12:16:32 -05:00
John Colvin b4e26d2fbe Drop ".exe" from log dir name on windows 2025-11-15 12:16:32 -05:00
John Colvin e2a101eb20 feat: Add SpreadBook stream 2025-11-15 12:16:32 -05:00
John Colvin 6300f8f7c2 fix: Check settings Subject.IsDisposed before using 2025-11-15 12:16:32 -05:00
John Colvin 1c6f4669be ui (launcher): Move "X" button to left of report menu items
This is a more natural location. Also, it looked weird on the right because the
menu labels aren't fixed width, so the buttons weren't vertically aligned.
2025-11-15 12:16:32 -05:00
John Colvin 45fcb2e20b refactor: Use HostBuilder to launch spark w/ streams 2025-11-15 12:16:32 -05:00
John Colvin 75dee2a05a build (cleanup): Get rid of global.json
We don't need it anymore
2025-11-15 12:16:31 -05:00
John Colvin dcb02feb90 build: Update System.Composition pkg refs to 8.0.0 2025-11-15 12:16:31 -05:00
John Colvin 81444c1045 cleanup: Mark a few classes "internal" in Spark 2025-11-15 12:16:31 -05:00
John Colvin 934c0ae70a cleanup: Use consistent access modifier in TabDocument partial class
It should be marked internal in both xaml and xaml.cs (it was previously
only so in the latter).
2025-11-15 12:16:31 -05:00
John Colvin efdd91935e refactor: Consolidate ShellBootstrapper and base class into one 2025-11-15 12:16:31 -05:00
John Colvin da96744f7f feat: Add Stream Reports shell window 2025-11-15 12:16:31 -05:00
John Colvin 6ed6a36ee1 build: Define custom "main" (entry point) for Spark exe
This is a prelude to integrating streams into spark. The plan is to use
an ms HostBuilder to instantiate stream services/dependencies alongside
the spark Application object.
2025-11-15 12:12:55 -05:00
John Colvin e0fc22121f cleanup: Get rid of stunnel code
This looks like it hasn't been hooked up for years...it would only start
if a stunnel.conf file was detected, which hasn't been around for a very
long time.
2025-11-14 15:02:16 -05:00
John Colvin 3671e4d713 fix: Only create mutex if spark app is NOT running in vs designer
I accidentally droped the '!' in a cleanup commit 4 years ago
2025-11-14 15:02:16 -05:00
John Colvin dd69842a3c vscode: Add DSQA debug cfg to skip build 2025-11-14 15:02:16 -05:00
John Colvin 15883e42b6 fix (dsqa): Resolve name conflicts with new BlueOrb streams 2025-11-14 15:02:16 -05:00
John Colvin 34caf54015 fix: Fix typo in "DateTimeExpansionStream" type name
Previously, it was "DateTimeExpanstion" (erroneous "t")
2025-11-14 15:02:16 -05:00
John Colvin 84a4182077 chore: Add CircularBuffer class (c#) 2025-11-14 15:02:16 -05:00
John Colvin e8197a81cc refactor: Rename ExceptionAssert.is_valid ExceptionAssert.Index.IsValid
Like the ExceptionAssert.Argument class that provides methods that throw
ArgumentException if a condition isn't satisfied, the ExceptionAssert.Index
class provides methods that throw IndexOutOfRange exceptions.
2025-11-14 15:02:15 -05:00
John Colvin 17d2f6d701 chore: Add OffsetList class
This supports mutations (unlike the SubRangeWrapper class).
2025-11-14 15:02:15 -05:00
John Colvin b728a8cb46 cleanup: Remove "partial" class modifiers from OrderBook vm classes
They simply weren't necessary.
2025-11-14 15:02:15 -05:00
John Colvin 6766e48706 check pt: Proof of concept ladder left panel price point collection 2025-11-14 15:02:15 -05:00
John Colvin e7246b22bc ui: Adjust ladder ticks-per-row with ctrl+shift+mousewheel 2025-11-14 15:02:15 -05:00
John Colvin 5550166d03 build: Change publish_data_stream.py to publish the sln
We were publishing individual projects as a workaround for the
dependency issue (bug) that caused some projects to incorrectly target
net8.0. The fact that publishing individual projects "just worked" was
probably dumb luck. Now that the underlying issue has been addressed, we
can resume publishing the sln as a whole.
2025-11-14 15:02:15 -05:00
John Colvin 489c683d2e build: Add scripts to publish data stream apps 2025-11-14 15:02:15 -05:00
John Colvin ff18d39d58 build: Use separate cmd to build DataStreamSuite/QueryAnalyzer.sln
The DataStreamSuite.sln builds the new webapp now, which will
(eventually) replace DSQA. We'll keep it around for now, but with a
clean separation from the main build.
2025-11-14 15:02:15 -05:00
John Colvin 05644ed6cf feat: Add DataStream WebApp
Included in DataStreamSuite.sln, but not DataStreamQueryAnalyzer.sln
2025-11-14 15:00:45 -05:00
John Colvin d6a88a7ac9 cleanup: Remove unused code 2025-11-14 14:59:33 -05:00
John Colvin f7e620db3e Reduce log noise in AsyncTCPConnection_AsyncWrapper 2025-11-14 14:59:33 -05:00
John Colvin c745407ae7 TEMP changes to appsettings.json (at least i should review them before making permanent) 2025-11-14 14:59:33 -05:00
John Colvin 1bb7d6eca2 Add ColumnIndex class to resolve column names to indices
Improves performance compared to accessing columns by name
2025-11-14 14:59:33 -05:00
John Colvin 519ef8b0af Increase SQLiteEnumerator page size to 65536
This performs better, and doesn't seem to be too large in practice
2025-11-14 14:59:33 -05:00
John Colvin 6b93c07271 Serialize TimeSpan w/ microsecond precision in DataPacket json 2025-11-14 14:59:33 -05:00
John Colvin c3288c721d Format DateTimes w/ microsecond precision in DataPacketJsonConverter 2025-11-14 14:59:33 -05:00
John Colvin 2622626eed Add BlueColumnTable for pandas integration
This will make it easier/more natural to support pandas column expressions
2025-11-14 14:59:33 -05:00
John Colvin d9470bdb6e Add c# Variant class, modeled after std::variant in c++ 2025-11-14 14:59:33 -05:00
John Colvin f32e9bba10 refactor: Rename snake_case fns PascalCase 2025-11-14 14:58:14 -05:00
John Colvin f119f30b62 fix load IDataStreamFactory(s) with depdendency injection 2025-11-14 14:57:43 -05:00
John Colvin 4877355fee refactor: Use dependency injection in DSQA 2025-11-14 14:52:07 -05:00
John Colvin ada7d4bed1 refactor: Load IDataStreamFactory(s) with dependency injection
So we can create ViewStreams that depend on other services, config, etc.
Previously, the PrimitiveManager relied purely on static construction of
types through reflection.
2025-11-14 14:52:07 -05:00
John Colvin f65b6b82f2 refactor: Replace DataPacket w/ IDataPacket in stream handlers
This commit also renames BlueTableSchemaAdaptor to BlueTableSchemaAdapter,
and makes it implement IBlueTableSchema.
2025-11-03 16:21:32 -05:00
John Colvin 1155fd6997 refactor: Change namespace to Bts.DataStreamCore for a few types
Since they're in the DataStreamCore csproj
2025-11-03 16:19:50 -05:00
John Colvin f423ea518e Make 'name' default to CallerArgumentExpression for convenience 2025-11-03 16:19:50 -05:00
John Colvin 088b543e48 refactor: Replace if/else with switch cases 2025-11-03 16:19:50 -05:00
John Colvin 2163821b43 cleanup: Remove over-engineered subcommands
We don't need explicit subcommands to 'run onixs' md (just use config).
2025-11-03 16:19:50 -05:00
John Colvin abf4cdacf8 fix: Ensure IDGenerator instance is created before deref 2025-11-03 16:19:49 -05:00
John Colvin 70fc4f9ae7 fix: Expand undefined vars to empty string
Bugs (fixed):
  * Md shared memory file names have unexpanded variable: '${BTS_SHARED_MEMORY_PREFIX}'
2025-11-03 16:19:49 -05:00
John Colvin 797ba111ec fix: Emplace MDConsumerHandle instead of copy assign 2025-11-03 16:19:49 -05:00
John Colvin a51b015907 cleanup: Remove unnecessary code
Trim includes as well
2025-11-03 16:19:49 -05:00
John Colvin ec057605a5 improvement: Set SHARED_MEMORY_PREFIX to an environment variable
So we can effectively define it at runtime. We don't want to have to
produce different builds just to change the shared mem prefix.
2025-11-03 16:19:49 -05:00
John Colvin e0903d1c82 fix: Always prepend proc name to shared mem prefix
Our shared memory model isn't really designed to be shared across
processes. Maybe some day it will.
2025-11-03 16:19:49 -05:00
John Colvin 1616e1a8b9 cleanup: Remove redundant macro def 2025-11-03 16:19:49 -05:00
John Colvin cfd5579703 Skip md recording thread loop if no recorders configured to run 2025-11-03 16:19:49 -05:00
John Colvin 849737b2ef cleanup: Trivial whitespace cleanup in Protobuf.cmake 2025-11-03 16:19:49 -05:00
John Colvin 85a5b72ec5 build: Clean up data_files cmake proj (support --target clean) 2025-11-03 16:19:49 -05:00
John Colvin e9ac78b438 fix build: Add ShmSegmentName.cpp to windows client sources 2025-11-03 16:19:49 -05:00
John Colvin e5288f1be2 Add api server to docker compose 2025-11-03 16:19:49 -05:00
John Colvin 50c2a57c5c Change api server ports so they don't conflict w/ ds webapp 2025-11-03 16:19:49 -05:00
John Colvin 8f293f73e3 build: Resolve app data dir and shared mem prefix at runtime
We really can't define them at compile time anymore as we build multiple
exes that depend on libspark in the same dir (e.g., spark api server,
pubsub, ds-publisher, webapp, etc.). That means one libspark.so (or dll)
must serve all exes. We should compile a consistent libspark to mitigate
the risk of running an exe with the wrong version (of libspark). For
example, running pubsub with a libspark configured for spark api server,
leftover from an earlier build.
2025-11-03 16:19:49 -05:00
John Colvin ac42732a19 Don't log "initializing spark config" if it has already been initialized 2025-11-03 16:19:48 -05:00
John Colvin 97246e7ce7 vscode: Add debug cfg for spark api server 2025-11-03 16:19:48 -05:00
John Colvin 0815f9316d Use dependency injection in spark api server 2025-11-03 16:19:48 -05:00
John Colvin cc00ff2a0a Add SparkLoggerAdapter for Msft ILogger 2025-11-03 16:19:48 -05:00
John Colvin 28545b5099 fix: Don't assert on exception in BooleanExpression.evaluate
Bugs (fixed):
  * DataStream WebApp Server sometimes crashes when the user enters a
    bad query.
2025-11-03 16:19:48 -05:00
John Colvin 04c1ad7be8 Assert that local ip address is successfully obtained 2025-11-03 16:19:48 -05:00
John Colvin 2441a5267d improvement: Resolve hostnames when comparing ip addrs
So we can to configure pubsub routes by hostname or ipv4 (ipv6 hasn't
been tested yet...so we should assume it doesn't work).
2025-11-03 16:19:48 -05:00
John Colvin 12f44f63aa Assert TcpConnection endpoint matches expected 2025-11-03 16:19:48 -05:00
John Colvin c2fe578ccb Catch exceptions on AsyncTCPConnector_AsyncWrapper background thread
Since it spawns a new thread on each attempt, uncaught exceptions were previously undetected,
and caused silent failures down the pipe. This should make it easier to track such bugs down.
2025-11-03 16:19:48 -05:00
John Colvin a01fc6cc15 fix: Format PubSubRoute as json in log msgs
It was previously using the default ToString, which simply returns the type name
2025-11-03 16:19:48 -05:00
John Colvin d80cd9c0fd build: Upgrade packages (deps) in DataStreamTests 2025-11-03 16:19:48 -05:00
John Colvin d707847079 cleanup: Remove unused ViewStreamDatabase config 2025-11-03 16:19:48 -05:00
John Colvin 96d98a239f vscode: Add PubSubServer debug cfg to skip build 2025-11-03 16:19:48 -05:00
John Colvin 118e2a9f2e Remove reference to spark protobuf Config in ViewStream
We can't read spark protobuf Config until it has been initialized,
and since ViewStream doesn't have the configServer param handy it
isn't able to initialize config if necessary. In that case, it would simply
block until config was initialized by someone else. Such a risk doesn't
seem justified for this particular configuration dependency
(DataStreamConfig.UseUncopiedNewRowsInFire). For now, its hardcoded to false.
2025-11-03 16:19:48 -05:00
John Colvin 1481a3fd07 refactor: Run RemoteQueryManager as an IHostedService
This paradigm more accurately represents the RemoteQueryManager as a
Service that can be started/stopped.
2025-11-03 16:19:45 -05:00
John Colvin a5e58c26ce fix: Ensure spark config init and login are only performed once
As a static function that could be called from anywhere in the code
(directly or indirectly), 'Configuration.Initialize' was vulnerable to
race bugs. The "Configuration.get<T>()" method, whose calls are
ubiquitous in our code, would initialize config if it hadn't been done
so already. Without a reference to the configServer param, this param
would effectively be ignored/overridden with null.

It's important that login only be performed once, for obvious reasons
(our exes that connect to sparkd are generally designed to process data
for a single user). Furthermore, not every routine that requires a
successful login has a reference to the credentials needed to do it. To
address this, we now pass the ISparkdLoginService singleton to these
routines so they can wait until the singular login task has completed
before they begin.
2025-11-03 16:13:44 -05:00
John Colvin 8637bb9254 Assert ViewStreamDatabase.instance is only created once
The instance must be initialized via ViewStreamDatabase.SetGlobalInstance
2025-11-03 16:13:44 -05:00
John Colvin 7c61c2ef6d cleanup: Remove unused code from ViewStreamDatabase 2025-11-03 16:13:44 -05:00
John Colvin 0668e0cd8c fix: Define BoooleanConstant before StringConstant
Otherwise the former masks the latter and prevents it from matching
2025-11-03 16:13:44 -05:00
John Colvin 3ae88248cc cleanup: Remove unnecessary deps from csprojs 2025-11-03 16:13:43 -05:00
John Colvin 1367cea9ca fix: Catch exception when creating TcpClient 2025-11-03 16:13:43 -05:00
John Colvin b384d89820 fix: Make SystemSubscriptionMapPublisher threadsafe
The PubSubState.onTagMapChanged event may not be invoked on
the same thread as doWork.
2025-11-03 16:13:43 -05:00
John Colvin d669d24aea fix: Add a few null checks
* TimeBasedFilter
* TableAccumulator2
* NonIncGroupByStream
2025-11-03 16:13:43 -05:00
John Colvin e5bc6667e7 Reduce task wait/timeout code in AsyncTCPConnector 2025-11-03 16:13:43 -05:00
John Colvin 41512cb811 fix build warnings: Make props nullable strings in SharedStreamConfig 2025-11-03 16:13:43 -05:00
John Colvin 01d600b821 Assert that System publishers 'doWork' is always called on same thread 2025-11-03 16:13:43 -05:00
John Colvin d6197f7d1f deploy: Add pubsub and md-publisher services to compose.yaml 2025-11-03 16:13:43 -05:00
John Colvin 9e1cb14523 refactor: Model config profiles after dotnet appsettings
Namely, in DataStreamPublisher and PubSubServer. This is part of a
broader paradigm shift towards dotnet factory pattern.

https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines

* Rename Configurations/<name>.json -> appsettings.<name>.json
* Move reusable startup code into new Bts.Extensions.Hosting proj
2025-11-03 16:13:41 -05:00
John Colvin cd7c04dce5 Add ErrorChanged event to propagate DataStream exceptions 2025-11-03 16:13:19 -05:00
John Colvin 6aee76590f fix build: Fix Spark build after making config init async 2025-11-03 16:13:19 -05:00
John Colvin fc79c451b2 refactor: Change Configuration.initialize to be async
Tweak SparkCoreServices to synchronize Configuration.init globally
2025-11-03 16:13:19 -05:00
John Colvin b916f1b836 vscode: Add 'isSparkApiEnabled' input to launch.json 2025-11-03 16:13:19 -05:00
John Colvin 826e03af94 cleanup: Remove unused PubSubConfig props 2025-11-03 16:13:19 -05:00
John Colvin 57217c9adf Capture OperationCanceledException (instead of TaskCanceledException)
The former is the base class for the latter, so this will widen the umbrella to catch a few corner cases
that would otherwise been handled by the generic "Exception" catch block. One corner case I know
of is when an op is canceled via "CancellationToken.ThrowIfCancellationRequested". In that case,
an OperationCanceledException is thrown (not a TaskCanceledException).

https://stackoverflow.com/questions/34359129/operationcanceledexception-vs-taskcanceledexception-when-task-is-canceled
2025-11-03 16:13:19 -05:00
John Colvin deae8c7fb0 Fix dsqa config json 2025-11-03 16:13:18 -05:00
John Colvin 81d3b10c2b fix: Always call 'handle_internal_pubs' in PubSubMessageReactor main loop 2025-11-03 16:13:18 -05:00
John Colvin f2cc576c51 Fix mismatched types in PubSubServerTagStatsStream schema 2025-11-03 16:13:18 -05:00
John Colvin b25038fac6 Add DataStreamPublisher service modules designed for Dependency Injection
* Change DataStreamPublisher json fields to match cs class property names verbatim
* Rename Publishers -> PublisherNodes in DataStreamPublisherConfig
2025-11-03 16:13:18 -05:00
John Colvin 9b95caa8e6 refactor: Decouple ViewPublisher from IPubTableBroker impl 2025-11-03 16:13:18 -05:00
John Colvin b4cb6ac856 Add PubSub service modules designed for Dependency Injection
* Get rid of JsonPropertyName attributes in PubSubConfig
  - The Dependency Injection framework (based around dotnet ApplicationBuilder)
    does not support this attribute, and maps property names verbatim to json fields.

* Change PubSubServer json fields to match cs class property names verbatim
2025-11-03 16:13:18 -05:00
John Colvin 83cbb0f47f refactor: Decouple RemoteQueryManager from IManagedSubscriber, IPubTableBroker impl 2025-11-03 16:13:18 -05:00
John Colvin 4e6d1a2335 refactor: Decouple ManagedSubscriber from ISubscriberCore impl 2025-11-03 16:13:18 -05:00
John Colvin 41cd59e6f9 refactor: Only support 1 tag type in IPubSubServerTagHandler 2025-11-03 16:13:18 -05:00
John Colvin f2ace4ae32 refactor: Decouple PubTableBroker from IPublisherCore impl
Reference interface rather than the PublisherCore impl.
2025-11-03 16:13:18 -05:00
John Colvin 8db5655151 fix: Dispose market data sub in various md streams 2025-11-03 16:13:18 -05:00
John Colvin 135db27733 cleanup: Remove unused pubsub tag handler code
* Removed 'IPubSubServerTagHandler.register'
  - All implementations of this function were empty
* Removed 'IPubSubServerTagHandler.supportedTagTypes'
  - This property wasn't referenced anywhere
* Remove IWorker inheritance from IPubSubServerTagHandler
  - No reason these interfaces have to be tied together
2025-11-03 16:13:18 -05:00
John Colvin f240f005f4 refactor: Rename CloudStreamManager.cs -> RemoteQueryManager.cs
This matches the file name
2025-11-03 16:13:18 -05:00
John Colvin 67b1a213b2 Change pub sub schema prefix 'bluto://' -> 'bts-cloud://'
In config json files (not actual code change)
2025-11-03 16:13:17 -05:00
John Colvin e8a16b11bb chore: Fix typos in log msgs
* Bad string interopolation syntax
2025-11-03 16:13:17 -05:00
John Colvin d3f6ca8afb vscode: Add DataStreamPublisher debug cfg to skip build 2025-11-03 16:13:17 -05:00
John Colvin 8e42eb5d18 Add spark service modules designed for Dependency Injection 2025-11-03 16:13:17 -05:00
John Colvin 70d4a33b2b cleanup: Fix benign typos in build scripts
Change BUILD_SPARK_PY var name to BUILD_PY (these scripts build other
projects than 'spark').
2025-11-03 16:13:17 -05:00
John Colvin f289c3d2b0 fix build (warn): Only use Registry on windows 2025-11-03 16:13:17 -05:00
John Colvin 6e77eda16c cleanup: Fix numerous build warnings in DataStreamSuite
Nearly all warnings were due to nullability issues, since the concept of
nullable reference types did not exist when the DataStream code was
originally written.

* PubSubClientMessage
  - Normalize indentation/whitespace
  - Fix some typos
  - Address nullability issues to fix build warnings
  - Modernize code

* Compression related files
  - ZipEntry
  - ZipFile
  - ZipInputStream
  - TarHeader

* RemoteMessage
  - make 'message' nullable to resolve warnings elsewhere (in calling code)

* PubSubSingleTagStateEvent.cs
* PubSubMessageReactor
* PubSubState
* SystemHeartbeatPublisher
* SystemStatsPublisher
* SingleTagData
* ManagedTCPMessageHub
* PubsubDeserializationController
* PublisherCore
* InflaterHuffmanTree
* AsyncTCPConnection_APM
* ThreadSafeQueue
* ManagedSubscriber
* SubscriberCore
* PubSubServerManagedTCPConnection
* AsyncTCPConnection_APM
* SuperMapStream
* MapStream
* TradeBarsStream
2025-11-03 16:13:17 -05:00
John Colvin 41be31a7b0 cleanup: Disable nullability warnings
* BZip2InputStream
* TarInputStream
* Inflater
* InflaterDynHeader
* BZip2OutputStream
* DeflaterHuffman
* ParallelJoinTest
2025-11-03 16:13:17 -05:00
John Colvin 96976b3fcd cleanup: Modernize PubSubRoute code, json parsing 2025-11-03 16:13:17 -05:00
John Colvin 58d6a4534e cleanup: Remove unused DateTime grammar from exp parser 2025-11-03 16:13:17 -05:00
John Colvin bf913f2b99 chore: Add json facilities for DataStream websocket api 2025-11-03 16:13:17 -05:00
John Colvin 003b0a3fa0 refactor: Add handy WebsocketExtensions
* IsSocketStateOpen
* IsClosedProperly
2025-11-03 16:13:17 -05:00
John Colvin de7c94014e chore: Add JsonRpc types for websocket paradigm 2025-11-03 16:13:17 -05:00
John Colvin 714cf3572e cleanup: Remove empty file (clearly added by accident) 2025-11-03 16:13:16 -05:00
John Colvin 3a6bdbc27e editorconfig: Use standard c# capitalization rules
* Use 'PascalCase' for public members (instead of 'snake_case')
* Use 'camelCase' for private members (instead of '_snake_case')
2025-11-03 16:13:16 -05:00
John Colvin bf1ce9467d Add generic ViewStream, BlueRow types/interfaces 2025-11-03 16:13:16 -05:00
John Colvin 906e48743d Add BlueRowColumnMask to hide columns w/ minimal overhead
Less overhead than ProjectStream
2025-11-03 16:13:16 -05:00
John Colvin 33764aaa28 fix: Remove query from literal error stream
The parser can't handle colons in the expression (it throws/crashes)
2025-11-03 16:13:16 -05:00
John Colvin bdfef3d9b1 fix: Remove erroneous "DSL: " prefix in ai prompt stream
This causes parse errors since we extended the dsl syntax to support
named params in the form of "name: arg".
2025-11-03 16:13:16 -05:00
John Colvin 1df508a16c fix Impl IDictionary<int, IBlueRow> in BlueTable 2025-11-03 16:13:16 -05:00
John Colvin 57423e449d feat: Extend dsl to support explicit param names
When using explicit names, the argument order doesn't matter, and args
with defaults are automatically populated if omitted.
2025-11-03 16:13:16 -05:00
John Colvin 934ea76f5a Add async SQLiteEnumerator to read sqlite tables as IBlueRowCollection 2025-11-03 16:13:16 -05:00
John Colvin 97efe8396e Add gen purpose blue row/table utils for data streams
* Add BlueRowReflection<T> so ViewStream implementations can use
  strong row types without needing to implement IBlueRow
* Add default implementations for IDictionary members in IBlueRow to make
  implementing IBlueRow less tedious
2025-11-03 16:13:16 -05:00
John Colvin cc80785d8a ui: Add long/short avg cost lines to ladder 2025-11-03 16:13:16 -05:00
John Colvin a9267394ef ui: Add exec fee columns to fills grid 2025-11-03 16:13:16 -05:00
John Colvin 6939934399 chore: Add exec fee to sec metadata config 2025-11-03 16:13:16 -05:00
John Colvin b6b7be7f25 chore: Add fill_aggressor prop to ITradeLogRow 2025-11-03 16:13:16 -05:00
Taha Afzal 66c0baa150 Adding meta data to DataPacket, NLStream adds nl -> dsl as a name value pair in the meta data. Also removing windows ref from DataStreamStudio.sln 2025-09-24 13:16:28 +00:00
arham 5305b32c04 Fix file path didnt work in linux 2025-09-17 04:28:25 +00:00
arham e6304609f8 changes to make streams run on linux 2025-09-16 15:38:21 +00:00
arham 91d6f80b45 streams ws defaults to nl 2025-09-16 15:33:58 +00:00
arham 21d84d811d simple changes to make stream work with wbesite 2025-09-02 17:17:43 +00:00
arham 77e542efa9 Fix: websocket code 2025-09-02 17:17:43 +00:00
Taha 6b28fc4779 Lot of BlueOrb stream changes 2025-09-02 17:17:43 +00:00
John Colvin c0602ceb93 fix (api server linux): Add missing deps to fix crash on start
The api server likely broke when we reorganized cpp with stuttering dirs.

* Link Catch2 and ssh2
* Add a few .cpp files to the build
2025-09-02 14:06:08 +00:00
John Colvin 6f5d89cceb fix build (api server linux): Trivial cmake changes
* Get rid of old 'cfg_defaults' param in get_proto_sources impl for linux client
  builds
* Make libfmt dep header-only
* Use CMAKE_SHARED_LIBRARY_PREFIX if "PREFIX" is undefined when copying libspark
  to dotnet bin dirs
2025-08-29 19:54:39 +00:00
arham 7baaafbeeb sasalgo local pos working 2025-08-29 15:50:43 +00:00
Adeel Khan 8569b02f5c cleanup: spread websocket. 2025-08-25 11:45:57 +00:00
Adeel Khan 8f0784e6c2 cleanup: logs. 2025-08-25 11:31:28 +00:00
Adeel Khan c15968e17e ADD: py_spread.h.. 2025-08-25 07:51:04 +00:00
John Colvin 0e331229d4 fix build: Add missing constructors for SecuritiesEventLog 2025-08-25 06:07:30 +00:00
John Colvin 45c85310e7 hotfix: Remove 0 qty check in ExecutionReport validation fn
This (hopefully) fixes part of a compounding error where 1) the SprayAndSkewAlgo
sends 0 lot orders, which are rejected by the exchange. Then 2) the REJECT exe
report fails a misguided "validation" check due to its 0 qty, and is never
propagated to the rest of the system.
2025-08-25 05:53:36 +00:00
John Colvin 7b41dbb19a diag: Reduce "Waiting for request" log level to diag in py bridge
It's too noisy for Info
2025-08-24 17:45:21 +00:00
Adeel Khan e6a3116327 FIX: Minor recorder fixes. 2025-08-21 22:12:03 +00:00
Adeel Khan 86ce16790a FIX: Changed log level. 2025-08-21 22:12:03 +00:00
Adeel Khan 897cf10be6 ADD: Support for bbo in MDRecorder & Config support. 2025-08-21 22:12:03 +00:00
Adeel Khan ca27903f63 ADD: Support for Spreads on Bybit. Gateio fixes. 2025-08-21 22:12:03 +00:00
Adeel Khan 1b5f1bfb27 UPDATE: Added back Spots to Bybit MD. 2025-08-21 22:12:03 +00:00
John Colvin 217db51cbc fix: Propagate error message to ExecutionReport in bybit gw
Specifically, the 'rejectReason' field in order update json.
2025-08-21 13:52:57 +00:00
John Colvin c08a893ecf change: Hardcode PostOnly flag for Limit/GTC orders in bybit gw
'PostOnly' is a TimeInForce flag that we don't have in our system. It makes it
so Limit orders that would be filled on entry are rejected. Ali's SprayAndSkew
algo needs this to avoid taker fees.
2025-08-21 13:31:37 +00:00
John Colvin a022f08b49 fix: Don't log error when positions are empty in bybit gw
This particular error log was intended for failed position requests. Since an
empty list evaluates to false in python, we were incorrectly interpreting an
empty result as a failed request.
2025-08-20 15:08:25 +00:00
John Colvin 5af98658d8 fix: Fix typos in bybit_execution_gateway.py
* Change 'new_req' -> 'req'
* Change 'self.exe_report_callback' -> 'self.bridge.on_execution_report'

...this is what you get when you take shortcuts and copy/paste code.
2025-08-18 22:14:55 +00:00
John Colvin 933511f430 fix: Reset last filled qty to 0 on updates with no fills in bybit gw
For example, on status change updates.
2025-08-18 18:54:38 +00:00
John Colvin 63291d7374 fix: Buffer req on ws closed exception just like normal disconnect in bybit gw 2025-08-18 18:31:58 +00:00
John Colvin 30276066f8 cleanup: Remove redundant code 2025-08-18 18:24:05 +00:00
John Colvin 809a66323d fix: Guarantee unique tx id for bybit exe reports
Use a version counter instead of 'time.time_ns' (the latter probably would have
been fine in practice, but the former is better).
2025-08-18 15:54:22 +00:00
John Colvin 5c4ee1a262 fix: Insulate algos from bybit disconnect/reconnect
* Buffer incoming requests while reconnecting
* Sync orders upon reconnect and fire execution reports to account for diff
2025-08-18 15:54:22 +00:00
John Colvin 504f441d2e fix: Count fills correctly in bybit exe gw plugin
* Round fill price and qty to 7 digits in bybit gw plugin
* Remove erroneous line of code that reset filled_quantity to 0 on each
  update (it was causing partial fills to be overcounted)
2025-08-18 15:51:11 +00:00
John Colvin fd51025b00 diag: Increase waiting for admin data log throttle to 5s
This log is particularly noisy if the configured account isn't in the system. It
logs continuously until the account is added via the Risk webapp.
2025-08-18 15:51:11 +00:00
John Colvin f4afd92db2 diag: Get sec name from py_sec instead of var that was std::moved 2025-08-18 15:51:11 +00:00
John Colvin b1fe5b52f6 diag: Change bybit_dev exe gw console level to INFO
I found myself constantly changing it from WARN to INFO while developing/testing.
This is a 'dev' config profile, so it should be tuned for development.
2025-08-18 15:45:43 +00:00
John Colvin 164ca306f5 hotfix: Reject new orders when bybit gw not in READY state 2025-08-15 13:42:33 +00:00
John Colvin a6d23ef4ef devcontainer: Add python extension 2025-08-15 04:00:25 +00:00
John Colvin c0f4090d70 fix: Add error_msg to PyExecutionReport json fmt 2025-08-14 03:54:33 +00:00
John Colvin 63c94e09e7 diag: Add bybit order ticket webapp for testing purposes 2025-08-13 21:39:21 +00:00
John Colvin fc94ec4d46 feat: Automatically reconnect in bybit exe gw plugin
Previously, the plugin would just throw an exception and assert crashed
when this happened (the exception would bubble up to the c++ bridge).
2025-08-13 21:39:21 +00:00
John Colvin e2fdb569c6 feat: Fire position snapshots in bybit exe gw plugin 2025-08-13 21:39:21 +00:00
John Colvin 804c27eb49 fix: Stop bybit websocket stream ping timer on disconnect 2025-08-13 21:39:21 +00:00
John Colvin 6448b4cf90 diag: Add support for custom json formatter in JsonElement
The JsonElement class is primarily used for structured logging, as it provides a
means to format objs as json via printf style formatting outside the "hot" path.
2025-08-13 21:39:21 +00:00
John Colvin e01a825aff fix: Create parent dirs if necessary for py plugin log files 2025-08-13 21:39:21 +00:00
John Colvin 00649e0c14 chore: Hook up py execution gw position callback
Refactor the py bridge placeholder function to accept a vector of position
objs (instead of a single one) to more accurately reflects its c++ counterpart.
It also allows us to pass the account's entire position (for all securities) in a
single call.
2025-08-13 21:39:21 +00:00
arham ae1e9ecb29 Removing docker submodules 2025-08-11 17:03:26 -04:00
John Colvin ff65acba05 fix: Gracefully handle missing column settings in pnl win
Pnl window settings are stored with the workspace on the server (like all shell
window settings), and may originate from an older version of Spark with a
different set of columns. We shouldn't crash if this happens.
2025-08-07 14:33:55 -04:00
John Colvin dc08945719 fix: Check proto HasPrice/QuantityDisplay before accessing props 2025-08-07 14:02:10 -04:00
John Colvin a739ca9a75 fix build: Disable warning in cs Timestamp struct
It is initialized from data stored in shared memory (not a normal struct). The
cs build emits a warning because it detects a field that "is never written to
and will always have a default value of 0."

This code hasn't changed for years. It suddenly becoming a warning-as-error is
likely related to a recent Visual Studio update.
2025-08-07 13:39:21 -04:00
John Colvin ed7b187876 feat: Add ExportToCsv button to fills win 2025-08-07 13:01:06 -04:00
John Colvin 53e1e92a6a fix: Keep existing items in SettingsPersistenceModel filtered sub
Bugs (fixed):
  * SettingsPersistenceModel drops items after initial update in filtered
    subscriptions.

    Cause: The SettingsPersistenceModel subscribe fn only publishes the "added"
    items in each update, and omits pre-existing items. From the observer's
    perspective, pre-existing items appear deleted.
2025-08-05 16:23:02 -04:00
John Colvin eee7af494a fix (fills ui): Preserve sort order during incremental update
Binary search is used to add, remove, and change rows in response to
incremental updates. Needless to say, if the rows become unsorted,
everything breaks.

Bugs (fixed):
  * The fills window crashes sometimes, possibly related to client
    reconnecting to server.

    Cause: This was a 2 part bug.
      1) Changes to existing rows are applied BEFORE the new sort index
         is determined via binary search. If the change affects a
         property in the current sorting criteria, the rows could
         become unsorted and thus invalidate the binary search.

      2) When an existing row is changed so that its new sort index is
         greater than its current one, the new index is not decremented
         by one (it must be since the row is removed before it is
         re-inserted).
2025-08-05 13:47:06 -04:00
Taha Afzal 91a7c35edf Registering Pingpong algo 2025-08-05 15:59:43 +00:00
Taha Afzal 3eaef1a1b6 PingpongAlgo scaffolding 2025-08-05 15:22:53 +00:00
arham 4a70b4910d config: test algos 2025-07-28 13:07:15 +00:00
arham 7697f73ada Fix: change order of plugin loading 2025-07-28 13:03:45 +00:00
arham 029875e0a0 Add: SASALGO 2025-07-28 13:03:26 +00:00
John Colvin 9dc7611c0a fix build: Forgot to add NullableWrapper.cs a few commits back
When I made the change to format min order qty/notional cols with sec
ticks
2025-07-21 17:07:52 -04:00
John Colvin 782cdd7430 ui (orderbook): Use dark theme font color in Algo State grid
It was barely legible to against the dark background (must have been
overlooked when we originally implemented dark theme).
2025-07-21 10:02:38 -04:00
John Colvin 4184278d94 fix: Fix typo in weighted_average fn
Was multiplying distance from x by wrong weight

Bugs (fixed):
  * Pnl window avg cost is wrong when there are both start and day
    positions
2025-07-18 09:54:17 -04:00
John Colvin 0b3fa813f1 tool: Add script to dump structured logs to console 2025-07-17 16:25:23 +00:00
John Colvin e60f2159e1 fix: Prevent python plugins from intercepting kill sig
So the process actually terminates on ctrl+c
2025-07-17 16:25:23 +00:00
John Colvin 610861be20 diag: Shorten BookLevel json field names
To reduce log file size and improve readability
2025-07-17 16:25:23 +00:00
John Colvin 1787460c35 refactor: Rewrite binance usds futs md without ccapi 2025-07-17 16:25:23 +00:00
John Colvin 588b86710f refactor: Rename lib_binance_execution lib_binance
Implementing md and execution for the same exchange in separate projects
has proven to be messy and impractical. I'm renaming this project in
advance of a binance md rewrite which will join md and execution
together in the same lib.
2025-07-17 16:25:23 +00:00
John Colvin 3022a25a10 Add config toggle to batch subscribe binance ufuts md 2025-07-17 16:25:23 +00:00
John Colvin cf30649dd2 fix: Fix elapsed check in ConcurrentQueue waiter 2025-07-17 16:25:23 +00:00
John Colvin afb8444815 diag: Use structured logging in gen md listener 2025-07-17 16:25:23 +00:00
John Colvin fc59247958 cleanup: Remove redundant if check in gen md listener 2025-07-17 16:25:23 +00:00
John Colvin 14c417eeec refactor: Use core::TimeStamp::to_ll in gen md listener
Working towards unifying TimeStamp code
2025-07-17 16:25:23 +00:00
John Colvin 62a6e4340f chore: Add to_ll fn to core::TimeStamp
One step closer to unifying TimeStamp code
2025-07-17 16:25:23 +00:00
John Colvin cce85e45b7 chore: Make a few domain types fmt formattable
So we can pass them directly to LOG statements

* Book
* BookSide
* TradeRecord
2025-07-17 16:25:23 +00:00
John Colvin ff584ee19a refactor: Make pretty enum formatting more performant 2025-07-17 16:25:23 +00:00
John Colvin f146859677 diag: Disable frame_header/payload logs in ws client
They're useless noise. Worse than that, the messages are byte strings
that cause text searches to omit matches (via grep, less, etc.).
2025-07-17 16:25:23 +00:00
John Colvin cf55e4bc1d refactor: Make domain::Security default ctr constexpr 2025-07-17 16:25:23 +00:00
John Colvin a0b1078572 chore: Move a few inlined fns to .cpp 2025-07-17 16:25:23 +00:00
Taha afd229b7c7 Merging 2025-07-15 17:51:28 -04:00
John Colvin bfc7615c9a ui: Add "Is Aggressor" column to fills grid 2025-07-15 14:48:44 -04:00
John Colvin 0023574905 build: Add data stream projs to spark_client.sln
They're already built with the solution as dependencies, so it seems
appropriate to include them. This also fixes intellisense issues in
those projs when you have spark_client.sln loaded.
2025-07-15 10:22:23 -04:00
John Colvin 7e79a21cd4 Add some nifty DataPacket/TableUpdate IObservable utils 2025-07-15 10:13:53 -04:00
John Colvin 6b0030559b fix build: Fill in transaction fee prop getter
TODO: Actually implement maker/taker fee. For now, just return 0.
2025-07-15 10:13:53 -04:00
John Colvin 77caef2640 fix build: Add missing name to fn arg 2025-07-15 10:13:53 -04:00
Adeel Khan edb8a9d516 FIX: Removed Commented Out Code. 2025-07-14 22:35:36 +00:00
Adeel Khan 153631191a FIX: Removed fallbacks(not needed). 2025-07-14 22:35:36 +00:00
Adeel Khan 026a8af893 FIX: Removed Normalize Contract Function-Not Needed. 2025-07-14 22:35:36 +00:00
Adeel Khan 3739532947 FIX: Reduced REST calls. 2025-07-14 22:35:36 +00:00
Adeel Khan 0cb3cadc11 FIX: Added filter to exclude InverseFutures--MD not freezing. 2025-07-14 22:35:36 +00:00
Adeel Khan 36db69dc67 FIX: bybit_md_provider.py works with default symbol format in py_bybit_md_provider.json.--MD Freezes on InverseFutures. 2025-07-14 22:35:36 +00:00
Adeel Khan c7c755a964 ADD: Added support for Linear USDC channel-py_bybit_md_provider.json takes full security names. 2025-07-14 22:35:36 +00:00
Adeel Khan 0e460353a7 ADD: BYBIT MD support for USDC Margined Perps. 2025-07-14 22:35:36 +00:00
Adeel Khan 5198236b45 ADD: BYBIT MD support for Spots and Linear USDT Margined Perps/Futures. 2025-07-14 22:35:36 +00:00
Taha 767fa25d61 Adding comments 2025-07-14 15:18:03 -04:00
Taha 0858eaac23 Polishing BlueOrb streams 2025-07-13 21:21:10 -04:00
Taha Afzal 593601551d Adding FillAggressorFlag o ExecutionReport 2025-07-13 22:43:19 +00:00
John Colvin 036df46b1c deploy: Copy algo plugins from config to runtime dir
So we can deploy algos by uploading them to the config dir on the
server, then bouncing sparkd.
2025-07-07 17:24:13 +00:00
John Colvin 84697a6d65 dev qol: Add slim requirements.txt to binance_dev cfg profile
So it doesn't needlessly install every package in our dev venv when it
builds the container, which can take several minutes.
2025-07-07 16:34:29 +00:00
John Colvin bb550d3f88 fix (deploy): Add PYTHON_REQUIREMENTS docker arg to base image
Docker args must be redeclared in each build stage that uses them.
2025-07-07 16:34:29 +00:00
John Colvin 85bd2da414 cleanup: Remove security from StoredFillEvent
It created an unnecessary dependency
2025-07-07 16:02:42 +00:00
John Colvin cc55fe1acf ui: Format min order qty/notional cols with sec ticks 2025-07-07 12:00:39 -04:00
John Colvin bb64760c62 fix (mview): Add switch case for Venue column comparer
Bugs (fixed):
  * In mview, venue column sorting is broken
2025-07-07 12:00:39 -04:00
John Colvin 8b9525f503 dev qol: Add python.env for plugin intellisense 2025-06-26 04:25:43 +00:00
John Colvin eccfd1ee30 fix: Disable ccapi md provider if venue has empty symbols config
Previously, ccapi exchanges (e.g., binance) would subscribe to EVERYTHING if
symbols config was empty. There are way too many symbols for that.
2025-06-26 04:20:08 +00:00
John Colvin 7b933026bb refactor: Only import fake_md_bridge if running plugin standalone 2025-06-25 16:33:30 +00:00
John Colvin ffe859703a fix: Forgot to add fake_md_bridge.py
Needed to run py md plugins as standalone apps
2025-06-25 16:30:44 +00:00
John Colvin 4756e243fa chore: Add DUCK and MILK to bybit_dev config profile 2025-06-24 19:57:44 +00:00
John Colvin 0133159296 fix: Support integer ticks in sparkd, py plugins
Bugs (fixed):
  * sparkd crashes if DUCK is added to bybit symbols

  Cause: DUCKUSDT qty tick is 10 (previously, we only supported qty
  ticks <= 1)
2025-06-24 18:09:20 +00:00
John Colvin 38afd3055f diag: Add sec name, venue, and type to PyBook json 2025-06-24 18:08:27 +00:00
John Colvin 4f184c2566 feat: Support running bybit_md_provider as standalone app
HACK: I changed it so numpy is imported AFTER python_interop_module to
avoid a strange segfault that only happens when running standalone app.
2025-06-24 18:08:27 +00:00
John Colvin f608497094 fix: Propagate min notional value from py plugins sparkd
It was getting lost in the translation from PySecurity ->
domain::Security -> FixedSecurity.
2025-06-24 15:27:13 +00:00
John Colvin 9035b95612 fix: Propagate newly deleted synths in MDDaemon
There was a typo when determining "deletes" and "undeletes" on synth
changes (i.e., when deleted changes false -> true, and true-> false
respectively).
2025-06-24 10:48:42 -04:00
John Colvin 7483665b5a chore: Set min notional value for secs in bybit md provider 2025-06-24 10:48:42 -04:00
John Colvin 5f141a8030 chore: Add missing TimeInForce enum vals in py 2025-06-24 10:48:42 -04:00
John Colvin 83f1b5b567 dev qol: In envsource.fish, split PATH vars into list
Assume they are semi-colon delimited.
2025-06-24 10:48:42 -04:00
John Colvin 06ad3a1d9e dev qol: Add trade_client_cli.py to test md/exe plugins
Independent of sparkd
2025-06-24 10:48:42 -04:00
John Colvin 01de13bb43 refactor: Pass sec id instead of ptr to FilledPositionEvent
Then we don't need to resolve the security in order to fire the event.
This removes a noisy log that would happen when the security resolution
fails as a byproduct.
2025-06-24 10:48:42 -04:00
John Colvin 155a396af5 fix: Round tick base to power of 10 in default case
Bugs (fixed):
  * Bybit execution gw throws on MILKUSDT orders due to bad price tick.

    Cause: rounding error when converting the small price tick (0.00001)
    to "tick factors" (1/10000). The denominator is computed as
    1/0.00001 = 99999.9...
2025-06-24 10:48:42 -04:00
John Colvin 399f9e61e2 chore: Add security type to py reqs
Venue and sec name alone don't always identify a unique security.
2025-06-24 10:48:42 -04:00
John Colvin 1f67456cc5 cleanup: Remove unused code 2025-06-24 10:48:42 -04:00
John Colvin 9ff7821b59 fix: Propagate bybit gw conn status change to bridge 2025-06-24 10:48:42 -04:00
John Colvin c598f831d0 ui: Change default TimeInForce to GTC
The old default, "Day" orders, isn't supported at crypto exchanges, it
doesn't apply (there is no "end of day").
2025-06-24 10:48:42 -04:00
John Colvin 3ca4cedf54 ui (mview): Add min order qty/notional value columns 2025-06-24 10:48:42 -04:00
John Colvin d919ec5474 fix (mview): Bind cpp column filter to correct prop in vm
Was bound to "currency_filter" (prob a typo due to lazy copy/paste job)
2025-06-21 01:24:04 -04:00
John Colvin 98c2d738b2 fix: Set timeInForce in bybit order reqs 2025-06-20 20:17:01 +00:00
John Colvin cf3f7e6862 fix: Don't send unlisted/fake bybit securities to sparkd
Securities that aren't listed in the instrument-info response from the
exchange shouldn't be in our system.
2025-06-20 16:37:47 +00:00
John Colvin ae868ab74d fix: Use default cash-per-point of 1 at bybit 2025-06-20 16:37:47 +00:00
John Colvin bd22f4779d fix: Request all "pages" of instrument info (not just first) 2025-06-20 16:37:47 +00:00
John Colvin 27cbd51b1c fix: Fire reject report if order ack has errors
Rejects are ONLY observable in the order ack, not the execution report
stream from which all other updates are observed. While the pybit sdk
does expose ack callbacks, it doesn't invoke them on rejects/errors.
2025-06-20 16:37:47 +00:00
John Colvin 02626ce35c chore: Copy a few pybit modules into our code for tweaking
No tweaks have been made (yet). I'm copying the files verbatim first
to give us a frame of reference. Then, when the tweaks are committed,
we'll be able to view them in terms of the diff with the sdk.
2025-06-18 18:39:18 +00:00
John Colvin d219804caf fix: Prepend plugin dir to py path, rather than append
So our modules win import-name conflicts with system/third party ones.
This became a problem when I copied a couple files from the pybit sdk
into our plugins dir in order to apply tweaks.
2025-06-18 18:18:35 +00:00
John Colvin 1e0ec5bb46 chore: Bind __str__ fns for py interop list types
The default __str__ for these list types shows item addresses (not the
items themselves)...a bit of a headscratcher considering the lists'
element types have proper __str__ functions.
2025-06-18 18:14:45 +00:00
John Colvin c281bc49b5 improvement: Set bybit exe gw websocket timeout to 5s
The default (1s) is too strict in practice.
2025-06-17 17:49:52 +00:00
John Colvin b3566155a3 fix: Fix rounding error when creating tick factors 2025-06-16 15:52:09 +00:00
John Colvin a4e39afbb9 ui: Add cash-per-point col to mview 2025-06-16 15:52:09 +00:00
John Colvin efce01d28e vscode: Replace net7 w/ net8 in tasks.json 2025-06-16 15:52:09 +00:00
John Colvin d362022b89 Add c# SyntheticBookCalculator 2025-06-16 15:52:09 +00:00
John Colvin e7d751fb68 refactor: Remove unnecessary constraint on MathUtils.lcm params
Not sure why T ever needed to implement IMultiplyOperators<T, int, T>
2025-06-16 15:52:09 +00:00
John Colvin 50eefc65d4 cleanup: Remove unnecessary py_module.cpp files
We used to have separate modules for md and execution. I forgot
to remove these after unifying them to a single module.
2025-06-16 15:52:09 +00:00
John Colvin 8770de4a87 diag: Reduce book log noise in md plugins
They're too noisy for Info level.
2025-06-16 15:52:09 +00:00
John Colvin e799b6e24b diag: Optimize logging in bybit exe gw plugin
* Format params as json (not python pseudo json)
* Format params lazily
2025-06-16 15:52:09 +00:00
John Colvin 69e338c1ca fix: Use lock_guard instead of lock/unlock calls
I just noticed it was technically possible for the "work thread" (in py
plugin manager) to be started twice, due to improper lock/unlock calls.
2025-06-16 11:08:59 -04:00
John Colvin e8bb64a80f Increase sleep time in bybit_md_provider keep-alive loop
This thread's only job is to stay alive until "stop" is called. It
doesn't need to be particularly fast/responsive. Longer sleep intervals
reduce contention with the "hot" thread that receives market data.
2025-06-16 11:08:59 -04:00
John Colvin 8e2ede596f diag: Add security to assert msg when qty tick deduction fails 2025-06-16 11:08:59 -04:00
John Colvin 9772d0eb77 deploy: Add alphaworx_zen_hk_live-preview configuration 2025-06-16 11:08:59 -04:00
John Colvin 687e2477c0 fix: Assert that acc name length < max_size (not size)
Since we changed the account field from std::array -> bts::core::SmallString
2025-06-16 11:08:59 -04:00
John Colvin 1bd0ba9950 deploy: Install python requirements in docker
No point in using a virtual environment. These containers have one
purpose: run sparkd with a constant, predetermined set of py plugins.
2025-06-16 11:08:59 -04:00
John Colvin ddabc744f2 chore: Update alphaworx_zen_hk_live init.py to query new secret
I decided to put all alphaworx api keys in one secret. It's easier this way.
2025-06-16 11:08:58 -04:00
John Colvin 74b88b003e fix: Fix config dir env var in bybit_dev profile 2025-06-16 11:08:58 -04:00
John Colvin aaf1be09c2 fix: Fix SmallString numeric parsing, make more robust
* Fix by parsing numbers from left to right instead of right to left
  (previously, would parse "17" to 71)
* Support leading +/- signs
* Throw if unexpected char encountered
* Unit tests
2025-06-16 11:08:58 -04:00
John Colvin e3bd9a457c fix: Change thread_id type to size_t (not int64_t) 2025-06-16 11:08:58 -04:00
John Colvin 59f62b6ec9 cleanup: Remove unnecessary signal handler in bybit_md_provider.py
We handle them properly in sparkd now
2025-06-16 11:08:58 -04:00
John Colvin b46e75f7d7 cleanup: Sort gateio symbols in symbols.conf 2025-06-16 11:08:58 -04:00
John Colvin 3f100feaca chore: Add config json for py plugins
Support overrides per venue in a separate file, so symbols can be
changed after deployment using arham's widget.
2025-06-16 11:08:58 -04:00
John Colvin 093da2a938 fix: Init shared_ptr w/ default obj (not null)
Prob a typo
2025-06-16 11:08:58 -04:00
John Colvin 2f17c2b8c5 chore: Fix path in .env file (server/sparkd -> app_sparkd) 2025-06-16 11:08:58 -04:00
John Colvin 33bfdc517a fix build: Remove include of non-existent header
I created the file locally, but ended up not using it and forgot to remove an
include before I pushed.
2025-06-16 11:08:58 -04:00
John Colvin d17be48e6d fix build: Always return value in non-void fn 2025-06-16 11:08:58 -04:00
John Colvin cbaa1df641 fix: Don't compare size_t to -1 in SmallString num parsers 2025-06-16 11:08:58 -04:00
John Colvin 8c23665d65 refactor: Manage all py plugins (md and exe) in one class
All calls to/from python are performed on one thread via a simple
message queue (necessary because of the global interpreter lock).
2025-06-16 11:08:58 -04:00
John Colvin c7db105533 chore: Add pybit to py venv 2025-06-16 11:08:58 -04:00
John Colvin 3f9923239e diag: Log incoming books at Info level in bybit_md_provider 2025-06-16 11:08:58 -04:00
John Colvin 9ae9451359 chore: Add bybit execution config to bybit_dev profile 2025-06-16 11:08:58 -04:00
John Colvin c33e5ddb02 chore: Add numpy to py venv
This is needed for md plugins
2025-06-16 11:08:58 -04:00
John Colvin 54388d3b92 fix: Include fill id in format str for ExecutionReport 2025-06-16 11:08:58 -04:00
John Colvin 17ada3c57c dev qol: Bind PySecurity __format__ to fmt::formatter impl 2025-06-16 11:08:58 -04:00
John Colvin 046f412ec4 refactor: Change floats to doubles in py bridge types
Getting rounding errors with floats
2025-06-16 11:08:58 -04:00
John Colvin c94d11f525 refactor: Use SmallString instead of std::array in ExchangeFillID 2025-06-16 11:08:57 -04:00
John Colvin 2b69846381 Add py bybit exe config to bybit_dev profile 2025-06-16 11:08:57 -04:00
John Colvin bdd5e08b60 refactor: Consolidate all py interop into single lib
It was just too hard trying to make it work with separate libs. The
main issue was getting the python md and execution modules to
recognize PySecurityList and other types defined in the core module.
2025-06-16 11:08:57 -04:00
John Colvin 7c9330650d refactor: Move tick fns into gen-purpose tick_utils.h 2025-06-16 11:08:57 -04:00
John Colvin 98fca5ad7f Change bybit_dev symbols.conf to list bybit futs (not spots) 2025-06-16 11:08:57 -04:00
John Colvin 8a0fcce90d fix: Actually use configured logger in bybit_md_provider.py 2025-06-16 11:08:57 -04:00
John Colvin 112527f9ed dev qol: Reduce noisy logs in PositionServicePNLHelper 2025-06-16 11:08:57 -04:00
John Colvin fc1e20538d build: Add cmake script to sym link py module and plugins
We have several of these (python_core, python_md, python_execution),
so the repetative boilerplate code was starting to pile up.
2025-06-16 11:08:57 -04:00
John Colvin 83acbecf55 deploy: Install/activate py venv in sparkd container 2025-06-16 11:08:57 -04:00
John Colvin 06c494291c dev qol: Add pyrightconfig.json for type hints in py/c++ bridge 2025-06-16 11:08:57 -04:00
John Colvin fe74aed048 Tweak py to get rid of false positive type hint warns 2025-06-16 11:08:57 -04:00
John Colvin b94cdb561e feat: Support python execution plugins 2025-06-16 11:08:57 -04:00
John Colvin 06bd8791c0 refactor: Move py security (and related types) to python_core lib
So the execution gw plugins can use it (as well as the md plugins).
2025-06-16 11:08:57 -04:00
John Colvin b5850cdb02 fix: Add missing switch cases for bybit & gateio 2025-06-16 11:08:57 -04:00
John Colvin 545d76142f fix: Initialize bybit secs as perp futs (not spots) 2025-06-16 11:08:57 -04:00
John Colvin edc2cea60e dev qol: Add wait/notify functionality to ConcurrentQueue 2025-06-16 11:08:56 -04:00
John Colvin e6466f8309 chore: Add CANCEL_ORDER op to binance.env 2025-06-16 11:08:56 -04:00
John Colvin 501be8d6a5 dev qol: Add ConcurrentValueStore, with notify/wait functionality
Uses std::condition_variable
2025-06-16 11:08:56 -04:00
John Colvin 7dea9b3792 fix: Don't raise ex if var isn't defined in req path 2025-06-16 11:08:56 -04:00
John Colvin a3f49b1ce8 dev qol: Add gen-purpose validation fns for execution gws 2025-06-16 11:08:56 -04:00
John Colvin 765237d701 build: Use new gen-purpose python cmake fns in py md proj
Significantly simplifies CMakeLists.txt
2025-06-16 11:08:56 -04:00
John Colvin 97c52410a2 build: Add gen-purpose InitPythonModule, InitPythonPlugin cmake scripts 2025-06-16 11:08:56 -04:00
John Colvin 8b43d93e26 dev qol: Add fn to make Stopwatch and start it at once 2025-06-16 11:08:56 -04:00
John Colvin 14baa7d62e chore: Use same log format as sparkd in py md plugins 2025-06-16 11:08:56 -04:00
John Colvin a054f4cc63 improvement: Set PyBook data in single call to reduce interop overhead 2025-06-16 11:08:56 -04:00
John Colvin 5311128607 Reduce log noise in py md providers 2025-06-16 11:08:56 -04:00
John Colvin 075acdc8eb doc: Improve py md readme
* Put examples in a table
* Show command to create symlinks for intellisense
2025-06-16 11:08:56 -04:00
John Colvin e1fdf6306b refactor: Redesign py md so it doesn't lock the global interpreter
We can only embed one interpreter in the sparkd process, so all py
plugins need to share it.

* Move core py interop code to new lib_python_core. We're about to add a
  new lib for py execution, so it needs to be accesible from both
  execution and md libs
* Fix typo for type hint in py_md_utils.py
* Run py md providers on background thread
* Rename MdSink -> MdBridge
2025-06-16 11:08:56 -04:00
John Colvin 3b9fb2759b build: Add lib_spark subdir first (all others depend on it) 2025-06-16 11:08:56 -04:00
John Colvin 3f89d3b7f7 dev qol: Add to_chrono fns to TimeSpan 2025-06-16 11:08:56 -04:00
John Colvin a6dd0d842d dev qol: Add -> operators to ValueWrapper for convenience
So we can type "foo->" instead of "foo.value()" (much shorter!)
2025-06-16 11:08:56 -04:00
John Colvin d050d2d19a build: Link against system installed ssl to support py interop
Unless we want to build python packages ourselves, the underlying clibs
they use link to the system installed ssl. Notably, the requests module.
2025-06-16 11:08:56 -04:00
John Colvin 942d00a8b1 chore: Add pybind11, pybind11-stubgen to py venv 2025-06-16 11:08:56 -04:00
John Colvin 31cb25ac2e improvement: Generate py stubs for md providers
Provides intellisense for the python_md_module
2025-06-16 11:08:56 -04:00
John Colvin cbb43a22de cleanup: Get rid of unecessary namespace prefixes 2025-06-16 11:08:55 -04:00
John Colvin 9e7b4849f0 cleanup: Get rid of "PyList" classes, bind to stl vector instead
I wasn't aware this was possible until recently.
2025-06-16 11:08:55 -04:00
John Colvin 6e72890153 refactor: Move SimpleOrderKeyMap from binance_execution to lib_spark
It provides basic functionality that will be useful in other execution gateways.
2025-06-16 11:08:55 -04:00
John Colvin c238fce057 chore: Add smart parse and pretty name fns for proto enums
Mainly to ease the pain of representing enums as strings in python md
and execution plugins
2025-06-16 11:08:55 -04:00
John Colvin b8f5db4ee2 refactor: Move ErrorList from binance_execution to lib_spark
It provides basic functionality that will be useful elsewhere
2025-06-16 11:08:55 -04:00
John Colvin 87c8b9862a refactor: Use SmallString std::array in AccountName 2025-06-16 11:08:55 -04:00
John Colvin fbc2d8633d refactor: Use SmallString instead of std::array in ExchangeOrderID 2025-06-16 11:08:55 -04:00
John Colvin d12d798fa5 Add our own "SmallString" class for fixed cap strings
This will replace the std::array<char, N> we use in ExecutionReport, etc.
among other things
2025-06-16 11:08:55 -04:00
John Colvin e75be2da2f refactor: Move ExecutionReportStore to lib_spark
From lib_binance_execution
2025-06-16 11:08:55 -04:00
Adeel Khan 3ae3a7094e CLEANUP: BYBIT Symbols.conf. 2025-06-16 11:08:55 -04:00
Adeel Khan bafa2f71ba ADD: BYBIT MD Working, NaN's also Added. 2025-06-16 11:08:55 -04:00
Adeel Khan 63bd29a49e ADD: BYBIT Securities showing up on frontend. Default Contract Spec 2025-06-16 11:08:55 -04:00
Adeel Khan 5fdc9ca48b ADD: GATEIO md_provider with dynamic contract specs using Batch REST call 2025-06-16 11:08:55 -04:00
Adeel Khan a19e7bc7b4 ADD: GATEIO md_provider with dynamic contract specs 1 REST call per Contract 2025-06-16 11:08:55 -04:00
Adeel Khan 04b0a89644 ADD: GATEIO md_provider with default contract specs 2025-06-16 11:08:55 -04:00
John Colvin 363a43cb8b Add gateio_dev config profile for Adeel 2025-06-16 11:08:55 -04:00
John Colvin c9f9c5f0f6 cleanup: Remove unused spark.conf file
Originally, this spark.conf was hardcoded into compose.yaml, and copied
into the sparkd container on each deploy. It hasn't been used since
config profiles were introduced.
2025-06-16 11:08:54 -04:00
John Colvin ab84a05644 Add py_dummy_md config profile to test py md plugin 2025-06-16 11:08:54 -04:00
John Colvin 4681f22e71 deploy: Move configurations to new stutter dir "app_sparkd" 2025-06-16 11:08:54 -04:00
John Colvin a25e055e35 Add protos for gateio 2025-06-16 11:08:54 -04:00
John Colvin 2386612b51 Add protos for bybit 2025-06-16 11:08:54 -04:00
John Colvin 3606dac145 build: Move onix fix adapter to its own lib
We should keep order execution dependencies out of lib_spark.
2025-06-16 11:08:54 -04:00
John Colvin bf936c9d13 fix build: Move OnixsUtils to from lib_spark to app_sparkd
They're only needed MDP3Main.cpp and ICEMain.cpp. We're one step closer
to removing lib_spark's dependency on onixs.
2025-06-16 11:08:54 -04:00
John Colvin 1aeeb7170e cleanup: Remove unnecessary onixs include from TrafixOrderStateHandler.h 2025-06-16 11:08:54 -04:00
John Colvin 78ad8a3aed feat: Support python md plugins 2025-06-16 11:08:54 -04:00
John Colvin 5ca8dbafaf Add "clear" fn to Book & BookSide
Prelude to python market data bindings
2025-06-16 11:08:54 -04:00
John Colvin b464b9466e Add "smart" parse fns for a couple proto enums
Prelude to python market data bindings. Unlike the normal parse fns, the
"smart" variants aren't case sensitive, and can match strings with or
without TYPE_NAME_ prefixes.
2025-06-16 11:08:54 -04:00
John Colvin 7a5c676e99 build: Move okx execution to its own lib 2025-06-16 11:08:54 -04:00
John Colvin 1e8d699dd1 cleanup: Remove unnecessary onixs include from IDGenerator.h 2025-06-16 11:08:53 -04:00
John Colvin aadd2dd594 cleanup: Remove unnecessary includes from MDP3Listener.h 2025-06-16 11:08:53 -04:00
John Colvin 5163ca4cae refactor: Organize libspark proj w/ stuttering include
This new structure provides several benefits:

* Allows us to install multiple CMake projects in the same destination
  dir (without include path conflicts).
  - This makes shipping the trading algo sdk a trivial excercise. Simply
    copy the lib and include dirs from the install dest. That's it!
* Puts us in a position to break up the monolithic libspark into smaller
  modules, which are easier to manage.

Windows specific notes:

* Get rid of unnecessary script vs_filters.cmake
* Define WebsocketConnectionGateway directly in cs (instead of proto)
  - The proto would no longer be in the include path passed to protoc.
    Moving it to c# because there wasn't a compelling reason for it to
    be a proto in the first place.
* Set spark output prefix to "lib" on windows
  - So we get "libspark.dll" instead of "spark.dll", which conflicts
    with the csharp lib, "Spark.dll".
* Copy libspark.dll to net8.0 & net8.0-windows in a single cmake build
  target

fix build: Copy libspark to both dirs net8.0, net8.0-windows
2025-06-16 11:08:53 -04:00
John Colvin b5d520d568 cleanup: Move TradeSummarySignalDispatcher to libspark
It is needed by TradeSummarySignal, which is in libspark. This prevents a
circular dependency from libspark -> sparkd.
2025-06-16 11:08:51 -04:00
John Colvin 83e4465a3b build: Clean up protobuf cmake scripts
Make them more flexible, reusable. This is a precursor to porting over
python md provider code from another branch.
2025-06-16 11:08:51 -04:00
John Colvin 54cfb343a8 build: Rename external path vars w/ "EXTERNAL_" prefix in cmake
The old "SPARK_" prefix felt like a misnomer.
2025-06-16 11:08:51 -04:00
John Colvin 4e9b360e23 Add a few arithmetic ops to TimeSpan 2025-06-16 11:08:51 -04:00
John Colvin 16bc93b1a0 diag: Log time it takes to start md sources
So we can detect bottlenecks
2025-06-16 11:08:50 -04:00
John Colvin f56d4d9062 refactor: Split MarketDataGateway up into providers/sources
MarketDataGateway still exists, but is greatly simplified. It creates
several `IMarketDataProvider`s, each of which generates a list of
`IMarketDataSource`s. The market data sources provide market data to an
`IMarketDataListener`.

* EmumbaMarketDataProvider
* CCApiMarketDataProvider
* PyMarketDataProvider
* OnixsMarketDataProvider
* ReplayMarketDataProvider

As a side effect, individual py market data sources are now explicitly
limited to a single venue (it probably should have been this way all
along, since the shared memory writers do not expect updates to involve
multiple venues).
2025-06-16 11:08:50 -04:00
John Colvin 36cc186626 cleanup: Remove dependency on quickfix for string to_lower
We shouldn't depend on external libs for trivial things like converting
strings to lower case.
2025-06-16 11:08:50 -04:00
John Colvin 2e78af1756 fix build: Upgrade IronPython to 3.4.2
All csprojs need to be on same version (Spark.csproj was on 3.4.1, while
DataStream.csproj was on 3.4.2)
2025-06-16 11:08:50 -04:00
Taha 70fae06a30 BlueOrb streams and MLP 2025-06-08 21:52:38 -04:00
John Colvin 07b59559a2 dev qol: Reduce md log level in config profile 2025-06-03 02:23:59 +00:00
John Colvin cd1709563b fix: Generate reject exe reports from ack at binance
This only applies to "new" order acks. In all other cases, exe reports
are generated from the order update stream. New order rejects are the
exception because they're not visible in the stream. Presumably, they're
never actually entered into binance's order system (they're "rejected"
before that happens).
2025-06-03 02:21:18 +00:00
arham 94f22ddf40 Update: changed url for okx ws exe in config 2025-05-30 12:59:26 +00:00
arham 7f6c08f956 Fix: positions now go through dispatch incoming 2025-05-29 16:26:05 +00:00
arham 1d737cdf82 Fix: change time to 5s for pos update okx 2025-05-23 16:12:12 +00:00
arham b645192355 Add: OKX position turned on 2025-05-23 15:33:11 +00:00
John Colvin 5ad7755b28 tool: Make open orders symbol param optional in binance.env 2025-05-20 20:08:34 +00:00
John Colvin df4ca124c3 cleanup: Remove redundant script get_binance_md.sh
I should have removed this when I added binance_rest_client.sh
2025-05-20 20:08:34 +00:00
John Colvin 2c8b9ee288 tool: Add envsource.fish to load .env files in fish 2025-05-20 20:08:34 +00:00
John Colvin f02f28e347 tool: Add a few vars to binance.env 2025-05-20 20:08:34 +00:00
John Colvin c8a89c1e8d cleanup: Rename get_binance_md.py -> binance_client.py
This is a more appropriate name (it's not just md anymore).
2025-05-20 20:08:34 +00:00
John Colvin 7ae9e07a30 tool: Use binance.env file in python client
Like the bash impl (binance_rest_client.sh), it gets urls, params
(templates), and api credentials from env vars.
2025-05-20 20:08:34 +00:00
Taha a130e47218 Arb Scoring stream 2025-05-20 00:24:17 -04:00
John Colvin a377f6de7f improvement (ui): Use sec qty display settings in rec report 2025-05-19 12:35:25 -04:00
John Colvin 855373c9e6 cleanup: Remove unnecessary code 2025-05-19 15:57:35 +00:00
John Colvin e1b535b633 feat: Sync exch positions in binance usds futs gw 2025-05-19 15:57:35 +00:00
John Colvin 3b9ac71460 refactor: Abstract out start/stop state machine for gen use
* From binance usds futures gateway
* Thread-safe
* Handles re-entrancy gracefully
2025-05-19 15:57:35 +00:00
John Colvin 9a5075daaf improvement: Use recursive_mutex in ws client to prevent deadlocks 2025-05-19 15:57:35 +00:00
John Colvin 4e7fe811a8 Add ConcurrentUnorderedMap (c++) 2025-05-19 15:57:35 +00:00
Taha 0a6733180d Added SpreadStatsStream 2025-05-18 01:10:33 -04:00
Taha f2169c6bd4 Adding more data to SpreadPerformanceStream 2025-05-16 01:42:32 -04:00
Taha 23ef478391 Spread analytics streams 2025-05-15 15:01:27 -04:00
John Colvin 236a5a9362 fix: Format binance order price up to 5 decimal places
Bugs (fixed):
  * Binance JELLYUSDT order prices get rounded to 4 decimal places, even
    though the price tick is 0.00001
2025-05-13 15:02:02 +00:00
John Colvin cb560bbe33 fix: Sum realized & unrealized pnl correctly in header rows 2025-05-08 14:13:02 -04:00
John Colvin 248f2644ca deploy: Automatically restart "sparkd-symbols" service
This is what we deploy to the alphaworx-zen-hk-live server
2025-05-07 14:49:53 +00:00
arham b87d86f4d6 FIx: okx exe user cancel should be cxl not rjct 2025-05-07 08:38:28 +00:00
arham 8515196dcb Add: okx exe fok orders working 2025-05-07 08:08:40 +00:00
Taha Afzal e5e042f016 Spreader in RH mode now fires FOK, and hedges only whne the FOK is completely filled (ie not as we we might get partial fills) 2025-05-07 06:12:48 +00:00
John Colvin da3593326d Enable FOK orders at binance 2025-05-06 22:20:46 +00:00
John Colvin 313d3a2377 Enable FOK in EMNativeOrder.cpp
This class was originally designed for Emumba execution at deribit
(hence the "EM" prefix), but now serves all crypto exchanges.
2025-05-06 22:19:11 +00:00
Taha Afzal 22f0e126d0 Adding FillOrKill (FOK) to TimeInForce 2025-05-06 19:39:40 +00:00
arham c76b061e20 fmt: okx exe 2025-05-02 09:00:00 +00:00
arham 0107b65281 add code for okx exe position updates 2025-05-02 08:59:46 +00:00
Taha Afzal 6b5496b799 OrderStore does not eleted all orders at sparkd boot-time now 2025-05-02 00:10:48 +00:00
arham 42f9daf9cb Add: okx cancel all after code, not turned on for now 2025-05-01 09:48:06 +00:00
arham bc5ddade7f Add: okx exe small changes to maybe make ping better 2025-05-01 09:42:54 +00:00
arham d8d198f7f4 Add: OKX only handle orders from this session 2025-05-01 09:41:20 +00:00
John Colvin af9eb670c1 build: Update build scripts to target dotnet 8 2025-04-30 12:06:28 -04:00
John Colvin d91837c377 improvement (pnl ui): Get qty display params from sec proto
Instead of config
2025-04-30 03:06:04 -04:00
John Colvin 7725ae33ef fix: Fix total realized/unrealized pnl calc 2025-04-30 01:35:22 -04:00
John Colvin 9f56abaa38 vscode: Replace net7 w/ net8 in launch.json 2025-04-30 01:15:34 -04:00
John Colvin 95444d4a4c fix: Exclude nans from pnl sum 2025-04-30 00:47:49 -04:00
John Colvin a7e1c18fe2 diag: Improve trace logs in binance usds futures gw 2025-04-29 19:49:33 +00:00
John Colvin d11fd23cb0 refactor: Drop "_" suffix in private vars (cpp)
It just looks ugly, and unnecessary with modern tooling. Plus it's ugly.
2025-04-29 19:35:15 +00:00
John Colvin 8499ac0dfb diag: Clean up structured logging in binance order store 2025-04-29 19:35:15 +00:00
John Colvin f4afe348e6 fix: Update timestamps in binance order store when orders change 2025-04-29 19:35:15 +00:00
John Colvin 096424b60b refactor: Use WebsocketClient fn run_in_background in binance gws 2025-04-29 19:35:15 +00:00
John Colvin b4f5e66466 fix: Set okx perp exp to year 2100
Because the year 3000 is too big for signed int64
nanosecond timestamp
2025-04-29 19:35:15 +00:00
John Colvin 69a0bcc9cd fix: Request binance fut md on ccapi dispatcher thread 2025-04-29 19:32:21 +00:00
John Colvin d72ebdf421 perf: Send Binance Usds Futs md rest reqs on background thread 2025-04-29 19:32:21 +00:00
John Colvin 8e93856767 fix: Send exchangeInfo req via our HttpClient instead of ccapi
For binance usds futs (we already did this for spots)

Bugs (fixed):
  * We get a boost beast exception about the read limit being exceeded
    when requesting securities from Binance prod url (testnet works).
2025-04-29 19:32:21 +00:00
John Colvin 19dae6cd34 fix build (cpp release): Don't return reference to local var 2025-04-29 19:32:21 +00:00
John Colvin 24f42550dd fix build (cpp release): Explicitly cast int64 -> uint64 2025-04-29 19:32:21 +00:00
John Colvin c2150ae450 refactor: Move BinanceUsdsFuturesFilterTuple fns to cpp file 2025-04-29 19:32:21 +00:00
John Colvin 40dbfaa068 improvement: Support both key types in binance spots gw
Namely, ED25519 and HMAC-SHA256. Only the former can be used
to login over websocket, which eliminates the need to sign requests
after that.
2025-04-29 19:32:21 +00:00
John Colvin 3756a8a8b7 Add api key type enum to binance exe gateway config
ED25519 keys offer performance benefits over the HMAC-SHA256
keys when using the websocket api, as you can log in on the websocket
connection to avoid signing each request.
2025-04-29 19:32:21 +00:00
John Colvin 46c8dcb18f fix: Add binance spot order newOrderResp type to json/query params 2025-04-29 19:32:21 +00:00
John Colvin 024480f32e improvement: Send binance usds fut orders via websocket api
Unlike the previous implementation, which sent REST requests, this won't
block the dispatch thread waiting for a response.
2025-04-29 19:32:21 +00:00
John Colvin ba525372bb cleanup: Move a few types from orderman_utils to their own headers
So they can be included without depending on OrderService.pb.h
2025-04-29 19:24:41 +00:00
John Colvin 8aa0f1b674 refactor: Move binance state machine out of "spots" dir
So it can be used in the implementation for all binance apis (usds futs,
cross margin, etc.)
2025-04-29 05:24:04 +00:00
John Colvin fc14215d9b fix: Don't assert-crash if ord key isn't found for outgoing req
The i/o is asynchronous, so it's possible for the gateway to receive
a terminal ack from the exchange, remove the order from its index,
then receive a cancel/modify req from an algo that submitted the
req before it knew the order reached a terminal state.
2025-04-29 05:24:04 +00:00
John Colvin 091ed5a327 fix: Don't fire exe report for internal rejections
Internal rejections don't mean the order isn't "working". Only
rejections from the exchange mean that.
2025-04-29 05:24:04 +00:00
John Colvin 30bb9d0837 fix: Make SpotsOrderStore threadsafe 2025-04-29 05:24:04 +00:00
John Colvin 9cb2c580d6 diag: Use structured logging in binance spots gw 2025-04-29 05:24:04 +00:00
John Colvin 2e2150ac1a refactor: Send binance spot orders through websocket api
This offers performance benefits over the REST api, since we don't have
to sign each request.

Note: I consolidated the UserDataSubscription code into SpotsGateway
because it was cleaner than trying to share a single websocket
connection between the two classes (UserDataSubscription and
SpotsGateway).
2025-04-29 05:24:04 +00:00
John Colvin 21d9435a67 Add run_in_background fn to WebsocketClient (cpp) 2025-04-29 05:24:04 +00:00
John Colvin e7e334fb5a Add continous ping function to WebsocketClient (cpp) 2025-04-28 17:12:59 +00:00
John Colvin 61f04c4605 fix: Implement state machine for binance cxl/replace 2025-04-28 17:12:59 +00:00
John Colvin 24fb2e1e39 refactor: Remove 'get_internal_key' delegate from exe report store
It made constructing the exe report store awkward, and smelled like poor
design.
2025-04-28 17:12:59 +00:00
John Colvin ece141c448 Add dedicated types for structured logging 2025-04-28 17:12:59 +00:00
John Colvin 617bac8e74 cleanup: Move CancelOrderMsg impl to .cpp 2025-04-28 17:12:59 +00:00
John Colvin ab217401eb cleanup: Move CreateOrderMsg impl to .cpp, get rid of unused code 2025-04-28 17:12:58 +00:00
John Colvin 77e5c1481b Add security_id/name fns to ExecutionReport for convenience
So callers don't need to check if security is a nullptr
2025-04-28 17:12:58 +00:00
John Colvin a0f77f92a3 cleanup: Move ExecutionReport impl to .cpp
Also add using aliases to get rid of repetitive namespace prefixes
2025-04-28 17:12:58 +00:00
John Colvin 0acdcb150d fix: Format price & qty w/ 6 decimals in binance modify order json 2025-04-28 17:12:58 +00:00
John Colvin ac2f3b9bc6 Add ConcurrentQueue (c++) 2025-04-28 17:12:58 +00:00
John Colvin 0e522f90e5 refactor: Add line num suffix to var name in LOCK_GUARD macro
Rename to "BTS_LOCK_GUARD" while we're at it to avoid potential naming conflicts
2025-04-28 17:12:58 +00:00
John Colvin 40ad1f1b48 Update binance websocket endpoints
The api just changed, and old urls will be retired in May 2025
2025-04-28 17:12:58 +00:00
John Colvin 1cb75d399f devcontainer: Update dotnet7 -> dotnet8 2025-04-28 17:12:58 +00:00
John Colvin cddecc0543 cleanup: Fix benign typo in websocket_client.h
The open_handler fn return type should be "OpenHandler", not
"CloseHandler" (obviously).
2025-04-28 17:12:58 +00:00
John Colvin 484e36b330 refactor: Make timestamp internal value to signed (int64)
I am sick of casting uint64 -> int64. The latter is a much more
common argument type than the former.
2025-04-28 17:12:58 +00:00
John Colvin 8a9c22fa2a vscode: Add Python Debugger entry to launch.json 2025-04-28 17:12:58 +00:00
John Colvin a6e8853cb9 fix build (warns): Account for nullable ref types 2025-04-28 17:12:58 +00:00
John Colvin dafa193283 fix build (windows): Update externals to fix catch include path
I couldn't get the build to work with the new catch2 on windows, though
it does work on linux. Rather than revert the catch -> catch2 include
path changes (again, which do build on linux), I decided to rename the
old windows catch include dir 'catch2'.
2025-04-28 17:12:58 +00:00
John Colvin 23651970c9 fix: Fix improper 'OfType' linq cast
Previously, animations were expressed through polymorphism (derived from the
base class CellAnimation), but the new impl uses composition. This 'OfType' call
was leftover from the polymorphism implementation, an attempt to linq cast
elements of base class (CellAnimation) to BackgroundAnimation.
2025-04-28 17:12:58 +00:00
John Colvin 043efab5a2 build: Upgrade c# to net8.0 2025-04-28 17:12:58 +00:00
John Colvin 5476466d55 Fetch secrets for config profiles with new aws sso config 2025-04-28 17:12:58 +00:00
John Colvin 54cb5a2ab2 tool: Add script to configure aws sso 2025-04-28 17:12:58 +00:00
John Colvin 29bf7d8d7e deploy (fix): Update okx urls
Their aws domain has been retired.
2025-04-28 17:12:58 +00:00
John Colvin 855d2fd790 build: Update vcpkg 2025-04-28 17:12:58 +00:00
John Colvin 8a6a652882 fix build: Get rid of databento's internal nlohmann-json
** Migrate nlohmann-json over to vcpkg in the process **

Make it so databento is built with the same nlohmann-json lib that our
code is built with. Recently, I started getting undefined symbol errors
in databento code from nlohmann headers - presumably a conflict between
their nlohmann dep and ours.
2025-04-28 17:12:58 +00:00
John Colvin 3f288704bf build (linux): Migrate openssl to vcpkg, update to 3.5.0
Databento, as of version 0.32.0, requires openssl >= 3.0

https://github.com/databento/databento-cpp/releases/tag/v0.32.0
2025-04-28 17:12:58 +00:00
John Colvin 98bc78463d build (linux): Migrate catch2 dep to vcpkg (from externals) 2025-04-28 17:12:58 +00:00
John Colvin 6d271ad7cd build (linux): Manage fmt dep with vcpkg 2025-04-28 17:12:58 +00:00
John Colvin 2a17d656a5 fix: Dump json to str in LOG statements
The nlohmann::json obj isn't formattable (i.e., doesn't have a
fmt::formatter<T> specialization)
2025-04-28 17:12:58 +00:00
John Colvin 10436f0f3b tool: Rename binance_orders_client.sh -> binance_rest_client.sh
It supports md and order/trade requests
2025-04-28 17:12:58 +00:00
arham 86b19f0ef0 Add: okx exe treats all cancelations except ioc as rejections 2025-04-26 05:35:46 +00:00
Taha Afzal 502024dd10 Removing an overzealous assert in the spreader, which was being false if the spread_book was not received once 2025-04-23 04:40:16 +00:00
John Colvin 623012b0b0 deploy: Add "sparkd_symbols" image to Dockerfile
Normally, we bake spark config into the image, which is simpler and less
error prone than managing separate configurations per host machine
and/or container. The sparkd_symbols deployment breaks the mold. Config
is mounted to a dir on the host machine so symbols can be added/removed
via config (i.,e., spark.conf) when we bounce sparkd.
2025-04-17 05:53:05 +00:00
John Colvin 5859f0f2d1 deploy (fix): chown bts for whole sparkdata dir in sparkd-symbols
Unlike normal deploys, the config dir isn't copied directly into the image
2025-04-16 13:48:24 +00:00
John Colvin eeffb735ae deploy (fix): Override existing values when loading env files 2025-04-16 13:48:24 +00:00
John Colvin b602997f3a deploy: Build sparkd if any service starts with "sparkd"
Previously, we only built sparkd for the "sparkd" service (i.e., exact match)
2025-04-16 13:48:24 +00:00
Taha Afzal 3530a77b38 Fixing the situation where an IOC cancels, cancels the spreader 2025-04-16 04:26:50 +00:00
arham afdf635b31 fix: changes spread_book_tick_sz from int to double to fix low tick being zero for reverse hedge orders 2025-04-11 07:31:03 +00:00
John Colvin 4d4aefc0a7 fix: Bind SpreaderType dropdown to all algo types in synth ladder
Bugs (fixed):
  * The SpreaderType dropdown only works for plain spreader orders. It
    doesn't work for icebergs, leans, etc.
2025-04-10 16:16:43 -04:00
arham 960a8af77a Fix: OKX futures working now 2025-04-10 05:06:17 +00:00
Taha Afzal 89828659a4 Reducing loggging on reverse hedge evaluation 2025-04-09 20:05:48 +00:00
John Colvin 76f83fd7f5 Revert "Set irrelevant spreader params to null in ladder"
This reverts commit 924386084b.
2025-04-09 15:52:00 -04:00
Taha Afzal d4e40948d2 Changing the logic to determine if a leg is a quoting leg or not 2025-04-09 19:29:50 +00:00
arham 912ab512d2 Add: ioc orders to okx exe 2025-04-09 19:00:24 +00:00
Taha Afzal 5d3eb92175 Spreader gets the spread ticks from the right source now (it was hard coded at 0.1 earlier) 2025-04-09 18:42:06 +00:00
John Colvin 924386084b Set irrelevant spreader params to null in ladder
Depending on the SpreaderType enum value (treat it like a oneof case)
2025-04-09 10:29:15 -04:00
Taha Afzal acab3ac392 Reversehedging fixed 2025-04-09 09:42:47 -04:00
arham d06a62f6ee Format: ran clang format on file 2025-04-09 09:42:47 -04:00
arham 14e3eee0bc Add: config changes to make server controller work and set up hk zen server 2025-04-09 09:42:47 -04:00
Taha Afzal 7277bf52f5 Fixing build: 2025-04-09 09:42:47 -04:00
Taha Afzal b81dc7f5cf Fixing a bug, where the spreader was isidentifying the quoting leg 2025-04-09 09:42:47 -04:00
John Colvin b1a4635345 ui: Enable IOC for Iceberg orders
Previously, it was only enabled for Limit orders.
2025-04-08 13:22:09 -04:00
John Colvin 7c7c689170 fix build: Don't cast to unsigned int when checking bytesRead < 0 2025-04-04 15:36:55 -04:00
John Colvin 116506c8da fix build: Lock databento version to v0.31.0
Previously, we were simply fetching HEAD. This was fine until they
released v0.32.0 (on 04/02/2025), which introduced a new dependency
requirement that broke our build: OpenSSL >= 3.0.
2025-04-04 15:36:55 -04:00
Adeel Khan 3feda49ef1 Fix: Taifex Time Conversion 2025-04-04 15:36:54 -04:00
Adeel Khan 10318be7bc FIX: Taifex Crash Fixed, Added Diag Logs 2025-04-04 15:36:54 -04:00
Adeel Khan f7d525a527 ADD: TBBO & Symbol wise Naming in Recorder 2025-04-04 15:36:54 -04:00
Adeel Khan d2fe9f167a ADD: TWSE Code Complete 2025-04-04 15:36:54 -04:00
Adeel Khan 696d91a5af ADD: TWSE to WebApp 2025-04-04 15:36:54 -04:00
Adeel Khan d575ba0ea8 ADD: TWSE throughout Sparkd 2025-04-04 15:36:54 -04:00
Taha 058b131760 Cleaning up some streams,new prompt etc 2025-04-04 15:36:52 -04:00
John Colvin 57f8f3ff58 ui: Add Sniper and Reverse Hedge controls 2025-04-04 00:19:38 -04:00
John Colvin 775df2850d Add Sniper and Reverse Hedge params to spreader proto 2025-04-04 00:19:38 -04:00
Taha Afzal cb2b15f10b Adding IOC to SimExecution 2025-04-03 23:24:30 +00:00
Taha Afzal 60362a4c95 Adding reverse-hedge to spreader 2025-04-03 23:24:30 +00:00
John Colvin c6d7a8f38c ui (pnl): Use dark theme fg in "Selected" and "Total" labels
Previously, it was black and hard to read since the background is dark.
2025-03-26 14:46:44 -04:00
John Colvin 7931c62b46 ui (pnl): Hide "Override Cpp" menu item
The overrides aren't respected in (new) pnl calcs.
2025-03-26 14:35:23 -04:00
John Colvin 58518451c8 ui (pnl): Show realized/unrealized pnl in row group totals 2025-03-26 14:31:04 -04:00
John Colvin e3bad78aef fix (pnl ui): Get cash per point from security
Previously, it was hardcoded to 1.
2025-03-24 16:54:37 -04:00
John Colvin 8e9ab8f2eb fix: Don't apply cme cpp overrides to non-cme securities
Bugs (fixed):
  * The cash per point for many securities is wrong. For example, it is
    5 for all BTC securities (which is wrong).

    Cause: cpp override config that should be specific to cme is applied
    to ALL securities.
2025-03-24 20:20:53 +00:00
John Colvin 8512f414d6 fix: Don't duplicate animations when migrating pnl window settings 2025-03-24 14:53:09 -04:00
John Colvin 513fc353cf ui (pnl): Increase default width for a few columns
I noticed some data was truncated when connected to Alphaworx prod
server.
2025-03-24 14:39:40 -04:00
John Colvin 4a04beafd2 cleanup: Consoildate order extension methods 2025-03-24 11:19:51 -04:00
John Colvin 79a3251f26 fix: Make security comparison deterministic again 2025-03-24 11:19:51 -04:00
John Colvin 8785e0fecd fix: Bootstrap all observers w/ snapshot on first update
Previously, the Subscribe impl only bootstrapped the first observer
(i.e., only in first call to Subscribe).

Bugs (fixed):
  * The ladder crashes if there are working orders when the
    ticks-per-row setting is changed.

    Cause: The ladder viewmodel resubscribes to order updates each time
    the ticks per row setting changes (as this determines the size of
    the price buckets into which orders are grouped). The IObservable
    "Bootstrap" extension fn doesn't properly clean up state leftover
    from the previous subcsription when this happens.
2025-03-24 11:17:04 -04:00
John Colvin 04251c96b6 ui (pnl): Compute pnl as sum of realized + unrealized
This addresses a subtle inconsistency in the pnl window.

The old "start_pnl" and "day_pnl" values (from IPNLUnit) are computed
with respect to the mid market for both long and short positions, while
the new "unrealized" pnl values are computed with respect to the side of
the market needed to close the position (i.e., bid market for long pos,
ask for short). Consequently, the combined pnl columns weren't equal to
the sum of realized and unrealized pnl.
2025-03-24 10:13:24 -04:00
John Colvin c2d400af1a fix (pnl ui): Don't overwrite menu display settings w/ defaults 2025-03-24 10:13:24 -04:00
John Colvin 133cc16628 ui: Add menu item to open mview from pnl
Populated with the same securities as the pnl window
2025-03-24 10:13:24 -04:00
John Colvin 16f7aede93 fix: Add case for Synthetic sec type in proto conversions 2025-03-24 10:13:24 -04:00
John Colvin 2b9488faff ui: Make pnl default settings pretty
* Make profit (green) and loss (red) easier to read against dark bg
* Hide 'SOD' and 'Day' columns (just show totals)
2025-03-24 10:13:24 -04:00
John Colvin 51b6d592d3 fix (pnl ui): Negate short avg cost when translating to avg price 2025-03-23 15:17:41 -04:00
John Colvin 1eb4ceba0a ui: Add columns for long/short avg pnl
When looking at avg cost, you often really want to know the difference
between it and the current market. These columns provide that information
so you don't need to do the arithmetic in your head.
2025-03-23 15:17:41 -04:00
John Colvin dbf5cadb44 ui: Add columns for realized/unrealized pnl 2025-03-23 15:17:41 -04:00
John Colvin 90f1bb0851 refactor: Move PNL data types to spark_api csproj 2025-03-23 13:08:46 -04:00
John Colvin 547d83e1b8 cleanup: Remove unused pnl enum WaterfallPriceFlags 2025-03-23 13:08:41 -04:00
John Colvin e888149c41 Add order msg throttle to sparkd config profiles 2025-03-20 17:29:43 +00:00
John Colvin b944c3980f Show 6 decimals in spreader order log
So we can better track those rascally iceberg orders w/ small fractional
quantities
2025-03-20 14:30:35 +00:00
Taha Afzal c2a8266106 merging 2025-03-20 01:23:05 +00:00
Mohammad Arham 4277ba61b9 Fix: Okx execution only sends 2025-03-19 13:26:57 -04:00
Mohammad Arham 35cfe9533b Add filter for okx er we that were caused by orders from outside the system 2025-03-19 13:26:39 -04:00
John Colvin d49efef93d Increase ram to 8gb in alphaworx_prod config
The previous value of 4gb wasn't enough, as the container would
occasionally crash with a SIGKILL interrupt.
2025-03-19 15:26:14 +00:00
John Colvin 502ed13456 fix: Round IcebergAlgo remaining quantity
Bugs (fixed, 2nd attempt):
  * Iceberg orders w/ fractional quantities can get into a state where
    they send an infinite stream of orders w/ 0 qty
2025-03-19 14:43:24 +00:00
John Colvin 716c4dd674 tool: Add script to dump sparkd logs to sqlite db 2025-03-19 14:41:23 +00:00
John Colvin 4a2b189a07 diag: Format Price and Quantity w/ up to 6 decimal places 2025-03-17 13:00:15 +00:00
John Colvin a42edaf9fa diag: Trivial fix for log msgs in SpreaderOrder
* Format qty with fractional part
* Remove redundant log
2025-03-17 13:00:15 +00:00
John Colvin cbc142ab15 fix: Account for rounding error in IcebergAlgo fills
Bugs (fixed):
  * Iceberg orders w/ fractional quantities can get into a state where
    they send an infinite stream of orders w/ 0 qty

    Note: I also changed the number of decimal places used when rounding
    order quantities from 6 to 7 (the level of precision guarenteed by
    IEEE 754).
2025-03-17 13:00:15 +00:00
John Colvin 3980f2d030 Add order msg throttle to alphaworx_prod config 2025-03-17 13:00:15 +00:00
John Colvin 00f6d269c9 fix: Set binance routing record "operator_id" to username (not account)
Must have been a typo
2025-03-17 00:51:54 +00:00
John Colvin 10b2d38de8 tool: Add cmd to tail sparkd log file using docker compose 2025-03-17 00:51:54 +00:00
John Colvin 6d4d8f6ac1 fix: Disable ccapi order book cross check for Binance md
When enabled, the checks fail regularly (usually more than once per
hour). Sometimes many of them happen in rapid succession before sparkd
crashes. I'm disabling them in the hopes that the crosses are benign,
and this will stop the crashes.
2025-03-17 00:51:54 +00:00
John Colvin 5db89e1e8d diag: Tone down noisy order management logs
* EMNativeOrder
* OrderRouter - only log routing record result if there are errors
* OrderServiceServer
* OrderStore - also avoid calling pb::to_string twice per change (it's
  expensive)
* ServiceBridge
2025-03-17 00:51:47 +00:00
John Colvin 379a46afa2 Add order msg throttle to sim_dev config 2025-03-15 20:07:14 +00:00
John Colvin d9eff37184 fix: Don't let rounding error accumulate in mview order summarizer
Bugs (fixed):
  * Sometimes mview shows that a security has working orders when it
    doesn't. It appears to be rounding-related as the price and qty of
    the phony working orders is NaN and 0, respectively.

  Cause: Rounding error accumulates in the order summarizer. If it gets
  bad enough, the working quantity (which is computed incrementally as
  orders transition to/from a working state) may still be non-zero after
  all orders become non-working.
2025-03-15 15:21:56 -04:00
John Colvin 3d055de139 cleanup: Rename _tick_factors -> _qty_tick in mview order summarizer
It is specifically for quantity (not price).
2025-03-15 15:21:56 -04:00
John Colvin 648be7dd97 improvement: Don't resort orderbook on every order update
This causes performace issues when the orderbook is sufficiently large
(noticeable when around 20k orders).
2025-03-15 15:21:56 -04:00
John Colvin 2cac9affe4 fix: Use consistent price comp in ladder order stream
Bugs (fixed):
  * The ladder occasionally crashes when orders are changing.

    Cause: Subtle differences between the ordinal and equality comparers
    made it possible for 2 prices to be considered equal by the former,
    and not equal by the latter. Since the ladder indexes orders by
    price, a "Key not found" exception could be thrown if the equality
    comparer detected a price change that the ordinal comparer did not.
2025-03-15 15:21:55 -04:00
John Colvin 30ff3ba707 Add some null checks in LadderGridHost 2025-03-15 15:21:55 -04:00
John Colvin 9d3afd68aa ui: Format Perp and Spot exps as "Perp" and "None" 2025-03-15 15:21:55 -04:00
John Colvin 8c3de1789b fix: Apply leg price mults when computing synth book
This is necessary to support synths with BTC-USDC-SWAP legs at Okx
(because 1 "contract" == 0.01 BTC).

Bugs (fixed):
  * Price multiplier has no effect in synthetic book
2025-03-14 19:32:04 +00:00
John Colvin b251e1d47e build: Generate semantic version header from git
Dump it to console and log file when sparkd runs
2025-03-14 16:00:06 +00:00
arham 6ccd48a8ba Fix, 1e-5 orders working okx 2025-03-13 17:49:14 +00:00
John Colvin 41d84f7f73 Change externals submodule url from ssh to https 2025-03-13 17:08:04 +00:00
John Colvin 795062864d fix: Fix typo in permission filter comparison (change == to !=)
Bugs (fixed):
  * The orderbook and ladder blink/flicker when orders change if logged
    in as non-bts user.

    Cause: The permission filter acted as if the predicate itself
    changed everytime an order changed. As a result, it would fire one
    update to cleared all values, then fire another to add items that
    pass the "new" predicate.
2025-03-13 10:39:33 -04:00
John Colvin 1ff72e9444 fix (ui): Show fractional order qty in activity log
Also fix formatting - change "price @ qty" to "qty @ price"
2025-03-13 10:39:33 -04:00
John Colvin e13b5066a3 fix: Preserve fractional qty when modifying existing order in ticket 2025-03-13 10:39:33 -04:00
Mohammad Arham 5ba7901258 Fix: undo special case for btc swap and add ccp for swaps 2025-03-12 20:47:44 +00:00
Mohammad Arham bc6eb59110 Fix: Change cttype from optional to string 2025-03-12 20:45:27 +00:00
John Colvin 5ae5a5dcbf fix: Add switch case for PositionSide::BOTH in binance futs gw
In this case, the position is signed (unlike LONG and SHORT).
2025-03-12 14:55:56 +00:00
John Colvin 7966bcce77 fix: Change trade id from uint32 to int64 2025-03-12 04:27:54 +00:00
Mohammad Arham ff86b10895 Fix: temp fix to move okx btc swap to config for min tick 2025-03-11 20:35:24 +00:00
John Colvin ea3c0717d8 fix: Remove erroneous assert from binance usds futs gateway 2025-03-11 19:48:11 +00:00
John Colvin d0abca3fd9 Fix symbols and account alphaworx_prod config 2025-03-11 19:47:34 +00:00
John Colvin 7895fe7357 Remove taifex and glbx from NotSimExecution in conf profiles 2025-03-11 15:24:08 +00:00
Mohammad Arham 6f5f4f5cc2 Fix: okx excution working now 2025-03-11 14:25:42 +00:00
John Colvin 066a0ba693 ui: Use dark theme foreground in mini orderbook, fills popups
It was black in a couple places before, making it hard to read.
2025-03-10 14:47:58 -04:00
John Colvin c2b67b4813 fix: Check for null in orderbook root algo type filter 2025-03-10 14:47:58 -04:00
John Colvin ecad456a2d fix: Apply exp filter to popup grids in pnl window 2025-03-10 14:47:58 -04:00
Taha Afzal 2cb3cfdcdd Spreader's orders were hardcoded to be DAY, now it gets it from the parent 2025-03-10 17:22:08 +00:00
Taha Afzal 0ebc8e5b4b Orders in DB are deleted on server bounce 2025-03-10 15:42:12 +00:00
John Colvin 2072105c0f cleanup: Add using aliases to make code more concise 2025-03-10 14:53:07 +00:00
John Colvin d663c86aa0 improvement: Only load synth metadata from disk once in md dumper
Instead of every time a synth is dumped (i.e., do it lazily, outside the
loop body)
2025-03-10 14:53:07 +00:00
John Colvin 4a2dfa46a1 improvement: Print delimiter line when dumping secs to console 2025-03-10 14:53:07 +00:00
John Colvin 6e35db6a24 improvement: Apply filter to shared mem secs in md dumper
Previously, filter only applied to db securities (i.e., when --db flag
was used)
2025-03-10 14:52:39 +00:00
John Colvin b45dc3bc2d fix: Preserve create time when order is updated
Bugs (fixed):
  * Order create time mirrors last update time (it should never change).
2025-03-10 05:24:49 +00:00
John Colvin a1912907b8 Add brief readmes for sparkd config profiles 2025-03-10 05:24:49 +00:00
John Colvin b1b3dcdeb8 Add sim_dev config profile for sparkd 2025-03-10 05:24:49 +00:00
John Colvin 03dc8ef767 Add boto3 to py venv, so we can retrieve secrets from aws 2025-03-10 05:24:49 +00:00
John Colvin 975bf56c2a fix: Don't spam binance w/ listen key requests
Sleep for a few seconds before trying again on failure.
2025-03-10 05:24:49 +00:00
John Colvin 3f58c2ce84 Add script to install aws cli
The aws cli can be used to configure credentials in the shell in order
to retrieve secrets (e.g., production api keys for alphaworx, etc.).
2025-03-10 05:24:49 +00:00
John Colvin 96f28acf91 Add dev config profile for sparkd 2025-03-10 05:24:49 +00:00
John Colvin c2c2658e21 Add alphaworx_prod config profile for sparkd 2025-03-10 05:24:49 +00:00
John Colvin d6b8e359cd Expand environment vars in binance gw config 2025-03-10 03:05:59 +00:00
John Colvin 84a0850d95 Tweak binance_dev config profile
* Factor out repeated symbol list into SymbolLists entry
* Change "binance_dev" account to "BINANCE_DEV" in exe gw json
* Adjust log level
* Remove unused config:
  - ExchangeSubAccounts
  - JetFIX_Connection.max_order_msgs_per_window
2025-03-10 03:05:59 +00:00
John Colvin 4effdc4160 diag: Log when http buffers are resized in binance exe gws 2025-03-10 03:05:59 +00:00
John Colvin b9fe39f6e3 refactor: Make MDRecorder join the run thread in destructor
This wasn't broken, but detached threads bother me.
2025-03-10 03:05:59 +00:00
John Colvin b555850015 build: Package & deploy sparkd with docker compose 2025-03-10 03:05:59 +00:00
John Colvin 23794fa30d devcontainer: Add docker in docker
Also remove default port bindings in devcontainer (so the "docker in
docker" sparkd can use them). It's easy enough to manually bind ports
in vs code.
2025-03-08 05:20:08 +00:00
John Colvin 90b5a309a3 cleanup: Make a couple fns const
Probably a typo, since these fns don't mutate anything.
2025-03-08 05:20:08 +00:00
John Colvin 07beda3fc6 fix: Support fractional show qty in iceberg orders 2025-03-07 20:42:24 +00:00
Taha Afzal e3c1ea70f2 Spreader orders can now be entered in fractional qty 2025-03-07 05:24:39 +00:00
Mohammad Arham 93b93969f5 Add: config to control databento md better 2025-03-06 02:44:40 +00:00
Mohammad Arham 0a10111846 Small changes to make cboe md work 2025-03-06 02:44:07 +00:00
John Colvin eee00cc407 diag: Integrate websocketpp with our logging system
* Unifies log message structure for improved readability
* Grants us runtime control over log levels via config
2025-03-05 20:48:14 +00:00
John Colvin 0eb58eaba8 fix (preemptive): Pass websocket io_context to PImpl ctr
The 'io_context' member var is accessed in the constructor (it must be
initialized before that happens).
2025-03-05 20:42:50 +00:00
John Colvin 12f6733d1e cleanup: Remove unnecessary account mapping in binance_dev conf 2025-03-05 18:04:04 +00:00
John Colvin 93a0984ed1 Filter out non usdc/usdt spots in binance_dev conf 2025-03-05 18:04:04 +00:00
John Colvin b7e9c393a0 fix: Don't sign binance listen key requests
The spots and usds futures apis ignore the unnecessary signature params,
but the cross margin api responds with an error.
2025-03-05 18:04:04 +00:00
John Colvin aa602b3fd6 tool: Add cross margin reqs to binance.env 2025-03-05 18:04:04 +00:00
John Colvin b4b8cabdae tool: Add listen key reqs to binance.env 2025-03-05 18:04:04 +00:00
John Colvin cfe06869b3 tool: Support empty query params in binance_orders_client.sh 2025-03-05 18:04:04 +00:00
John Colvin b177ed1cc0 fix: Include listenKey param in listen key refresh req (binance)
The cross margin api requires this. Spots and futures apis don't, but
including it for them is harmless. I also changed the response json impl
to treat the 'listenKey' field as optional (it's omitted in the cross
margin api).
2025-03-05 18:04:04 +00:00
John Colvin ff17a64933 cleanup: Remove unnecessary includes 2025-03-05 18:04:04 +00:00
John Colvin 81a1fe11f6 feat: Add binance gw for cross margin orders
Spots only
2025-03-05 18:04:04 +00:00
John Colvin ad5bf88fcf refactor: Rename user_data_streams dir, ns without plural 's'
There is one user data stream for Binance Spots, and another for Usds
Futures. We model both of these user data streams in separate folders,
so each "user_data_stream" dir represents a single stream.
2025-03-05 18:04:04 +00:00
John Colvin e15f155ac9 refactor: Abstract out common binance gw code
For all sub-venues to use
2025-03-05 18:04:04 +00:00
John Colvin 6d6822cd65 refactor: Rename binance exe gw filenames so they are unique
The debugger gets confused when a particular lib contains multiple
source files with the same name (in different dirs).
2025-03-05 18:04:04 +00:00
John Colvin ace0c70516 fix: Trim account name string when converting to Fill proto 2025-03-05 18:04:04 +00:00
John Colvin e9e5059bc2 fix: Disable CURLOPT_FAILONERROR to always get response body
Even when the http error code indicates failure. The payload may contain useful
information about the error.
2025-03-05 18:04:04 +00:00
John Colvin cde72f51a0 Improve logging/error reporting in binance execution gw
In the process, consolidate binance exe report stores into single class.
2025-03-05 18:04:04 +00:00
John Colvin e1fd49ae32 cleanup: Specify bts account directly in binance gw config
This eliminates the need for an ExchangeSubAccountResolver.
2025-03-05 16:00:43 +00:00
John Colvin 274f3f98e4 cleanup: Replace includes w/ fwd declares in header 2025-03-05 16:00:43 +00:00
John Colvin a4b7d84318 fix: Reject orders w/ invalid TimeInForce at binance
Do this internally (before sending them to the exchange)
2025-03-05 16:00:43 +00:00
John Colvin be17205094 fix: Report internal reject msgs to ExecutionReport
For Binance execution gateways (spots and usds futures)
2025-03-05 16:00:43 +00:00
John Colvin 6ef9bee8e4 Add reconnect behavior to WebsocketClient 2025-03-05 16:00:43 +00:00
John Colvin 2c8b3563f1 cleanup: Perform basic init in WebsocketClient ctr 2025-03-05 16:00:43 +00:00
John Colvin 71f7e64ccb Move config profiles dir to server/sparkd 2025-03-05 16:00:43 +00:00
John Colvin c31b44ecfb diag: Log when config server starts and show url 2025-03-03 19:32:16 +00:00
John Colvin 19efb77e07 tool: Add some usds futures requests to binance.env 2025-02-24 15:12:45 +00:00
John Colvin 76de640421 diag: Show Binance sub-venue in log message prefix
Otherwise it's hard to tell which Binance venue the log is about (e.g.,
spots, usds futures, etc.).
2025-02-24 06:16:22 +00:00
John Colvin fb030742d3 Clean up binance_orders_client.sh a bit, add docs 2025-02-24 06:06:15 +00:00
John Colvin 73608f1b45 perf: Run each ccapi md session on its own thread 2025-02-24 06:04:52 +00:00
John Colvin 58aebe7002 diag: Reduce log noise in CCApiMarketDepthAdaptor 2025-02-24 06:04:52 +00:00
John Colvin 299cb0b29e diag: Log when curl_global_init/cleanup is called
It should only happen once for the whole app (so it shouldn't be noisy).
2025-02-23 20:08:52 +00:00
John Colvin 06663f711e improvement: Only call curl_global_init/cleanup once
Avoids unnecessary overhead (plus their docs say it only needs to be
done once for the whole app). These fns have "global" in their name for
a reason.

https://curl.se/libcurl/c/curl_global_init.html
2025-02-23 20:08:52 +00:00
John Colvin 51479f4885 fix: Disable signal handling in curl
This avoids a strange stack corruption bug related to set/longjmp.

https://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame
2025-02-23 20:08:50 +00:00
John Colvin 5b520ba241 cleanup: Get rid of custom fmt::formatter for security proto
Security proto objects are large, so we shouldn't pass them by value to
the log system. The custom formatter's presence makes this easy to do by
accident.
2025-02-23 19:05:42 +00:00
John Colvin f0651a5341 tool: Add flag to roll logs without running sparkd 2025-02-23 15:36:10 +00:00
John Colvin 897e0990a2 diag: Improve logging in SecurityCache 2025-02-23 15:36:10 +00:00
John Colvin 30e1ae5c3c Add Binance execution to config profile 2025-02-23 15:36:10 +00:00
John Colvin efe83f81ec fix: Index live securities (as well as db ones) in SecurityCache 2025-02-23 15:36:10 +00:00
John Colvin 5fb21ada97 diag: Add sec id to PositionMap log 2025-02-23 15:36:10 +00:00
John Colvin 58edd07f6b fix: Check for empty string in strn fns 2025-02-23 15:36:10 +00:00
John Colvin 9d81354c3a Add binance_orders_client.sh for quick/easy troubleshooting 2025-02-23 15:36:10 +00:00
John Colvin 0f37be89a4 Fix typos in binance.env 2025-02-23 15:36:10 +00:00
John Colvin f6586fe636 Activate ccapi exchanges according to md config at runtime
Previously, the list of exchanges ccapi would activate was determined at
build time.
2025-02-23 15:36:08 +00:00
John Colvin 7827ccc0e7 Update externals to get ccapi v6.12.0 2025-02-23 15:35:35 +00:00
John Colvin af6a2acc0a perf: Send Binance md rest reqs on background thread 2025-02-23 06:10:36 +00:00
John Colvin 84abf59a55 feat: Add binance spots execution 2025-02-23 06:10:36 +00:00
John Colvin 2fc1d30bf1 Add binance_usds_futures_testnet config profile
To activate this config profile, run:

tools/sparkd_profile.sh binance_usds_futures_testnet
2025-02-23 06:10:36 +00:00
John Colvin 4dcd5d63a1 tool: Add script to easily switch between spark.conf profiles 2025-02-23 06:10:36 +00:00
John Colvin 63e5697b20 Unify conf and conf_json dirs
Put all files (.conf and .json) in sparkdata/conf. This allows us to
implement config profiles via symlink.
2025-02-23 06:10:36 +00:00
John Colvin 8e5cb100a7 fix: Assign BTS account to ExecutionReport
Not external exch account
2025-02-23 06:10:36 +00:00
John Colvin 938bee1817 Validate ExecutionReport before invoking callback 2025-02-23 06:10:36 +00:00
John Colvin d48af15480 diag: Log warning if ExecutionReport acct not same as order def 2025-02-23 06:10:36 +00:00
John Colvin e3b2e98cd9 fix build: Add JsonReader.cpp to client & api server builds 2025-02-20 00:55:48 -05:00
John Colvin ee53fb854c ui: Make buy/sell button bg darker in order ticket
The buttons looked washed out; too bright, yet pale.
2025-02-20 00:49:59 -05:00
Taha d36e2d61f6 Fixing PNL for both crypto and non crypto 2025-02-19 19:06:12 -05:00
John Colvin bfff3ea5fe fix (preemptive): Check for null sec in ExecutionReport::to_string
I don't think it's ever a nullptr in practice, but if that changes, this
fn shouldn't seg fault.
2025-02-19 23:14:18 +00:00
John Colvin 059c03d289 cleanup: Removing docker submodules again
...they're back. It's like Night of the Living Dead submodules!
2025-02-19 22:50:29 +00:00
John Colvin 8c561f7886 improvement: Pass account/order updates by move (not value) 2025-02-19 22:50:29 +00:00
John Colvin 288567d1eb cleanup: Pass JetFIX cfg directly to binance usds futs exe gw 2025-02-19 22:50:29 +00:00
John Colvin ecfc5405fe refactor: Move macro defined json fns to cpp file
Some fns are big (shouldn't be inlined); plus its best to avoid exposing
macros in headers.
2025-02-19 22:50:29 +00:00
John Colvin 8db34a2a11 Add json & fmt formatters for order & sec protos 2025-02-19 22:50:29 +00:00
John Colvin 91094c4d84 refactor: Make parse_uint support generic char iterator 2025-02-19 22:50:29 +00:00
John Colvin dae8c85d73 fix: Check strlen when making order/fill keys
Although the string data is stored in a fixed size array, it isn't
necessarily the full length.
2025-02-19 22:50:29 +00:00
John Colvin abdd98082f Move BrokerProxy do_work fn to cpp
Doesn't make sense to inline; it's too big.
2025-02-19 22:50:29 +00:00
John Colvin 2f6aa785ab Use object_pool wrapper to count allocations and assert if too many 2025-02-19 22:50:29 +00:00
John Colvin 7acf1f7043 diag: Improve logging in PositionMap 2025-02-19 22:49:30 +00:00
John Colvin 1c3c7a078a diag: Improve logging in binance usds futures exe gateway
Also propagate error msgs through the execution report (so we can see
them in the front end).
2025-02-19 22:49:30 +00:00
John Colvin 93ace52bfa diag: Periodically log ActorThread work cycle speed
So we can better detect slowness, blockage, etc.
2025-02-19 22:49:30 +00:00
John Colvin b5d97c0b1b refactor: Consolidate ExecutionGatewayProvider/Factory code
It's easier to read and manage code when you're not constantly jumping
to and fro across a bunch of files.
2025-02-19 22:49:30 +00:00
John Colvin 284aa53ad4 Add script to run sparkd and roll over log w/ timestamp suffix
So each run gets its own log file
2025-02-19 22:49:30 +00:00
John Colvin 8a9952f52d diag: Improve logging in ServiceBridge 2025-02-19 22:49:30 +00:00
John Colvin 748aafaab3 cleanup: Remove unnecessary includes 2025-02-19 22:49:30 +00:00
John Colvin 3ba30c70de cleanup: Normalize indentation in orderman_cfg.conf
It was all over the place before; unreadable
2025-02-19 22:49:30 +00:00
John Colvin e72f465999 diag: Improve logging in OrderManager 2025-02-19 22:49:30 +00:00
John Colvin f3d93f7749 fix leak: Replace object pool raw ptrs w/ shared ptrs 2025-02-19 22:49:30 +00:00
Mohammad Arham 8d142b8a74 Fix: forgot to push a file 2025-02-19 03:36:14 +00:00
Mohammad Arham 8210b9f35f Fix: change onix exe so it uses dispatch fix msgs 2025-02-19 03:33:00 +00:00
Mohammad Arham 12b20b05ce Fix: OKX executions sends er only when dispatch called 2025-02-18 16:02:10 +00:00
Mohammad Arham 72efbccdbd Fix: USDC swaps md working for okx 2025-02-14 23:02:49 +00:00
Mohammad Arham 33643e9eab Fix: make log resize work again 2025-02-14 22:59:35 +00:00
Mohammad Arham 0f6dc6f40b Fix: Typo in Okx execution 2025-02-13 23:38:27 +00:00
Mohammad Arham 8c778f5e41 Fix: Added tdmode to config for okx execution 2025-02-13 23:32:53 +00:00
Mohammad Arham f308654200 Fix: okx execution fix large spreader orderd 2025-02-13 23:27:32 +00:00
Mohammad Arham bdf5b980fc Add: config for sim trading 2025-02-13 22:06:23 +00:00
Mohammad Arham 914ac597cd Vesrsion: Update to v2.2.5 deploying to jame prod 2025-02-13 14:40:27 +00:00
Mohammad Arham 4aa6a77680 Fix: OKX execution works as long as spreader not to big 2025-02-13 06:09:43 +00:00
Mohammad Arham 6b59dd84ee Fix: Ordermanager will not try to remake orders from er after restart 2025-02-13 06:09:01 +00:00
Mohammad Arham 4e2c5f2d17 Fix: Okx USDT perps fix re added 2025-02-13 06:08:27 +00:00
Mohammad Arham 9a1b94a1b0 Add: fill id to okx execution report 2025-02-13 01:13:50 +00:00
Mohammad Arham 13b3c55771 Change: lst msg only entered if error 2025-02-12 23:13:59 +00:00
Mohammad Arham d0a6fcdb9c Add: Stricter Assert if websocket goes down okx execution 2025-02-12 22:23:53 +00:00
John Colvin 1eb13028c1 ui: Get qty display settings from md instead of config 2025-02-12 15:59:51 -05:00
John Colvin 424e5ff16b Get Spark version at runtime via reflection
So it doesn't need to be hardcoded per release
2025-02-12 15:59:51 -05:00
John Colvin bed389885b Support qty tick size 10e-9
Some spots at OKX need this (e.g., BTC-AED)
2025-02-12 20:42:28 +00:00
John Colvin 85cf25a57a fix: Get min qty and min increment from correct fields in okx json
"lotSz" represents min qty, and "minSz" represents min qty increment. We
had them backwards before.
2025-02-12 20:36:40 +00:00
John Colvin 61471d4675 diag: Show qty info fields in FixedSecurity json 2025-02-12 20:34:48 +00:00
John Colvin d83435d49d Move FixedSecurity to_json fn to cpp
Doesn't make sense to inline; it's too big.
2025-02-12 20:34:47 +00:00
John Colvin 582fc10dce Add get_binance_md.sh for quick/easy troubleshooting 2025-02-12 20:34:47 +00:00
John Colvin 5969985d24 cleanup: Remove unnecessary includes 2025-02-12 20:34:47 +00:00
John Colvin fabe7d665c devcontainer: Install clang-format extension 2025-02-12 20:34:47 +00:00
John Colvin 7ce75bb8e7 devcontainer: Increase ram and shared mem
In order to support large symbol sets for Alphaworx
2025-02-12 20:34:47 +00:00
Mohammad Arham b8e1c3cd60 Add: Fix build Issue 2025-02-12 19:48:52 +00:00
Taha 4909944694 Fixing PNL for perps (its base don mid market now) 2025-02-12 11:20:55 -05:00
Mohammad Arham 7258af09b4 Add: Basic Okx execution working 2025-02-12 11:55:50 +00:00
Mohammad Arham 7f2ce6c1a9 Add: Okx execution cfg 2025-02-12 11:06:45 +00:00
Mohammad Arham 4835725e1c Add: diag logging to gttp client 2025-02-12 11:06:09 +00:00
Mohammad Arham 6a9e8de475 Deployment: Updating version numbers 2025-02-11 05:06:21 +00:00
Taha Afzal 4639951acc Fixing Sprader's reportin of its filled qty 2025-02-11 03:04:30 +00:00
Taha d8de211aac Front end does NOT filter nay CBOE or CME expirations 2025-02-10 17:49:14 -05:00
John Colvin 87394bc60b ui: Increase Currency col width so filter icon isn't clipped 2025-02-10 15:31:31 -05:00
John Colvin beba30f65c fix (ui): Remove temp hack that assumed synth qty always notional 2025-02-10 15:31:31 -05:00
John Colvin 2d58424d8f ui (mview): Hook up currency filter in vm
Make it a discrete values filter (listbox) while we're at it. This seems
more practical than regex.
2025-02-10 15:31:31 -05:00
John Colvin d1bbfa3791 Revert batch subscribe change in Binance 2025-02-10 19:59:42 +00:00
John Colvin aab17f460f cleanup: Remove docker submodules again
They just keep coming back.
2025-02-10 19:59:32 +00:00
John Colvin 461ef53322 devcontainer: Clean up and simplify
* Remove mounted dirs (the repo is automatically persisted in a volume)
* Remove user specific shell env initialization scripts (they're more
  trouble than they're worth, often break the devcontainer build)
* Remove redundant settings already defined in settings.json
2025-02-10 16:55:53 +00:00
Taha Afzal 2486bc90b2 Spreader is hooked to the priceCal dropdown while creating the spreader 2025-02-10 03:10:11 +00:00
John Colvin 1c5cbaf60b Change: Update version to 2.2.4-1 2025-02-08 13:19:41 -05:00
John Colvin b8de447cfb fix (pnl ui): Assume rows w/ exp DateTime.MinValue are Spots
Not the most elegant fix, but it'll do for now.

Bugs (fixed):
  * The Pnl window groups perpetual futures with spots
2025-02-08 13:18:27 -05:00
Mohammad Arham 8bfc2d40f9 Update: V2.2.4 pnl working now 2025-02-08 12:43:13 -05:00
Taha c09fd71c94 Fixing PNL for spots 2025-02-08 06:55:07 -05:00
Mohammad Arham f3b037553e Add: Batch subs in binance sub 2025-02-08 06:44:10 +00:00
Taha Afzal d386d93166 SpreaderChildOrder::on_order_udpate, changing an asserrt to an error lgo to monitor condition 2025-02-07 15:06:16 +00:00
Mohammad Arham 5217742c71 Add: SendingTime tag to cboe execution 2025-02-07 12:26:11 +00:00
Taha c59ec95460 Sheets and spreadbox theos can now optionally be set to market mid 2025-02-06 17:42:12 -05:00
Mohammad Arham 70f01583ec ADD: CBOE TIME and SALES working 2025-02-06 20:42:58 +00:00
Mohammad Arham 66d2f9eb63 Add: Quote algo updates, theos removed 2025-02-06 03:08:46 +00:00
John Colvin 4625bed8b8 Change: Update version to v2.2.3-1 2025-02-05 17:01:11 -05:00
John Colvin f18fb332b5 fix (orderbook): Make ColumnKeys enum 64bit
We rely on bit flags to indicate "dirty" columns in the vm. Need to do
this now that we have > 31 columns.
2025-02-05 17:01:11 -05:00
John Colvin 8746a6f5ca fix (orderbook): Refresh Notional Value view when it changes in vm 2025-02-05 16:30:13 -05:00
John Colvin 702d47abb5 fix (orderbook): Fix typos in default settings for Notional Value 2025-02-05 16:29:29 -05:00
John Colvin bf2a54717c ui (orderbook): Add Filled Notional Value column 2025-02-05 16:13:34 -05:00
John Colvin 9db779fb25 fix (hack): Assume synth quantities are always notional values 2025-02-05 16:07:07 -05:00
Mohammad Arham f55ddec708 Add: Vesrion logs to bothserver and client 2025-02-05 09:50:33 -05:00
Mohammad Arham 6e942dd2d4 Change: Updated version to v2.2.3 2025-02-05 08:38:31 -05:00
Mohammad Arham 142310a17a Add: SPXW to default for cboe 2025-02-05 12:16:47 +00:00
John Colvin 7f99a89a95 ui (fills): Add notional value column 2025-02-05 00:00:41 -05:00
John Colvin bfce663b4b fix (orderbook): Hook up notional val filter in vm, persist settings 2025-02-04 23:42:58 -05:00
John Colvin 33c743643d fix (orderbook): Include fractional qty in notional val 2025-02-04 23:34:47 -05:00
Taha Afzal 858e0a389b Fixed the issue where a notioanal quoter would send a 0 qty order 2025-02-05 04:16:53 +00:00
Taha Afzal 9c3bb85674 Notional qty handling in spreader 2025-02-05 04:16:53 +00:00
Taha Afzal f4930e8cd4 Added notional to PriceCalc type for the sprader, added code to compute notional books in SpreaderUtils 2025-02-05 04:16:53 +00:00
John Colvin f21e7f3602 ui (orderbook): Show synth notional value in terms of first leg 2025-02-04 16:54:37 -05:00
John Colvin b56c35aca4 ui (ladder): Show synth notional value in terms of first leg 2025-02-04 16:54:37 -05:00
John Colvin e6ac83196e fix (benign): Actually dispose _vm_sub in LadderGridHost 2025-02-04 16:54:37 -05:00
John Colvin 7b6f74fc4a cleanup: Get rid of 'mimic tt' ladder config
This config was leftover from custom DJ work. We no longer need it.
2025-02-04 16:54:37 -05:00
John Colvin f754953fac cleanup: Remove duplicate equality check 2025-02-04 16:54:37 -05:00
Taha 2dd1143d9f Fixed spx spreadbox theos 2025-02-04 16:31:17 -05:00
Mohammad Arham e1ee3275d8 Add: Filter secs upto n months databento 2025-01-31 21:46:11 +00:00
John Colvin 2549e406f3 fix: Compute notional value correctly when ticks per row > 1
It's the sum of price * qty for each level in the row. Previously, we
were multiplying the total qty by the same price (smallest in the row).
2025-01-29 16:28:28 -05:00
John Colvin d5024f1858 fix build: Don't check if unsigned int < 0 2025-01-28 20:20:06 +00:00
John Colvin 3623ac96db fix: Multiply array size by sizeof(T) 2025-01-28 20:19:51 +00:00
John Colvin 19834625f9 fix: Don't fall through switch case 2025-01-28 19:59:32 +00:00
John Colvin c8fc649b6b fix build: Pass config arg to cmake --build
For Ninja Multi-Config builds (i.e., api server and data streams)
2025-01-28 19:59:09 +00:00
John Colvin 6b1fc1c413 cleanup: Remove unused code 2025-01-28 19:59:09 +00:00
John Colvin 6f086532ac cleanup: Convert co2::TradeMessage in DomainSecurityUtils
Instead of domain::TradeRecord constructor. The domain types should have
minimal dependency on protos.
2025-01-28 19:59:09 +00:00
John Colvin dc66282225 cleanup: Rename UINT32 -> VALUE in qty macro names
Should have done this when we changed qty primitive from uint32 to float
2025-01-28 19:59:09 +00:00
John Colvin e2c74c0bb3 cleanup: Don't redefine CONTAINS macro 2025-01-28 19:59:09 +00:00
John Colvin e89d50eec1 cleanup: Move large fns from header to cpp 2025-01-28 19:59:08 +00:00
John Colvin 8bebbc278d cleanup: Get rid of 'using namespaces'
In headers, this is an objectively bad practice, since the type aliases
leak into dependent code and can cause unexpected naming conflicts. In
source files, it is debatable, but I prefer explicit aliases of the form
'using Foo = Bar'. The intentions are clear, and the resulting code is
naturally resistent to naming conflicts.
2025-01-28 19:59:08 +00:00
John Colvin 2e6d12c9c1 cleanup: Remove unnecessary includes 2025-01-28 19:59:08 +00:00
John Colvin 102a5e23ea vscode: Add Tests Sparkd entries to tasks.json 2025-01-28 19:59:08 +00:00
John Colvin 582dc791db vscode: Add Run Sparkd (Skip Build) entry to tasks.json 2025-01-28 19:59:08 +00:00
John Colvin afb35d8c25 Compute synth book qty in terms of first leg 2025-01-28 19:59:08 +00:00
John Colvin 3a32ed5fe6 Fix BookSide equality operators 2025-01-27 16:35:22 +00:00
John Colvin 4e01697327 Add equality and output operators to BookLevel 2025-01-27 16:25:32 +00:00
John Colvin 22032c1742 Add fmt::formatter for domain::Side
So we can pass it to LOG macros, etc.
2025-01-27 16:25:15 +00:00
John Colvin 0a9bce317c Add contract qty info to security proto (and FixedSecurity)
We should get this kind of security metadata from the exchange rather
than config.
2025-01-25 20:41:47 +00:00
Mohammad Arham 40e44db597 Fix: Added GCHandle in attempt to fix ui crash 2025-01-22 02:15:55 -05:00
Mohammad Arham 4a447e44e1 Add: Version to first log 2025-01-22 02:14:19 -05:00
John Colvin b1f4654b3c Fix benign typo in publish_spark.py 2025-01-21 17:28:50 -05:00
John Colvin 9d350334c0 fix build (installer): Fix install path typo in publish_spark.py 2025-01-21 17:28:05 -05:00
John Colvin ac597f505f fix build (installer): Set CMAKE_INSTALL_PATH for spark preset 2025-01-21 17:19:15 -05:00
John Colvin 147d9ceb1f ui: Enable net change price display for synths in ladder 2025-01-21 13:11:16 -05:00
John Colvin 377804dfb9 fix (ui): Don't crash on bad regex in grid filters
Wrap Regex construction in try/catch. This is from user input, so of
course we can't assume it will be valid.
2025-01-21 09:26:37 -05:00
John Colvin 0d26401961 fix: Preserve tick_factors in TickDisplaySettings "with" ext
Not sure this was a bug in practice, but it was definitely wrong.
2025-01-21 01:24:23 -05:00
John Colvin 889e226151 feat: Add setting to show notional values in ladder 2025-01-21 01:24:23 -05:00
John Colvin 986d53f4e0 ui: Make ladder size format text editable
This is the best of both worlds: the user can choose from one of the
canned presets in the dropdown, or specify a custom string format.
2025-01-21 01:24:23 -05:00
Taha d13ba2968a DateTimeExpanstionStream col name changes 2025-01-20 15:34:11 -05:00
John Colvin 1a192c8117 feat: Add setting to show ladder prices in basis pts from mid 2025-01-20 14:15:49 -05:00
John Colvin 5e36d4801c cleanup: Remove redundant if/else code 2025-01-20 13:31:41 -05:00
John Colvin 2cf6411ca7 fix: Send exchangeInfo req via our HttpClient instead of ccapi
Bugs (fixed):
  * We get a boost beast exception about the read limit being exceeded
    when requesting securities from Binance prod url (testnet works).
2025-01-19 14:21:11 +00:00
John Colvin e99df129d5 cleanup: Remove erroneous include 2025-01-18 22:54:06 +00:00
John Colvin d07c01bd17 ui: Support ladder ticks-per-row setting for synths
The ticks-per-row control was likely hidden by accident in the first
place. I'm guessing the original intent was to only hide the Price Terms
control for synthetics. At the time, there were no other controls in the
group box, so it made since to hide the whole thing. Some time later, we
added the ticks-per-row setting to the same group box, and forgot to
make the groupbox visible again.
2025-01-17 13:11:09 -05:00
Taha Afzal d69673c524 Previous change was not setting the create time for orders, fixing that + changign one log level to Dag 2025-01-17 07:00:57 +00:00
Taha Afzal 4ee595cdaf Fixing the issue were fractional fills on a working order could result in overfills being reported (it was only a reporting issue) 2025-01-17 06:38:38 +00:00
Mohammad Arham 6fcaf07df0 Fix: binance execution working on sim 2025-01-17 00:59:22 +00:00
Taha Afzal 492d48d886 Added qty_multiplier to spreader.proto 2025-01-16 20:18:26 +00:00
Taha Afzal b6a56e768b Adding config boolan config publish_boostrap_books_only to MarketDataReplayConfig which allows MDReplayer to only publish the bootstrap of all the books 2025-01-16 20:18:26 +00:00
Taha Afzal 3e2b5976a8 Minor changes and ocmments, to allow for fractional qtys 2025-01-16 20:18:26 +00:00
Mohammad Arham edd3f6cbd6 FIX: Orderbook quantity display fixed 2025-01-15 14:47:09 -05:00
John Colvin 2e9347341b fix: Don't throw ex just because leg qty is 0
ContractLegEnumerable shouldn't be responsible for security validation.
2025-01-14 12:58:58 -05:00
John Colvin 2061e38349 fix: Hook up mview venue filter in vm 2025-01-13 00:11:43 -05:00
Mohammad Arham 650dd57b6d FIX: increase max symbo count from 32 to 1024 2025-01-13 04:42:13 +00:00
Mohammad Arham 82747f7dc0 Fix: OKX perp swaps are same as futures now 2025-01-13 04:41:29 +00:00
John Colvin 04b26904a3 ui: Sort exchanges in grid filter dropdown 2025-01-12 15:10:20 -05:00
John Colvin f123e4c72c ui: Add Exchange col to mview 2025-01-12 15:10:20 -05:00
John Colvin b887cbebab fix: Actually capture emumba deribit cfg obj by value
The previous "fix" didn't actually work because, although the lambda
captured the obj by value, the thread captured the lambda by reference!
2025-01-09 16:53:22 +00:00
John Colvin 14e975bc7e vscode: Add Spark (Skip Build) entry to tasks.json 2025-01-09 11:23:30 -05:00
John Colvin 064f557057 ui: Sort accounts in ladder/ticket dropdown 2025-01-09 11:23:30 -05:00
John Colvin 4d106b5e6c ui: Fix alignment issues in order ticket 2025-01-09 11:23:30 -05:00
John Colvin 9fbeaf0d68 ui: Make TickUpDown style match default (dark) Button
* Set BorderThickness to 0
* Use same mouse-hover background as Button
2025-01-09 11:23:30 -05:00
John Colvin 4018ac8b78 ui: Add tooltips to order ticket 2025-01-09 11:23:30 -05:00
John Colvin 81c9503300 ui: Apply dark theme to order ticket buttons 2025-01-09 11:23:30 -05:00
John Colvin 477210926d ui: Fix text color in algo popup 2025-01-09 11:23:29 -05:00
John Colvin 0aa48a79a9 ui: Remove single quotes from tooltip
It looked busy
2025-01-09 01:38:26 -05:00
John Colvin 9e71c7af02 ui: Add tooltips to ladder 2025-01-09 01:38:26 -05:00
John Colvin 4647751409 vscode: Add Spark Test entry to launch.json, tasks.json 2025-01-09 01:03:33 -05:00
John Colvin a3087b874b cleanup: Remove unused code 2025-01-09 01:03:32 -05:00
John Colvin 5b2f5c5c8f Don't throw/catch type exception needlessly 2025-01-09 01:03:32 -05:00
John Colvin 479227090e ui: Remove overly rigid validation for "ticks per row" setting 2025-01-09 01:03:32 -05:00
John Colvin 2dfcf320f5 fix: Refresh ladder levels when "ticks per row" changes 2025-01-09 01:03:32 -05:00
John Colvin b4c7213cb2 fix: Refresh ladder prices in view when they change
Bugs (fixed):
  * Sometimes the prices in the left panel of the ladder get stale
2025-01-09 01:03:32 -05:00
John Colvin c7dfd69b90 Consolidate tick rounding functions
Too many functions were performing essentially the same service:
rounding ticks. It was confusing.
2025-01-09 01:03:32 -05:00
John Colvin 4fdc866485 build: Propagate py script exit codes back to the shell
I noticed the build cmds were broken on Windows because the debugger
would still start even when the preLaunchTask (build) failed!
2025-01-09 01:03:32 -05:00
John Colvin f5d5ca9a1c ui: Use appropriate colors in font picker control
We must have missed this when we switched to dark theme.
2025-01-09 01:03:32 -05:00
John Colvin cfafcf77b9 ui: Use appropriate text color in ladder settings dialog
We must have missed this black text when we switched to dark theme.
While we're at it, freeze the brushes it uses, since they are stored in
a StaticResource which could potentially be shared by multiple controls.
2025-01-09 01:03:32 -05:00
John Colvin 8ebe783139 fix: Clear low/high lines in ladder when scrolled out of view 2025-01-09 01:03:32 -05:00
John Colvin 1851a96a58 vscode: Add Spark (Skip Build) entry to launch.json 2025-01-09 01:03:32 -05:00
John Colvin 0b334ef870 fix: Remove erroneous early exit condition in Ladder render
Bugs (fixed):
  * The ladder doesn't always render ask prices
2025-01-09 01:03:32 -05:00
John Colvin ee811a729a ui: Make ladder price line colors match button text 2025-01-09 01:03:32 -05:00
John Colvin cb15be276d ui: Show bid, ask, mid lines in ladder by default 2025-01-09 01:03:32 -05:00
John Colvin 7d5af6729b ui: Show bid, ask, mid prices in ladder left panel 2025-01-09 01:03:31 -05:00
John Colvin e5679cf082 ui: Make ladder price links prettier
* Change them from hyperlinks to buttons
* Add mouse hover animation
* Align text to the right (so decimal places line up)
2025-01-09 01:03:31 -05:00
John Colvin 98f89b9ad4 fix: Capture emumba deribit cfg by value in thread body
Bugs (fixed):
  * Sparkd occasionally assert crashes on startup because it fails to
    read emumba deribit config.

    Cause: This is a simple race bug. The config obj isn't captured by
    value, so it can be destructed before the thread references it.
2025-01-08 19:03:01 +00:00
John Colvin ba4f8f67e3 fix: Disable binance usds futs symbol filter config
This was a premature optimization, and has become a burden now that we
need to subscribe to large symbol sets for Alphaworx.
2025-01-06 05:00:04 +00:00
John Colvin 9c78380490 fix build (gcc rel): Don't fallthrough switch cases 2025-01-06 03:52:25 +00:00
John Colvin f6166f0674 fix: Show iceberg show qty in algo params grid
Bugs (fixed):
  * In the ladder, you can't view/edit the iceberg show quantity.

  Cause: No view template was defined for nullable double params. Most
  likely, we changed the show qty in the vm from double to nullable
  double at some point, and forgot to reflect this change in the view.
  An easy mistake to make as it would not have broken the build.
2025-01-05 15:58:29 -05:00
John Colvin 6334a902e7 fix: Reclaim keyboard focus when mview popup is clicked 2025-01-05 15:29:11 -05:00
John Colvin 082ca62fff fix: Focus price textbox when mview order ticket popup opens 2025-01-05 15:18:46 -05:00
John Colvin 9dbd8dd7c6 fix: Set focus through FocusScope element if one exists 2025-01-05 15:18:46 -05:00
John Colvin 8f7a4d0df6 Fix typo in AutoFocusTargetProvider dependency prop 2025-01-05 15:18:46 -05:00
John Colvin f372dd56b3 fix: Return synthetic name in get_security_desc
If the security is a synthetic (of course).

Bugs (fixed):
  * Changing limits in the risk ui causes sparkd to assert crash
2025-01-05 20:15:10 +00:00
John Colvin 86eba20d61 build: Make compiler/build type case insensitive 2025-01-05 06:25:11 +00:00
John Colvin 35a985280d fix: Run heartbeat timer in MarketDataGateway
This is why we were getting those heartbeat asserts. Previously, only
ExchangeSimApplication would start this timer. Since MarketDataGateway
doesn't share its MDProducer with ExchangeSimApplication, it must start
one itself.
2025-01-03 16:59:07 +00:00
John Colvin f5e1b3319c cleanup: Don't write to cout in SparkDataDirectory
Don't need that noise.
2025-01-03 16:59:07 +00:00
John Colvin 65c3948e4f cleanup: Remove unused code 2025-01-03 16:59:07 +00:00
John Colvin 002bdff9a3 fix: Don't parse non-existent json files in SparkDataDirectory
Bugs (fixed):
  * Sparkd assert crashes if binance_usds_futures_execution.json file
    doesn't exist
2025-01-03 16:59:07 +00:00
John Colvin bb6e5dd548 diag: Fix sparkd log path in show_last_run.sh 2025-01-03 16:59:07 +00:00
John Colvin 7c1aed3339 fix build (api server): Remove old cpps from source file list 2025-01-03 16:59:07 +00:00
John Colvin 22ae51fb99 build: Clean up build py scripts 2025-01-03 16:59:07 +00:00
John Colvin a53d347f81 fix build (warns): Update cmake min version to 3.10
Versions < 3.10 will soon be deprecated (we just started getting
warnings about it).
2025-01-03 16:59:07 +00:00
John Colvin 838dbcd886 build: In py build scripts, exit with code 1 on failure
Previously, they would exit with 0 no matter what happened.
2025-01-03 16:59:07 +00:00
John Colvin 5ed2309d22 fix: Add switch case for VENUE_BTS in VenueIndex 2025-01-03 16:59:07 +00:00
John Colvin 4d41704dd2 build: Configure admin webapp build entirely in CMakeLists
Get rid of build_admin_web_app.py
2025-01-03 16:59:07 +00:00
John Colvin bd1000e518 build: Get rid of explicit "targets" in CMakePresets for sparkd
So we don't have to worry about keeping it synchronized with the actual
targets in the build (just one more thing that can break). You can build
specific targets via the cmake cli anyways.
2025-01-03 16:59:07 +00:00
John Colvin dd4aa3c2ed build: Update fable in dotnet-tools.json 2025-01-03 16:59:07 +00:00
John Colvin fee9d3f597 build: Update webpack-cli in admin webapp 2025-01-03 16:59:07 +00:00
John Colvin 416f9aef31 vscode: Add Sparkd Test to launch.json 2025-01-03 16:59:07 +00:00
John Colvin 48f5e4c201 vscode: Fix sparkd launch.json 2025-01-03 16:59:07 +00:00
John Colvin 4438b4ca8d feat: Support fractional quantities in synthetic book 2025-01-03 16:59:07 +00:00
John Colvin 73356e0d61 cli: Add cmd to print security info from the db to console
Usage: sparkd md dump --db
2025-01-03 16:59:07 +00:00
John Colvin 4a285fa581 cleanup: Make fn const 2025-01-03 16:59:07 +00:00
John Colvin cfa82c2177 cleanup: Remove erroneous fwd declared class 2025-01-03 16:59:07 +00:00
John Colvin 0410a0431a fix: Encode exp in valid date int when storing in sqlite
The strange bit shifting we were doing before doesn't support large
dates.

Bugs (fixed):
  * Synthetics with Binance perpetual future legs don't appear on the
    front end after sparkd restarts.

  * Cause: Exp dates for Binance perpetual futures aren't properly
    encoded in the database. As a result, when loaded from disk, they
    are mistakenly identified as expired, along with any synthetics that
    have them as legs.
2025-01-03 16:59:07 +00:00
John Colvin 02843f14ff cleanup: Get rid of trivial fn that's only used in one place
Move the logic into the function that used to call it.
2025-01-03 16:59:07 +00:00
John Colvin f1f1d4259c diag: Add logs around security discovery in MDConsumer
This is multi-threaded event driven code. Without descriptive logs in
the right places, diagnosing problems is difficult and tedious.
2025-01-03 16:59:07 +00:00
John Colvin 71fc43af68 cleanup: Flatten nested if blocks with continue statements
Easier to read
2025-01-03 16:59:07 +00:00
John Colvin 350144ffd5 fix: Use security desc to get unique id (along with venue, type)
I'm guessing this typo went unnoticed because it didn't break the build
(another arg of the same was being used instead).
2025-01-03 16:59:07 +00:00
John Colvin da227a5550 Support synths at Binance, Binance USDS Futures, Deribit, and Okx 2025-01-03 16:59:07 +00:00
John Colvin 0c92cf3a87 Add double ops to IntPrice 2025-01-03 16:59:07 +00:00
John Colvin 1188af391e Add trivial fns to TimeStamp for convenience
* += and -= operators
* 'date' fn
* 'to_date_int' fn
2025-01-03 16:59:07 +00:00
John Colvin 0bb85cf915 refactor: Change month and day params from uint32 to uint8
They only need to hold values 1-12 and 1-31, respectively. A single byte
is more than sufficient. Also add ymdhms TimeStamp constructor.
2025-01-03 16:59:07 +00:00
John Colvin d77f5996fe cleanup: Make a couple TimeStamp fns const 2025-01-03 16:59:07 +00:00
John Colvin 940c41a274 Add int32 arithmetic operators to domain::Price 2025-01-03 16:59:07 +00:00
John Colvin e671960dc3 Add double arithmetic operators to domain::Price 2025-01-03 16:59:07 +00:00
John Colvin 8fa34b6b39 fix: Multiply additive factors by exponent in domain::Price
We must not have been using these operators because the implementation
was very broken (yet it doesn't seemed to have caused any bugs).
2025-01-03 16:59:07 +00:00
John Colvin f9d5429b5b cleanup: Remove unused code 2025-01-03 16:53:05 +00:00
Mohammad Arham 2c342482eb Merge branch 'development.crypto' of bitbucket.org:bluetradesys/spark into development.crypto 2025-01-02 15:30:13 -05:00
Mohammad Arham 3aefac26c2 fix: Timer condition was incorrect 2025-01-02 20:15:38 +00:00
John Colvin 1554594cdd diag: Throttle noisy log in Ladder 2025-01-02 12:15:22 -05:00
John Colvin 727a0f38b5 fix: Prevent arithmetic overflow when converting long to int in Ladder 2025-01-02 12:13:58 -05:00
John Colvin 7a0e8cdb26 fix: Properly format spread/synth leg qty in generated desc 2025-01-01 13:12:17 -05:00
John Colvin a4e97b04d7 fix: Don't propagate security criteria changes to other windows
Bugs (fixed):
  * Security criteria changes in mview and time/sales propagate to other
    windows of the same type.
2024-12-31 11:06:24 -05:00
John Colvin 463980a6ef Fix typo in get_binance_md.py 2024-12-29 18:37:54 +00:00
John Colvin 9b25857042 fix: Ensure "earliest_expiration" fn actually returns min exp
Previously, it assumed the first leg would have the earliest exp. While
this may be true for most securities, it isn't guarenteed by our data
model.
2024-12-29 13:26:44 -05:00
John Colvin ef19e7d76b fix: Don't assume all securities have expirations in pnl vm
Bugs (fixed):
  * PNL window crashes if data has Spots (i.e., securities without exps)
2024-12-29 13:17:17 -05:00
Taha Afzal 471faec2cb Added more etsts for fractional qtys 2024-12-28 08:31:16 +00:00
Taha Afzal ce96d42db8 Merging 2024-12-26 07:01:30 +00:00
Mohammad Arham 187894ca84 FIX: makes gateway factory work 2024-12-24 17:56:49 +00:00
John Colvin 63492c4575 fix: Remove erroneous assert in LadderRender 2024-12-21 23:24:52 -05:00
John Colvin e150c611f6 fix: Round fractional order qty to 6 decimal places
It was previously rounding to 2 decimal places, which isn't enough for
many crypto currencies (e.g., BTCUSDT at Binance)
2024-12-21 23:17:52 -05:00
John Colvin bb837d6c26 fix build: Trivial changes in DatabentoDataHandler 2024-12-22 04:00:23 +00:00
John Colvin 965e541ac8 Fix json structures and endpoints for Binance USDS Futures md 2024-12-22 04:00:23 +00:00
John Colvin 1b232aa610 feat: Add Binance USDS Futures execution 2024-12-22 04:00:23 +00:00
John Colvin 25439b094a improvement: Perform sec lookup in const time 2024-12-21 20:08:19 +00:00
John Colvin f927ef2c5e Add conf_json dir to spark data file tree
So we don't have to add proto files to the build for trivial config.
2024-12-21 20:08:19 +00:00
John Colvin d42389b75a Don't needlessly download cme config xml
When we aren't subscribing to cme md (i.e., no glbx symbols in spark
conf).
2024-12-21 20:08:19 +00:00
John Colvin 671cbcf03a Fix bts::Quantity string fmt so it includes fractional qty 2024-12-21 20:01:55 +00:00
John Colvin e589e23f41 Add py script to get md at binance
For quick/easy troubleshooting (much easier than doing stuff in c++)
2024-12-21 20:01:55 +00:00
John Colvin 11fadc0a3e Expose "run" fn in WebsocketClient
So it doesn't have to be hidden in a background thread
2024-12-21 20:01:55 +00:00
John Colvin b8c2d93866 Add ping/pong facilities to WebsocketClient 2024-12-21 20:01:55 +00:00
John Colvin 67a23dd196 Add open/close callbacks to WebsocketClient 2024-12-21 20:01:55 +00:00
John Colvin ff22c4eb5f diag: Change WebsocketClient log category to "Http"
A better fit than "Gen"
2024-12-21 20:01:55 +00:00
John Colvin 5addff4c50 cleanup: Remove redundant fn HttpRequest::perform
This was doing the same thing as HttpClient::send. I likely refactored
this function during development, then forgot to remove it.
2024-12-21 20:01:55 +00:00
John Colvin 142e1bd2da Add HttpMethod "DELETE"
In Binance's REST api, orders are canceled via DELETE requests.
2024-12-21 20:01:55 +00:00
John Colvin d05d91bbe7 diag: Log Http requests 2024-12-21 20:01:55 +00:00
Mohammad Arham 3a90ca9cb5 Add: Johns changes to sec oracle 2024-12-17 14:47:37 +00:00
John Colvin 85a46727be Improve c++ http client facilities 2024-12-11 19:28:36 +00:00
Mohammad Arham 624fd2c976 FIX: BUILD Error 2024-12-09 01:34:29 +00:00
Mohammad Arham 16d599d2d6 Add: OKX execution gateway connected 2024-12-08 20:14:16 -05:00
John Colvin c00e4fcb6b feat: Add Binance Coin Futures market data 2024-12-06 00:39:41 +00:00
John Colvin 03e19afe1d Fix qty display conf for BTC/ETH futs at Binance
Need to show 3 decimal places (qty tick size is 0.001)
2024-12-05 21:05:53 +00:00
John Colvin 058e11eadc fix build: Fix install path for sparkd 2024-12-05 18:31:36 +00:00
John Colvin 6ff52e7107 fix build: Add onixs libs again
Taha accidentally reverted externals
2024-12-04 16:56:46 +00:00
John Colvin ee4c28ed41 Port ws client over from cme-websocket-connector
Need this to implement execution at crypto exchanges (beginning with
Binance and OKX)
2024-12-04 16:56:46 +00:00
Mohammad Arham 50d26f0060 Merge branch 'development' of bitbucket.org:bluetradesys/spark into development 2024-12-03 09:52:09 -05:00
Mohammad Arham 36378e6074 Add:: Recording md to sql 2024-12-03 14:51:32 +00:00
Mohammad Arham 420b46741e ADD: Config for sql writer in mdrecorder 2024-11-24 19:14:45 +00:00
Mohammad Arham e8f65f95bd Wrapped proto write in function in mdrecorder 2024-11-24 19:13:36 +00:00
2573 changed files with 129540 additions and 40296 deletions

No files matched your search

+1 -1
View File
@@ -9,7 +9,7 @@
]
},
"fable": {
"version": "4.0.6",
"version": "4.24.0",
"commands": [
"fable"
]
+1 -3
View File
@@ -65,7 +65,7 @@ RUN \
&& apt-get install -y cmake
# Dotnet
RUN apt-get update && apt-get install -y dotnet7
RUN apt-get update && apt-get install -y dotnet8
# Node
ARG node_major=18
@@ -129,5 +129,3 @@ RUN \
-G sudo \
--password "$(openssl passwd -1 "$docker_pass")" \
"$docker_user"
RUN usermod -aG docker "$docker_user"
+6 -17
View File
@@ -1,15 +1,10 @@
{
"dockerFile": "Dockerfile",
// Creates local dir to mount to HOME in docker
"initializeCommand": "mkdir -p ${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId}",
"hostRequirements": {
"memory": "4gb",
"storage": "6gb"
"memory": "8gb",
"storage": "10gb"
},
"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
@@ -17,26 +12,20 @@
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
"runArgs": [
"--shm-size",
"2gb",
"--network",
"host"
"8gb"
],
"mounts": [
"source=${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId},target=/home/bts,type=bind",
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
],
"customizations": {
"vscode": {
"settings": {
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}/build/clang/debug"
]
},
"extensions": [
"eamodio.gitlens",
"llvm-vs-code-extensions.vscode-clangd",
"ms-python.python",
"ms-vscode.cmake-tools",
"ms-vscode.cpptools"
"ms-vscode.cpptools",
"xaver.clang-format"
]
}
}
+14 -46
View File
@@ -7,10 +7,10 @@
# We use Docker Compose to build images and launch containers for various services
# that make up our software suite:
# - sparkd
# - pubsub
# - md-publisher
# - webapp (DSQA on the web)
# - api-server (websocket based)
# - TODO: pubsub
# - TODO: md-publisher
# - TODO: webapp (DSQA on the web)
# - TODO: api-server (websocket based)
# From inside a devcontainer, you can launch all services in separate containers
# on your machine by running:
#
@@ -31,51 +31,19 @@
### Sparkd ###
# The internal image for sparkd to use. For more info, view:
# src/app_sparkd/Dockerfile
# src/server/sparkd/Dockerfile
SPARKD__IMAGE=sparkd
# The 'spark.conf' for this service to use
SPARKD__CONF_FILE=src/app_sparkd/spark.conf
# To run replay md, put recorded md files in SPARKD__MD_RECORDING_DIR, and use:
# SPARKD__IMAGE=sparkd_md_replay
# SPARKD__CONF_FILE=src/app_sparkd/spark.local.conf
# (configure which md to replay in the new SPARKD__CONF_FILE)
SPARKD__MD_RECORDING_DIR=.md_recording
# The config dir for this service to use
SPARKD__CONFIG_DIR=src/app_sparkd/configurations/dev
# Mount a specific volume or dir to "~/sparkdata/data" inside the docker.
# Defaults to a volume named "sparkd_<config_port>" (e.g., sparkd_8000).
# SPARKD__DATA_DIR=/home/bts/sparkdata/data
SPARKD__SOURCE_DIR=install/sparkd-release-gcc
SPARKD__RUNTIME_CONF_DIR=/home/ubuntu/8000/sparkdata/conf/
SPARKD__CONFIG_PORT=8000
SPARKD__QUICKBUS_PORT=8001
SPARKD__SIM_FIX_PORT=8002
SPARKD__SHM_SIZE=4gb
SPARKD__PYTHON_REQUIREMENTS=requirements.txt
### Pubsub ###
# The 'spark.conf' for this service to use
PUBSUB__CONF_FILE=src/DataStreamSuite/PubSubServer/spark.conf
PUBSUB__CS_SOURCE_DIR=build/Release/bin/net7.0/linux-x64/publish
PUBSUB__CPP_SOURCE_DIR=install/data-stream-suite-linux
# The appsettings to load at runtime. Expects a file named:
# src/DataStreamSuite/PubSub/appsettings.${PUBSUB__DOTNET_ENVIRONMENT}.json
PUBSUB__DOTNET_ENVIRONMENT=Staging
### Md Publisher ###
# The 'spark.conf' for this service to use
MD_PUBLISHER__CONF_FILE=src/DataStreamSuite/DataStreamPublisher/spark.conf
MD_PUBLISHER__SOURCE_DIR=build/Release/bin/net7.0/linux-x64/publish
# Determines which appsettings to load at runtime. Expects a file named:
# src/DataStreamSuite/DataStreamPublisher/appsettings.${MD_PUBLISHER__DOTNET_ENVIRONMENT}.json
MD_PUBLISHER__DOTNET_ENVIRONMENT=MarketData
### Webapp (DSQA on the web) ###
# The 'spark.conf' for this service to use
WEBAPP__CONF_FILE=src/client/spark_api_server/spark.conf
WEBAPP__SOURCE_DIR=build/Release/bin/net7.0/linux-x64/publish
WEBAPP__WEB_ROOT=build/Bts.DataStreamWebApp.Client/production/dist
# Determines which appsettings to load at runtime. Expects a file named:
# src/DataStreamSuite/Bts.DataStreamWebApp.Server/appsettings.${WEBAPP__ASPNETCORE_ENVIRONMENT}.json
WEBAPP__ASPNETCORE_ENVIRONMENT=Staging
### Api Server ###
# The 'spark.conf' for this service to use
API_SERVER__CONF_FILE=src/client/spark_api_server/spark.conf
API_SERVER__CS_SOURCE_DIR=build/Release/bin/net7.0/linux-x64/publish
API_SERVER__CPP_SOURCE_DIR=install/spark-api-server-linux
# Determines which appsettings to load at runtime. Expects a file named:
# src/client/spark_api_server/appsettings.${API_SERVER__ASPNETCORE_ENVIRONMENT}.json
API_SERVER__ASPNETCORE_ENVIRONMENT=Staging
# TODO: add config for other services (pubsub, md-publisher, webapp, api-server, etc.)
-1
View File
@@ -1,4 +1,3 @@
*.cmd text=auto eol=crlf
*.cs text=auto eol=crlf
*.sh text=auto eol=lf
*.py text=auto eol=lf
+4 -2
View File
@@ -11,6 +11,7 @@ cmake-build-debug/
**.vscode/*
!.vscode/launch.json
!.vscode/tasks.json
!.vscode/*.env
.vs
obj/
*.csproj.user
@@ -54,11 +55,12 @@ trace_data/
.cache/
/src/Spark.Admin.WebApp/tsconfig.*.g.json
/src/Spark.Admin.WebApp/src/bin/
/src/client/**/*appsettings.Local.json
/src/DataStreamSuite/**/*appsettings.Local.json
**/CMakeDeps.cmake
**/CMakeDeps_win.cmake
.devcontainer/
*.Generated.cs
*.lst
src/app_sparkd/spark.local.conf
.env.local
.md_recording
.secrets
+157 -73
View File
@@ -1,6 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File with Arguments",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": "${command:pickArgs}"
},
{
"name": ".NET Core Attach",
"type": "coreclr",
@@ -16,19 +24,10 @@
"program": "spark_api_server.exe",
"args": [
"run",
"--sparkd",
"http://127.0.0.1:8000/config"
],
"cwd": "build/Debug/bin/net7.0",
},
"linux": {
"preLaunchTask": "Build Api Server (Debug)",
"program": "spark_api_server",
"args": [
"run",
],
"cwd": "build/Debug/bin/net7.0",
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"cwd": "build/Debug/bin/net8.0",
},
"stopAtEntry": false,
"console": "integratedTerminal"
@@ -42,18 +41,10 @@
"program": "spark_api_server.exe",
"args": [
"run",
"--sparkd",
"http://127.0.0.1:8000/config"
],
"cwd": "build/Debug/bin/net7.0",
},
"linux": {
"program": "spark_api_server",
"args": [
"run",
],
"cwd": "build/Debug/bin/net7.0",
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"cwd": "build/Debug/bin/net8.0",
},
"stopAtEntry": false,
"console": "integratedTerminal"
@@ -62,9 +53,9 @@
"name": "Bts.DataStreamWebApp.Server",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "Build DataStream (Debug)",
"program": "${workspaceFolder}/build/Debug/bin/net7.0/Bts.DataStreamWebApp.Server.dll",
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0",
"preLaunchTask": "Build DataStream WebApp (Debug)",
"program": "${workspaceFolder}/build/Debug/bin/net8.0/Bts.DataStreamWebApp.Server.dll",
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0",
"env": {
"ASPNETCORE_HTTP_PORT": "8080",
"ASPNETCORE_HTTPS_PORT": "8443",
@@ -76,8 +67,8 @@
"name": "Bts.DataStreamWebApp.Server (Skip Build)",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/build/Debug/bin/net7.0/Bts.DataStreamWebApp.Server.dll",
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0",
"program": "${workspaceFolder}/build/Debug/bin/net8.0/Bts.DataStreamWebApp.Server.dll",
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0",
"env": {
"ASPNETCORE_HTTP_PORT": "8080",
"ASPNETCORE_HTTPS_PORT": "8443",
@@ -97,10 +88,10 @@
"windows": {
"program": "DataStreamPublisher.exe",
},
"cwd": "build/Debug/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:dataStreamPublisherEnvironment}",
},
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0",
"stopAtEntry": false,
"console": "integratedTerminal"
},
@@ -115,10 +106,10 @@
"windows": {
"program": "DataStreamPublisher.exe",
},
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:dataStreamPublisherEnvironment}",
},
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0",
"stopAtEntry": false,
"console": "integratedTerminal"
},
@@ -127,7 +118,39 @@
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
"args": ["run"],
"args": [
"run"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
},
{
"name": "spreader tests2",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
"args": [
"test",
"[spreader2]"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
},
{
"name": "spreader tests",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
"args": [
"test",
"[spreader]"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
"environment": [],
@@ -142,12 +165,25 @@
"program": "",
"windows": {
"program": "DataStreamQueryAnalyzer.exe",
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/DataStreamQueryAnalyzer/Configurations/DataStreamQueryAnalyzer.json"
],
"cwd": "build/Debug/bin/net7.0-windows",
"cwd": "build/Debug/bin/net8.0-windows",
"env": {
"DOTNET_ENVIRONMENT": "${input:dataStreamQueryAnalyzerEnvironment}",
"PYTHON_EXE": "${workspaceFolder}/.venv/Scripts/python.exe"
},
},
"stopAtEntry": false,
"console": "integratedTerminal",
},
{
"name": "DataStreamQueryAnalyzer (Skip Build)",
"type": "coreclr",
"request": "launch",
"program": "",
"windows": {
"program": "DataStreamQueryAnalyzer.exe",
"cwd": "build/Debug/bin/net8.0-windows",
"env": {
"DOTNET_ENVIRONMENT": "${input:dataStreamQueryAnalyzerEnvironment}",
"PYTHON_EXE": "${workspaceFolder}/.venv/Scripts/python.exe"
},
},
@@ -166,7 +202,7 @@
"windows": {
"program": "PubSubServer.exe",
},
"cwd": "build/Debug/bin/net7.0",
"cwd": "build/Debug/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:pubSubServerEnvironment}",
},
@@ -184,7 +220,7 @@
"windows": {
"program": "PubSubServer.exe",
},
"cwd": "build/Debug/bin/net7.0",
"cwd": "build/Debug/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:pubSubServerEnvironment}",
},
@@ -199,11 +235,25 @@
"windows": {
"preLaunchTask": "Build Spark (Debug)",
"program": "Spark.exe",
"args": [
"--sparkd",
"http://127.0.0.1:8000/config"
],
"cwd": "build/Debug/bin/net7.0-windows",
"cwd": "build/Debug/bin/net8.0-windows",
"env": {
"DOTNET_ENVIRONMENT": "${input:sparkEnvironment}",
}
},
"stopAtEntry": false,
"console": "integratedTerminal"
},
{
"name": "Spark (Skip Build)",
"type": "coreclr",
"request": "launch",
"program": "",
"windows": {
"program": "Spark.exe",
"cwd": "build/Debug/bin/net8.0-windows",
"env": {
"DOTNET_ENVIRONMENT": "${input:sparkEnvironment}",
}
},
"stopAtEntry": false,
"console": "integratedTerminal"
@@ -220,7 +270,40 @@
"--sparkd",
"${input:sparkConfigServer}"
],
"cwd": "build/Debug/bin/net7.0-windows",
"cwd": "build/Debug/bin/net8.0-windows",
},
"stopAtEntry": false,
"console": "integratedTerminal"
},
{
"name": "Spark Test",
"type": "coreclr",
"request": "launch",
"program": "",
"windows": {
"preLaunchTask": "Build Spark (Debug)",
"program": "Spark.Console.exe",
"cwd": "build/Debug/bin/net8.0",
"args": [
"test",
"${input:sparkTest}"
]
},
"stopAtEntry": false,
"console": "integratedTerminal"
},
{
"name": "Spark Test (Skip Build)",
"type": "coreclr",
"request": "launch",
"program": "",
"windows": {
"program": "Spark.Console.exe",
"cwd": "build/Debug/bin/net8.0",
"args": [
"test",
"${input:sparkTest}"
]
},
"stopAtEntry": false,
"console": "integratedTerminal"
@@ -229,7 +312,6 @@
"name": "Sparkd",
"type": "cppdbg",
"request": "launch",
"preLaunchTask": "Build Sparkd (Debug | Clang)",
"program": "",
"linux": {
"program": "${workspaceFolder}/build/sparkd-debug-clang/src/app_sparkd/sparkd",
@@ -282,34 +364,6 @@
}
]
},
{
"name": "Sparkd Web (Skip Build)",
"type": "cppdbg",
"request": "launch",
"program": "",
"linux": {
"program": "${workspaceFolder}/build/sparkd-debug-clang/src/app_sparkd/sparkd",
},
"args": [
"web"
],
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/src/app_sparkd",
"stopAtEntry": false,
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
],
"inputs": [
{
@@ -349,12 +403,42 @@
"Staging",
]
},
{
"id": "dataStreamQueryAnalyzerEnvironment",
"type": "pickString",
"description": "DataStreamQueryAnalyzer Configuration",
"options": [
"Development",
"Local",
],
"default": "Development",
},
{
"id": "sparkEnvironment",
"type": "pickString",
"description": "Spark Configuration",
"options": [
"Development",
"Local",
],
"default": "Development",
},
{
"id": "sparkConfigServer",
"type": "promptString",
"description": "Config Server URL",
"default": "http://localhost:8000/config",
},
{
"id": "sparkTest",
"type": "promptString",
"description": "Test to run",
},
{
"id": "sparkdTest",
"type": "promptString",
"description": "Test to run",
},
{
"id": "isSparkApiEnabled",
"type": "pickString",
+1
View File
@@ -0,0 +1 @@
PYTHONPATH=/workspaces/spark/build/sparkd-debug-clang/src/lib_python_interop;/workspaces/spark/src/lib_python_interop/python_interop_plugins
+169 -61
View File
@@ -485,12 +485,12 @@
"command": "",
"windows": {
"command": "DataStreamQueryAnalyzer.exe",
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/DataStreamQueryAnalyzer/Configurations/DataStreamQueryAnalyzer.json",
],
"options": {
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net7.0-windows"
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net8.0-windows",
"env": {
"DOTNET_ENVIRONMENT": "${input:dataStreamQueryAnalyzerEnvironment}",
"PYTHON_EXE": "${workspaceFolder}/.venv/Scripts/python.exe"
},
},
},
"icon": {
@@ -510,7 +510,7 @@
"${workspaceFolder}/src/DataStreamSuite/DataStreamQueryAnalyzer/Configurations/DataStreamQueryAnalyzer.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0-windows"
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0-windows"
},
},
"icon": {
@@ -530,7 +530,7 @@
"${workspaceFolder}/src/DataStreamSuite/DataStreamQueryAnalyzer/Configurations/DataStreamQueryAnalyzer.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Release/bin/net7.0-windows"
"cwd": "${workspaceFolder}/build/Release/bin/net8.0-windows"
},
},
"icon": {
@@ -549,12 +549,11 @@
"windows": {
"command": "DataStreamPublisher.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/DataStreamPublisher/Configurations/${input:dataStreamPublisherConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net7.0"
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:dataStreamPublisherEnvironment}",
},
},
"icon": {
"id": "run",
@@ -572,12 +571,11 @@
"windows": {
"command": "DataStreamPublisher.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/DataStreamPublisher/Configurations/${input:dataStreamPublisherConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0"
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:dataStreamPublisherEnvironment}",
},
},
"icon": {
"id": "run",
@@ -595,12 +593,11 @@
"windows": {
"command": "DataStreamPublisher.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/DataStreamPublisher/Configurations/${input:dataStreamPublisherConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Release/bin/net7.0"
"cwd": "${workspaceFolder}/build/Release/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:dataStreamPublisherEnvironment}",
},
},
"icon": {
"id": "run",
@@ -701,12 +698,11 @@
"windows": {
"command": "PubSubServer.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net7.0"
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:pubSubServerEnvironment}",
},
},
"icon": {
"id": "run",
@@ -724,12 +720,11 @@
"windows": {
"command": "PubSubServer.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0"
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:pubSubServerEnvironment}",
},
},
"icon": {
"id": "run",
@@ -746,12 +741,11 @@
"windows": {
"command": "PubSubServer.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0"
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:pubSubServerEnvironment}",
},
},
"icon": {
"id": "run",
@@ -769,12 +763,11 @@
"windows": {
"command": "PubSubServer.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Release/bin/net7.0"
"cwd": "${workspaceFolder}/build/Release/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:pubSubServerEnvironment}",
},
},
"icon": {
"id": "run",
@@ -791,12 +784,11 @@
"windows": {
"command": "PubSubServer.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Release/bin/net7.0"
"cwd": "${workspaceFolder}/build/Release/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:pubSubServerEnvironment}",
},
},
"icon": {
"id": "run",
@@ -811,7 +803,7 @@
"windows": {
"command": "Spark.exe",
"options": {
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net7.0-windows"
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net8.0-windows"
},
},
"icon": {
@@ -827,7 +819,7 @@
"windows": {
"command": "Spark.exe",
"options": {
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0-windows"
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0-windows"
},
},
"icon": {
@@ -843,7 +835,22 @@
"windows": {
"command": "Spark.exe",
"options": {
"cwd": "${workspaceFolder}/build/Release/bin/net7.0-windows"
"cwd": "${workspaceFolder}/build/Release/bin/net8.0-windows"
},
},
"icon": {
"id": "run",
"color": "terminal.ansiGreen",
}
},
{
"label": "Run Spark (Skip Build)",
"type": "process",
"command": "",
"windows": {
"command": "Spark.exe",
"options": {
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net8.0-windows"
},
},
"icon": {
@@ -868,7 +875,7 @@
"${input:sparkConfigQuery}",
],
"options": {
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net7.0"
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net8.0"
},
"icon": {
"id": "run",
@@ -894,6 +901,24 @@
"color": "terminal.ansiGreen",
},
},
{
"label": "Run Sparkd (Skip Build)",
"type": "process",
"command": "",
"linux": {
"command": "sparkd",
},
"args": [
"run"
],
"options": {
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
},
"icon": {
"id": "run",
"color": "terminal.ansiGreen",
},
},
{
"label": "Run Sparkd Config Query",
"dependsOn": "Build Sparkd",
@@ -915,6 +940,65 @@
"color": "terminal.ansiGreen",
},
},
{
"label": "Test Spark (Debug)",
"dependsOn": "Build Spark (Debug)",
"type": "process",
"icon": {
"id": "coverage",
"color": "terminal.ansiYellow",
},
"command": "",
"windows": {
"command": "Spark.Console.exe",
"args": [
"test",
"${input:sparkTest}"
],
"options": {
"cwd": "${workspaceFolder}/build/Debug/bin/net8.0",
}
},
},
{
"label": "Test Sparkd",
"dependsOn": "Build Sparkd",
"type": "process",
"command": "",
"linux": {
"command": "sparkd",
},
"args": [
"test",
"${input:sparkdTest}",
],
"options": {
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
},
"icon": {
"id": "coverage",
"color": "terminal.ansiYellow",
},
},
{
"label": "Test Sparkd (Skip Build)",
"type": "process",
"command": "",
"linux": {
"command": "sparkd",
},
"args": [
"test",
"${input:sparkdTest}",
],
"options": {
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
},
"icon": {
"id": "coverage",
"color": "terminal.ansiYellow",
},
},
{
"label": "View DataStreamQueryAnalyzer Logs (Last Run)",
"type": "process",
@@ -1003,26 +1087,40 @@
"default": "sparkd-debug-clang",
},
{
"id": "dataStreamPublisherConfig",
"id": "dataStreamPublisherEnvironment",
"type": "pickString",
"description": "Publisher Configuration",
"options": [
"MarketDataPublisher",
"SimpleEchoClient",
"SimpleTestDataPublisher",
"TestDataPublisher",
"Development",
"MarketData",
"Production",
"ProductionAws",
"Staging",
],
"default": "MarketDataPublisher",
},
{
"id": "pubSubServerConfig",
"id": "dataStreamQueryAnalyzerEnvironment",
"type": "pickString",
"description": "DataStreamQueryAnalyzer Configuration",
"options": [
"Development",
"Local",
],
"default": "Development",
},
{
"id": "pubSubServerEnvironment",
"type": "pickString",
"description": "PubSub Configuration",
"options": [
"PubSubServer",
"SimpleEchoClient",
"Development",
"EchoServer",
"Production",
"ProductionAws",
"Staging",
],
"default": "PubSubServer",
"default": "Development",
},
{
"id": "sparkConfigQuery",
@@ -1033,6 +1131,16 @@
"id": "sparkdConfigQuery",
"type": "promptString",
"description": "Config Type(s)"
}
},
{
"id": "sparkTest",
"type": "promptString",
"description": "Test to run",
},
{
"id": "sparkdTest",
"type": "promptString",
"description": "Test to run",
},
]
}
+82 -66
View File
@@ -1,7 +1,10 @@
cmake_minimum_required(VERSION 3.12)
project(spark)
include(GNUInstallDirs)
include(${CMAKE_SOURCE_DIR}/cmake/GitVersion.cmake)
set(CMAKE_CXX_STANDARD 20)
get_version_from_git()
if(WIN32)
set(platform "windows")
else()
@@ -33,85 +36,98 @@ else()
set(BUILD_ADMIN_WEB_APP ON CACHE BOOL "Build the Admin WebApp")
endif()
include_directories(BEFORE SYSTEM ${EXTERNALS_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
link_directories(${EXTERNALS_LIB})
add_compile_definitions(SHARED_MEMORY_PREFIX="${SHARED_MEMORY_PREFIX}")
if(DEFINED APP_DATA_DIR)
add_compile_definitions(APP_DATA_DIR="${APP_DATA_DIR}")
endif()
add_compile_definitions(EXPORT_MD_CONSUMER="${EXPORT_MD_CONSUMER}")
if(WIN32)
# subdirs
add_subdirectory(src/lib_spark)
else()
if(BUILD_SPARKD)
# Note: All macros that begin w/ 'CCAPI' are ccapi's macros (defined and/or
# read by their code). We override some below.
add_compile_definitions(SPARK_ENABLE_CCAPI)
if(BUILD_SPARKD)
# Note: All macros that begin w/ 'CCAPI' are ccapi's macros (defined and/or
# read by their code). We override some below.
add_compile_definitions(SPARK_ENABLE_CCAPI)
# This allows us to override hardcoded exchange-urls with ones read from
# config (i.e., resolved at runtime rather than compile-time)
add_compile_definitions(CCAPI_EXPOSE_INTERNAL)
# This allows us to override hardcoded exchange-urls with ones read from
# config (i.e., resolved at runtime rather than compile-time)
add_compile_definitions(CCAPI_EXPOSE_INTERNAL)
# Enable ccapi services
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
add_compile_definitions(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT)
# Enable ccapi exchanges
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_COIN_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_COINBASE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_DERIBIT)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_OKX)
# Enable ccapi services
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
add_compile_definitions(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT)
# Enable ccapi exchanges
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_COIN_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_COINBASE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_DERIBIT)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_OKX)
set(CCAPI_LOG_LEVEL
"Warn"
CACHE STRING "<Fatal|Error|Warn|Info|Debug|Trace> Log level for ccapi")
string(TOUPPER "${CCAPI_LOG_LEVEL}" _ccapi_log_level_upper)
add_compile_definitions("CCAPI_ENABLE_LOG_${_ccapi_log_level_upper}")
set(CCAPI_LOG_LEVEL
"Warn"
CACHE STRING "<Fatal|Error|Warn|Info|Debug|Trace> Log level for ccapi")
string(TOUPPER "${CCAPI_LOG_LEVEL}" _ccapi_log_level_upper)
add_compile_definitions("CCAPI_ENABLE_LOG_${_ccapi_log_level_upper}")
# ** Binance **
add_compile_definitions(CCAPI_BINANCE_URL_REST_BASE="https://api.binance.com")
add_compile_definitions(BINANCE_INSTRUMENTS_PATH="/api/v3/exchangeInfo")
add_compile_definitions(BINANCE_PRICE_STATS_PATH="/api/v3/ticker/24hr")
# ** Binance **
add_compile_definitions(CCAPI_BINANCE_URL_REST_BASE="https://api.binance.com")
add_compile_definitions(BINANCE_INSTRUMENTS_PATH="/api/v3/exchangeInfo")
add_compile_definitions(BINANCE_PRICE_STATS_PATH="/api/v3/ticker/24hr")
# ** Binance US **
add_compile_definitions(BINANCE_US_INSTRUMENTS_PATH="/api/v3/exchangeInfo")
add_compile_definitions(BINANCE_US_PRICE_STATS_PATH="/api/v3/ticker/24hr")
# ** Binance US **
add_compile_definitions(BINANCE_US_INSTRUMENTS_PATH="/api/v3/exchangeInfo")
add_compile_definitions(BINANCE_US_PRICE_STATS_PATH="/api/v3/ticker/24hr")
# ** Binance USDS Futures **
add_compile_definitions(CCAPI_BINANCE_USDS_FUTURES_URL_REST_BASE="https://fapi.binance.com")
add_compile_definitions(BINANCE_USDS_FUTURES_INSTRUMENTS_PATH="/fapi/v1/exchangeInfo")
add_compile_definitions(BINANCE_USDS_FUTURES_PRICE_STATS_PATH="/fapi/v1/ticker/24hr")
add_compile_definitions(BINANCE_USDS_FUTURES_USER_DATA_PATH="/api/v3/userDataStream")
# ** Binance USDS Futures **
add_compile_definitions(CCAPI_BINANCE_USDS_FUTURES_URL_REST_BASE="https://dapi.binance.com")
add_compile_definitions(BINANCE_USDS_FUTURES_INSTRUMENTS_PATH="/dapi/v1/exchangeInfo")
add_compile_definitions(BINANCE_USDS_FUTURES_PRICE_STATS_PATH="/dapi/v1/ticker/24hr")
# ** Binance Coin Futures **
add_compile_definitions(CCAPI_BINANCE_COIN_FUTURES_URL_REST_BASE="https://dapi.binance.com")
add_compile_definitions(BINANCE_COIN_FUTURES_INSTRUMENTS_PATH="/dapi/v1/exchangeInfo")
add_compile_definitions(BINANCE_COIN_FUTURES_PRICE_STATS_PATH="/dapi/v1/ticker/24hr")
# ** Deribit **
add_compile_definitions(DERIBIT_INSTRUMENT_PATH="/api/v2/public/get_instrument")
add_compile_definitions(DERIBIT_INSTRUMENTS_PATH="/api/v2/public/get_instruments")
add_compile_definitions(DERIBIT_COMBOS_PATH="/api/v2/public/get_combos")
add_compile_definitions(DERIBIT_TICKER_PATH="/api/v2/public/ticker")
add_compile_definitions(DERIBIT_CURRENCIES_METHOD="public/get_currencies")
add_compile_definitions(DERIBIT_INSTRUMENT_METHOD="public/get_instrument")
add_compile_definitions(DERIBIT_INSTRUMENTS_METHOD="public/get_instruments")
add_compile_definitions(DERIBIT_COMBO_DETAILS_METHOD="public/get_combo_details")
add_compile_definitions(DERIBIT_COMBOS_METHOD="public/get_combos")
# ** Deribit **
add_compile_definitions(DERIBIT_INSTRUMENT_PATH="/api/v2/public/get_instrument")
add_compile_definitions(DERIBIT_INSTRUMENTS_PATH="/api/v2/public/get_instruments")
add_compile_definitions(DERIBIT_COMBOS_PATH="/api/v2/public/get_combos")
add_compile_definitions(DERIBIT_TICKER_PATH="/api/v2/public/ticker")
add_compile_definitions(DERIBIT_CURRENCIES_METHOD="public/get_currencies")
add_compile_definitions(DERIBIT_INSTRUMENT_METHOD="public/get_instrument")
add_compile_definitions(DERIBIT_INSTRUMENTS_METHOD="public/get_instruments")
add_compile_definitions(DERIBIT_COMBO_DETAILS_METHOD="public/get_combo_details")
add_compile_definitions(DERIBIT_COMBOS_METHOD="public/get_combos")
# ** Okx **
add_compile_definitions(OKX_INSTRUMENTS_PATH="/api/v5/public/instruments")
add_compile_definitions(OKX_UNDERLYING_PATH="/api/v5/public/underlying")
endif()
# ** Okx **
add_compile_definitions(OKX_INSTRUMENTS_PATH="/api/v5/public/instruments")
add_compile_definitions(OKX_UNDERLYING_PATH="/api/v5/public/underlying")
endif()
# subdirs
add_subdirectory(src/lib_system)
add_subdirectory(src/lib_domain)
add_subdirectory(src/lib_http)
if (BUILD_SPARKD)
add_subdirectory(src/lib_python_md)
add_subdirectory(src/app_sparkd)
add_subdirectory(src/data_files)
endif()
add_subdirectory(src/lib_spark)
if (BUILD_ADMIN_WEB_APP)
add_subdirectory(src/Spark.Admin.WebApp)
endif()
if(BUILD_DOCS)
add_subdirectory(doc/user)
endif()
if(BUILD_SDK)
add_subdirectory(src/sdk)
# subdirs
add_subdirectory(src/lib_spark)
if (BUILD_SPARKD)
add_subdirectory(src/lib_python_interop)
add_subdirectory(src/lib_onixs_fix)
add_subdirectory(src/lib_binance)
add_subdirectory(src/lib_okx_execution)
add_subdirectory(src/app_sparkd)
add_subdirectory(src/data_files)
endif()
if (BUILD_ADMIN_WEB_APP)
add_subdirectory(src/Spark.Admin.WebApp)
endif()
if(BUILD_DOCS)
add_subdirectory(doc/user)
endif()
if(BUILD_SDK)
add_subdirectory(src/sdk)
endif()
endif()
+15 -12
View File
@@ -24,12 +24,13 @@
"CMAKE_CXX_FLAGS_DEBUG": "$env{SPARK_CMAKE_CXX_FLAGS} /MTd",
"CMAKE_CXX_FLAGS_RELEASE": "$env{SPARK_CMAKE_CXX_FLAGS} /MT",
"CMAKE_CXX_STANDARD": "20",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
"CMAKE_SHARED_LINKER_FLAGS_DEBUG": "/DEBUG",
"CMAKE_SHARED_LINKER_FLAGS_RELEASE": "/OPT:REF /OPT:ICF /LTCG",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"EXTERNALS_ROOT": "${sourceDir}/externals/windows",
"CONFIG_DEFAULTS_PATH": "spark/config/profiles/default/*.conf",
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0;${sourceDir}/build/$<CONFIG>/bin/net7.0-windows",
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net8.0;${sourceDir}/build/$<CONFIG>/bin/net8.0-windows",
"EXPORT_MD_CONSUMER": {
"type": "BOOL",
"value": "ON"
@@ -49,11 +50,20 @@
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"platform": "",
"CMAKE_BUILD_WITH_INSTALL_RPATH": {
"type": "BOOL",
"value": "OFF"
},
"CMAKE_CXX_STANDARD": "20",
"CMAKE_INSTALL_RPATH": ".",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
"CMAKE_PREFIX_PATH": "${sourceDir}/cmake/;${sourceDir}/.venv/lib/python3.10/site-packages/pybind11/share/cmake/pybind11",
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"DATABENTO_USE_EXTERNAL_JSON": {
"type": "BOOL",
"value": "ON"
},
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
"BUILD_SDK": {
"type": "BOOL",
@@ -164,12 +174,13 @@
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -DDEBUG",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -fno-omit-frame-pointer -DNDEBUG -O3",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
"CMAKE_EXPORT_COMPILE_COMMANDS": {
"type": "BOOL",
"value": "ON"
},
"CONFIG_DEFAULTS_PATH": "spark/config/profiles/default/*.conf",
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0",
"DOTNET_BINARY_DIR": "${sourceDir}/build/Spark.Api.Server/$<CONFIG>/bin/net8.0",
"EXPORT_MD_CONSUMER": {
"type": "BOOL",
"value": "ON"
@@ -208,7 +219,7 @@
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CONFIG_DEFAULTS_PATH": "spark/config/profiles/default/*.conf;spark/config/profiles/data_stream/*.conf",
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0;${sourceDir}/build/$<CONFIG>/bin/net7.0-windows"
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net8.0;${sourceDir}/build/$<CONFIG>/bin/net8.0-windows"
}
},
@@ -241,7 +252,7 @@
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -DDEBUG",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -fno-omit-frame-pointer -DNDEBUG -O3",
"CONFIG_DEFAULTS_PATH": "spark/config/profiles/default/*.conf;spark/config/profiles/data_stream/*.conf",
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0;${sourceDir}/build/$<CONFIG>/bin/net8.0",
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0",
"EXPORT_MD_CONSUMER": {
"type": "BOOL",
"value": "ON"
@@ -294,14 +305,6 @@
"inherits": "sparkd"
},
{
"name": "sparkd-debug-clang-j6",
"configurePreset": "sparkd-debug-clang",
"displayName": "Sparkd (Debug | Clang | jobs=6)",
"inherits": "sparkd",
"jobs": 6
},
{
"name": "sparkd-debug-gcc",
"configurePreset": "sparkd-debug-gcc",
-10
View File
@@ -42,8 +42,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.ColumnExpressions", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStream", "src\DataStreamSuite\DataStream\DataStream.csproj", "{39AEA075-6457-46C0-8821-1158BE6EFA57}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.Net", "src\DataStreamSuite\Bts.Net\Bts.Net.csproj", "{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -196,14 +194,6 @@ Global
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.Release|x64.Build.0 = Release|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}.Debug|x64.ActiveCfg = Debug|Any CPU
{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}.Debug|x64.Build.0 = Debug|Any CPU
{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}.Release|x64.ActiveCfg = Release|Any CPU
{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}.Release|x64.Build.0 = Release|Any CPU
{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{CCDCB00A-CABF-4D82-8A5F-33EEC8ACE51D}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+48
View File
@@ -0,0 +1,48 @@
include_guard(GLOBAL)
function(get_version_from_git)
find_package(Git QUIET)
if(NOT Git_FOUND)
message(WARNING "Git not found")
return()
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --tags --always
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE GIT_RESULT
)
if(NOT GIT_RESULT EQUAL 0)
message(WARNING "Failed to get git tag")
return()
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_SHORT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(PROJECT_VERSION_BUILD_METADATA ${GIT_COMMIT_SHORT_HASH})
set(PROJECT_VERSION_BUILD_METADATA ${GIT_COMMIT_SHORT_HASH} PARENT_SCOPE)
string(REGEX REPLACE "^v" "" CLEAN_TAG "${GIT_TAG}")
if(CLEAN_TAG MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-.*)?$")
set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1})
set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1} PARENT_SCOPE)
set(PROJECT_VERSION_MINOR ${CMAKE_MATCH_2})
set(PROJECT_VERSION_MINOR ${CMAKE_MATCH_2} PARENT_SCOPE)
set(PROJECT_VERSION_PATCH ${CMAKE_MATCH_3})
set(PROJECT_VERSION_PATCH ${CMAKE_MATCH_3} PARENT_SCOPE)
set(PROJECT_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
set(PROJECT_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE)
set(PROJECT_VERSION_FULL "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}+${GIT_COMMIT_SHORT_HASH}")
set(PROJECT_VERSION_FULL "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}+${GIT_COMMIT_SHORT_HASH}" PARENT_SCOPE)
else()
message(WARNING "Tag '${CLEAN_TAG}' does not match semver format")
endif()
endfunction()
+71
View File
@@ -0,0 +1,71 @@
include_guard(DIRECTORY)
include(GNUInstallDirs)
include(${CMAKE_SOURCE_DIR}/cmake/ExportToParent.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/SetFirstDefined.cmake)
function(init_python_module)
set(_options)
set(_one_value_args
NAME
)
set(_multi_value_args
SOURCES
)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
set_first_defined(_name _ARGS_NAME PROJECT_NAME)
set_first_defined(_sources _ARGS_SOURCES ${_name}_SOURCES)
set(module_proj ${_name}_module)
set(${module_proj}_SOURCES ${_sources})
pybind11_add_module(${module_proj} SHARED ${_sources})
target_link_libraries(
${module_proj}
PUBLIC
pybind11::embed
spark
${_name}
)
target_include_directories(
${module_proj}
PRIVATE
$<BUILD_INTERFACE:${pybind11_INCLUDE_DIRS}>
)
target_include_directories(
${module_proj}
PUBLIC
${Python3_INCLUDE_DIRS}
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/stubs/${_name}.pyi
DEPENDS ${_name} ${module_proj}
COMMAND PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR} pybind11-stubgen --numpy-array-use-type-var --ignore-invalid-expressions '.*' ${_name}_module
COMMENT "Generating py stubs"
)
add_custom_target(
"${_name}_py_stubs"
ALL
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/stubs/${_name}.pyi
)
install(
TARGETS ${module_proj}
EXPORT ${module_proj}Targets
RUNTIME
DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
foreach(dir ${pybind11_INCLUDE_DIRS})
install(DIRECTORY ${dir} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endforeach()
install(
EXPORT ${module_proj}Targets
FILE ${module_proj}Targets.cmake
NAMESPACE ${_name}::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${_name}
)
endfunction()
+65
View File
@@ -0,0 +1,65 @@
include_guard(DIRECTORY)
include(GNUInstallDirs)
function(init_python_plugin)
set(_options)
set(_one_value_args NAME PLUGIN_NAME)
set(_multi_value_args SOURCES)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
set_first_defined(_name _ARGS_NAME PROJECT_NAME)
set(_plugin_name ${_ARGS_PLUGIN_NAME})
if (NOT DEFINED _ARGS_PLUGIN_NAME)
set(_plugin_name ${_name}_plugins)
endif()
set(${_plugin_name}_SOURCES ${_ARGS_SOURCES})
if (NOT DEFINED _ARGS_SOURCES)
file(
GLOB_RECURSE ${_plugin_name}_SOURCES
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${_plugin_name}/*.py)
endif()
set(src_outs)
foreach(_src ${${_plugin_name}_SOURCES})
set(_src_in ${CMAKE_CURRENT_SOURCE_DIR}/${_src})
set(_src_out ${CMAKE_CURRENT_BINARY_DIR}/${_src})
add_custom_command(
OUTPUT ${_src_out}
COMMAND ${CMAKE_COMMAND} -E copy ${_src_in} ${_src_out}
DEPENDS ${_src_in}
COMMENT "Copying ${_plugin_name} plugin ${_src} to ${_src_out}"
)
list(APPEND src_outs ${_src_out})
endforeach()
add_custom_target(copy_${_plugin_name} ALL DEPENDS ${src_outs})
add_library(${_plugin_name} INTERFACE)
add_library(${_name}::${_plugin_name} ALIAS ${_plugin_name})
set_target_properties(
${_plugin_name}
PROPERTIES
INTERFACE_PYTHON_SCRIPTS_DIR
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/${_plugin_name}>$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/${_plugin_name}>
)
set_target_properties(
${_plugin_name}
PROPERTIES
INTERFACE_PYTHON_SCRIPTS $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/${${_plugin_name}_SOURCES}>$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/${${_plugin_name}_SOURCES}>
)
install(
TARGETS ${_plugin_name}
EXPORT ${_plugin_name}Targets
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
EXPORT ${_plugin_name}Targets
FILE ${_plugin_name}Targets.cmake
NAMESPACE ${_name}::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${_name}
)
install(DIRECTORY ${_plugin_name} DESTINATION ${CMAKE_INSTALL_BINDIR})
endfunction()
+182
View File
@@ -0,0 +1,182 @@
set(Protobuf_PROTOC_EXECUTABLE "${EXTERNALS_BIN}/protoc")
set(PROTOBUF_INCLUDE_DIRS "${EXTERNALS_INCLUDE}")
set(PROTOBUF_STATIC_LIB "libprotobuf")
set(_protobuf_src_dir ${CMAKE_SOURCE_DIR}/src/lib_spark)
set(_protobuf_bin_dir ${CMAKE_BINARY_DIR}/src/lib_spark)
function(Protobuf_Generate_Cpp)
set(_options)
set(_one_value_args CXX_SOURCES CXX_HEADERS CXX_OUT_DIR)
set(_multi_value_args PROTO_INCLUDE_DIRS PROTO_SOURCES)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
set(${_ARGS_CXX_SOURCES})
set(${_ARGS_CXX_HEADERS})
set(_proto_include_cli_args)
foreach(_dir ${_ARGS_PROTO_INCLUDE_DIRS})
list(APPEND _proto_include_cli_args -I ${_dir})
endforeach()
foreach(FIL ${_ARGS_PROTO_SOURCES})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
get_filename_component(FIL_DIR ${FIL} DIRECTORY)
if(FIL_DIR)
set(FIL_WE "${FIL_DIR}/${FIL_WE}")
endif()
list(APPEND ${_ARGS_CXX_SOURCES} "${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.cc")
list(APPEND ${_ARGS_CXX_HEADERS} "${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.h")
add_custom_command(
OUTPUT
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.cc"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.h"
COMMAND
${Protobuf_PROTOC_EXECUTABLE}
ARGS
--cpp_out=${_ARGS_CXX_OUT_DIR} ${_proto_include_cli_args} ${ABS_FIL}
DEPENDS ${ABS_FIL}
COMMENT "Running C++ protocol buffer compiler on ${FIL}"
VERBATIM
)
endforeach()
set_source_files_properties(
${${_ARGS_CXX_SOURCES}}
${${_ARGS_CXX_HEADERS}}
PROPERTIES
GENERATED TRUE
)
# remove some warnings from protobuf generated files
if(WIN32)
set_source_files_properties(
${${_ARGS_CXX_SOURCES}}
PROPERTIES
COMPILE_FLAGS "/wd4309 /wd4838"
)
else()
set_source_files_properties(
${${_ARGS_CXX_SOURCES}}
PROPERTIES
COMPILE_FLAGS "-Wno-unused-parameter -Wno-effc++ -Wno-pedantic -Wno-sign-compare -Wno-uninitialized"
)
endif()
set(${_ARGS_CXX_SOURCES} ${${_ARGS_CXX_SOURCES}} PARENT_SCOPE)
set(${_ARGS_CXX_HEADERS} ${${_ARGS_CXX_HEADERS}} PARENT_SCOPE)
endfunction()
function(Service_Compiler_Generate_Cpp)
set(_options)
set(_one_value_args CXX_SOURCES CXX_HEADERS CXX_OUT_DIR)
set(_multi_value_args PROTO_INCLUDE_DIRS PROTO_SOURCES)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
set(${_ARGS_CXX_SOURCES})
set(${_ARGS_CXX_HEADERS})
set(_proto_include_cli_args)
foreach(_dir ${_ARGS_PROTO_INCLUDE_DIRS})
list(APPEND _proto_include_cli_args -I ${_dir})
endforeach()
foreach(FIL ${_ARGS_PROTO_SOURCES})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
get_filename_component(FIL_DIR ${FIL} DIRECTORY)
if(FIL_DIR)
set(FIL_WE "${FIL_DIR}/${FIL_WE}")
endif()
list(APPEND ${_ARGS_CXX_SOURCES} "${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.cc")
list(APPEND ${_ARGS_CXX_HEADERS} "${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.h")
list(APPEND ${_ARGS_CXX_SOURCES} "${_ARGS_CXX_OUT_DIR}/${FIL_WE}.client.pb.cc")
list(APPEND ${_ARGS_CXX_HEADERS} "${_ARGS_CXX_OUT_DIR}/${FIL_WE}.client.pb.h")
list(APPEND ${_ARGS_CXX_SOURCES} "${_ARGS_CXX_OUT_DIR}/${FIL_WE}.server.pb.cc")
list(APPEND ${_ARGS_CXX_HEADERS} "${_ARGS_CXX_OUT_DIR}/${FIL_WE}.server.pb.h")
set(TEMPLATES
${CMAKE_SOURCE_DIR}/src/lib_spark/spark/api/service_compiler/templates/cpp/client.pb.cc
${CMAKE_SOURCE_DIR}/src/lib_spark/spark/api/service_compiler/templates/cpp/client.pb.h
${CMAKE_SOURCE_DIR}/src/lib_spark/spark/api/service_compiler/templates/cpp/server.pb.cc
${CMAKE_SOURCE_DIR}/src/lib_spark/spark/api/service_compiler/templates/cpp/server.pb.h
)
set(_plugin_py ${CMAKE_SOURCE_DIR}/src/lib_spark/spark/api/service_compiler/plugin.py)
if(WIN32)
set(_plugin_cmd ${CMAKE_SOURCE_DIR}/src/lib_spark/spark/api/service_compiler/plugin.cmd)
add_custom_command(
OUTPUT
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.cc"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.h"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.client.pb.cc"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.client.pb.h"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.server.pb.cc"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.server.pb.h"
COMMAND SET PYTHONPATH=${SPARK_PYTHON}
COMMAND ${CMAKE_SOURCE_DIR}/.venv/Scripts/activate.bat
COMMAND
${Protobuf_PROTOC_EXECUTABLE}
ARGS
--cpp_out=${_ARGS_CXX_OUT_DIR}
--plugin=protoc-gen-service=${_plugin_cmd}
--service_out=lang=cpp:${_ARGS_CXX_OUT_DIR}
-I ${PROTOBUF_INCLUDE_DIRS}
-I ${_protobuf_src_dir}
${ABS_FIL}
DEPENDS
${ABS_FIL}
${TEMPLATES}
${_plugin_cmd}
${_plugin_py}
COMMENT "Running C++ service compiler on ${FIL}"
VERBATIM
)
else()
add_custom_command(
OUTPUT
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.cc"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.pb.h"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.client.pb.cc"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.client.pb.h"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.server.pb.cc"
"${_ARGS_CXX_OUT_DIR}/${FIL_WE}.server.pb.h"
COMMAND ${CMAKE_SOURCE_DIR}/tools/venv_exec.sh
ARGS
${Protobuf_PROTOC_EXECUTABLE}
--cpp_out=${_ARGS_CXX_OUT_DIR}
--plugin=protoc-gen-service=${_plugin_py}
--service_out=lang=cpp,make_file_name=${ABS_FIL}:${_ARGS_CXX_OUT_DIR}
${_proto_include_cli_args} ${ABS_FIL}
DEPENDS
${ABS_FIL}
${TEMPLATES}
${_plugin_py}
COMMENT "Running C++ service compiler on ${FIL}"
VERBATIM
)
endif()
endforeach()
# remove some warnings from protobuf generated files
if(WIN32)
set_source_files_properties(
${${_ARGS_CXX_SOURCES}}
PROPERTIES
COMPILE_FLAGS "/wd4309 /wd4838"
)
else()
set_source_files_properties(
${${_ARGS_CXX_SOURCES}}
PROPERTIES
COMPILE_FLAGS "-Wno-unused-parameter -Wno-effc++ -Wno-pedantic -Wno-sign-compare -Wno-uninitialized"
)
endif()
set(${_ARGS_CXX_SOURCES} ${${_ARGS_CXX_SOURCES}} PARENT_SCOPE)
set(${_ARGS_CXX_HEADERS} ${${_ARGS_CXX_HEADERS}} PARENT_SCOPE)
endfunction()
+4 -1
View File
@@ -39,6 +39,8 @@ function(protobuf_generate_cpp)
list(APPEND ${_ARGS_CXX_SOURCES} "${_ARGS_CXX_OUT_DIR}/${_fil_we}.pb.cc")
list(APPEND ${_ARGS_CXX_HEADERS} "${_ARGS_CXX_OUT_DIR}/${_fil_we}.pb.h")
message(STATUS "add_custom_command for ${_fil} -> ${_ARGS_CXX_OUT_DIR}/${_fil_we}.pb.h")
# TODO: Pass proto sources by relative path (so this fn can be called from
# arbitrary subdirs).
add_custom_command(
OUTPUT
"${_ARGS_CXX_OUT_DIR}/${_fil_we}.pb.cc"
@@ -46,7 +48,7 @@ function(protobuf_generate_cpp)
COMMAND
${_ARGS_PROTOC}
ARGS
--cpp_out=${_ARGS_CXX_OUT_DIR} ${_proto_include_cli_args} ${_fil}
--cpp_out=${_ARGS_CXX_OUT_DIR} ${_proto_include_cli_args} ${_abs_fil}
DEPENDS ${_abs_fil}
COMMENT "Running C++ protocol buffer compiler on ${_fil}"
VERBATIM
@@ -78,3 +80,4 @@ function(protobuf_generate_cpp)
set(${_ARGS_CXX_SOURCES} ${${_ARGS_CXX_SOURCES}} PARENT_SCOPE)
set(${_ARGS_CXX_HEADERS} ${${_ARGS_CXX_HEADERS}} PARENT_SCOPE)
endfunction()
+3
View File
@@ -1,3 +1,5 @@
include_guard(GLOBAL)
function(protobuf_generate_cpp_config)
set(_options)
set(_one_value_args
@@ -56,3 +58,4 @@ function(protobuf_generate_cpp_config)
file(WRITE "${_ARGS_PROTO_DEFAULTS_HEADER}" "${_cfg_defaults_str}")
endif()
endfunction()
+39
View File
@@ -0,0 +1,39 @@
include_guard(DIRECTORY)
include(${CMAKE_SOURCE_DIR}/cmake/SymLink.cmake)
function(sym_link_python_module)
set(_options)
set(_one_value_args LIB_NAME MODULE_NAME PLUGIN_NAME)
set(_multi_value_args)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}"
"${_multi_value_args}" ${ARGN})
if(NOT DEFINED _ARGS_LIB_NAME)
message(
FATAL_ERROR "LIB_NAME must be specified for sym_link_python_module()")
endif()
set(_lib_name ${_ARGS_LIB_NAME})
set(_module_name ${_ARGS_MODULE_NAME})
if(NOT DEFINED _ARGS_MODULE_NAME)
set(_module_name ${_lib_name}_module)
endif()
set(_plugin_name ${_ARGS_PLUGIN_NAME})
set(_deps ${_lib_name} ${_module_name})
if(DEFINED _ARGS_PLUGIN_NAME)
list(APPEND _deps ${_plugin_name} copy_${_plugin_name})
endif()
add_dependencies(${PROJECT_NAME} ${_deps})
sym_link_local_lib(${_lib_name})
set(_module_suffix cpython-310-x86_64-linux-gnu.so)
sym_link_file(
SOURCE
${CMAKE_BINARY_DIR}/src/lib_${_lib_name}/${_module_name}.${_module_suffix}
DEST
${CMAKE_CURRENT_BINARY_DIR}/${_module_name}.${_module_suffix}
INSTALL_DEST
${CMAKE_INSTALL_BINDIR}/)
if(DEFINED _ARGS_PLUGIN_NAME)
get_target_property(plugins ${_plugin_name} INTERFACE_PYTHON_SCRIPTS_DIR)
sym_link_dir(TARGET_NAME ${PROJECT_NAME}_link_${_plugin_name} SOURCE
${plugins} DEST ${CMAKE_CURRENT_BINARY_DIR}/${_plugin_name})
endif()
endfunction()
+11
View File
@@ -0,0 +1,11 @@
#ifndef BTS_SPARK_VERSION_H
#define BTS_SPARK_VERSION_H
#define BTS_SPARK_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define BTS_SPARK_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define BTS_SPARK_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define BTS_SPARK_VERSION_BUILD_METADATA @PROJECT_VERSION_BUILD_METADATA@
#define BTS_SPARK_VERSION "@PROJECT_VERSION@"
#define BTS_SPARK_VERSION_FULL "@PROJECT_VERSION_FULL@"
#endif
-24
View File
@@ -1,24 +0,0 @@
cmake_minimum_required(VERSION 3.5)
# Explicitly set policy to convert relative paths to absolute (although this is a default policy,
# setting it explicitly prevents a compile-warning)
cmake_policy(SET CMP0076 NEW)
# add protos to the library to facilitate source_group
target_sources(spark PUBLIC ${Config_Protos} ${NonConfig_Protos} ${Service_Protos})
function(assign_source_group)
foreach(_source IN ITEMS ${ARGN})
if(IS_ABSOLUTE "${_source}")
file(RELATIVE_PATH _source_rel "${CMAKE_CURRENT_SOURCE_DIR}" "${_source}")
else()
set(_source_rel "${_source}")
endif()
get_filename_component(_source_path "${_source_rel}" PATH)
string(REPLACE "/" "\\" _source_path_msvc "${_source_path}")
source_group("${_source_path_msvc}" FILES "${_source}")
set(SOURCE_FILES "${_source}")
endforeach()
endfunction(assign_source_group)
assign_source_group(${SparkLib_Sources} ${Config_Protos} ${NonConfig_Protos} ${Service_Protos})
-51
View File
@@ -1,51 +0,0 @@
services:
sparkd:
build:
dockerfile: Dockerfile
context: ./sparkd
command: [ "./sparkd", "run" ]
ports: [ "8000:8000", "8001:8001", "8002:8002" ]
pubsub:
build:
dockerfile: pubsub.Dockerfile
context: ./DataStream
command: [ "./PubSubServer" ]
environment:
- DOTNET_ENVIRONMENT=Production
ports:
- 22019:22019
md-publisher:
build:
dockerfile: md-publisher.Dockerfile
context: ./DataStream
depends_on:
- pubsub
- sparkd
command:
[
"dockerize",
"-wait",
"tcp://pubsub:22019",
"-wait",
"tcp://sparkd:8000",
"-timeout",
"10s",
"./DataStreamPublisher"
]
environment:
- DOTNET_ENVIRONMENT=MarketData
webapp:
ports:
- 8080:8080
- 8443:8443
build:
dockerfile: webapp.Dockerfile
context: ./DataStream
command: [ "./Bts.DataStreamWebApp.Server" ]
environment:
- ASPNETCORE_HTTP_PORT=8080
- ASPNETCORE_HTTPS_PORT=8443
- ASPNETCORE_ENVIRONMENT=Staging
+137 -53
View File
@@ -1,69 +1,100 @@
services:
sparkd:
volumes:
- ${SPARKD__DATA_DIR:-sparkd_data}:/home/bts/sparkdata/data
build:
dockerfile: src/app_sparkd/Dockerfile
args:
- IMAGE=${SPARKD__IMAGE}
- SOURCE_DIR=${SPARKD__SOURCE_DIR}
- CONF_FILE=${SPARKD__CONF_FILE}
- MD_RECORDING_DIR=${SPARKD__MD_RECORDING_DIR}
command: [ "./sparkd", "run" ]
- CONFIG_DIR=${SPARKD__CONFIG_DIR}
- PYTHON_REQUIREMENTS=${SPARKD__PYTHON_REQUIREMENTS}
shm_size: "${SPARKD__SHM_SIZE:-512mb}"
command: >
sh -c "
echo bts | sudo -S chown -R bts:bts /home/bts/sparkdata/data
if [ -e "/home/bts/sparkdata/data/logging.sparkd/log" ]; then
mv "/home/bts/sparkdata/data/logging.sparkd/log" "/home/bts/sparkdata/data/logging.sparkd/log_$(date '+%Y%m%d-%H%M')"
fi
if [ -e "/home/bts/sparkdata/conf/.env.local" ]; then
echo 'Sourcing /home/bts/sparkdata/conf/.env.local'
set -a
. /home/bts/sparkdata/conf/.env.local
set +a
fi
if [ -d /home/bts/sparkdata/conf/plugins ]; then
echo 'Copying /home/bts/sparkdata/conf/plugins to runtime dir (/home/bts/bin)'
cp -r /home/bts/sparkdata/conf/plugins /home/bts/bin/
fi
./sparkd run"
env_file:
- .env
- ${SPARKD__CONFIG_DIR}/.env
ports:
- "8000:8000"
- "8001:8001"
- "8002:8002"
- "${SPARKD__CONFIG_PORT}:${SPARKD__CONFIG_PORT}"
- "${SPARKD__QUICKBUS_PORT}:${SPARKD__QUICKBUS_PORT}"
- "${SPARKD__SIM_FIX_PORT}:${SPARKD__SIM_FIX_PORT}"
deploy:
resources:
limits:
memory: "${SPARKD__MEMORY:-4gb}"
pubsub:
environment:
- DOTNET_ENVIRONMENT=${PUBSUB__DOTNET_ENVIRONMENT}
sparkd-symbols:
volumes:
- ${SPARKD__DATA_DIR:-sparkd_data}:/home/bts/sparkdata/data
- ${SPARKD__RUNTIME_CONF_DIR:-sparkd_conf}:/home/bts/sparkdata/conf
build:
dockerfile: src/DataStreamSuite/PubSubServer/Dockerfile
dockerfile: src/app_sparkd/Dockerfile
args:
- CS_SOURCE_DIR=${PUBSUB__CS_SOURCE_DIR}
- CPP_SOURCE_DIR=${PUBSUB__CPP_SOURCE_DIR}
- CONF_FILE=${PUBSUB__CONF_FILE}
- IMAGE=${SPARKD__IMAGE}
- SOURCE_DIR=${SPARKD__SOURCE_DIR}
- CONFIG_DIR=${SPARKD__CONFIG_DIR}
- PYTHON_REQUIREMENTS=${SPARKD__PYTHON_REQUIREMENTS}
shm_size: "${SPARKD__SHM_SIZE:-512mb}"
command: >
sh -c "
echo bts | sudo -S chown -R bts:bts /home/bts/sparkdata
if [ -e "/home/bts/sparkdata/data/logging.sparkd/log" ]; then
mv "/home/bts/sparkdata/data/logging.sparkd/log" "/home/bts/sparkdata/data/logging.sparkd/log_$(date '+%Y%m%d-%H%M')"
fi
ls -alh /home/bts/sparkdata/conf/
cat /home/bts/sparkdata/conf/.env.local
if [ -e "/home/bts/sparkdata/conf/.env.local" ]; then
echo 'Sourcing /home/bts/sparkdata/conf/.env.local'
set -a
. /home/bts/sparkdata/conf/.env.local
set +a
fi
if [ -d /home/bts/sparkdata/conf/plugins ]; then
echo 'Copying /home/bts/sparkdata/conf/plugins to runtime dir (/home/bts/bin)'
cp -r /home/bts/sparkdata/conf/plugins /home/bts/bin/
fi
./sparkd run"
env_file:
- .env
- ${SPARKD__CONFIG_DIR}/.env
ports:
- "22019:22019"
command: [ "./PubSubServer" ]
- "${SPARKD__CONFIG_PORT}:${SPARKD__CONFIG_PORT}"
- "${SPARKD__QUICKBUS_PORT}:${SPARKD__QUICKBUS_PORT}"
- "${SPARKD__SIM_FIX_PORT}:${SPARKD__SIM_FIX_PORT}"
deploy:
restart_policy:
condition: any
delay: 3s
resources:
limits:
memory: "${SPARKD__MEMORY:-4gb}"
md-publisher:
build:
dockerfile: src/DataStreamSuite/DataStreamPublisher/Dockerfile
args:
- SOURCE_DIR=${MD_PUBLISHER__SOURCE_DIR}
- CONF_FILE=${MD_PUBLISHER__CONF_FILE}
depends_on:
- pubsub
- sparkd
command: [ "dockerize", "-wait", "tcp://pubsub:22019", "-wait", "tcp://sparkd:8000", "-timeout", "10s", "./DataStreamPublisher" ]
environment:
- DOTNET_ENVIRONMENT=${MD_PUBLISHER__DOTNET_ENVIRONMENT}
webapp:
ports:
- "8080:8080"
- "8443:8443"
build:
dockerfile: src/DataStreamSuite/Bts.DataStreamWebApp.Server/Dockerfile
args:
- SOURCE_DIR=${WEBAPP__SOURCE_DIR}
- WEB_ROOT=${WEBAPP__WEB_ROOT}
- CONF_FILE=${WEBAPP__CONF_FILE}
command: [ "./Bts.DataStreamWebApp.Server" ]
environment:
- ASPNETCORE_ENVIRONMENT=${WEBAPP__ASPNETCORE_ENVIRONMENT}
api-server:
depends_on:
- sparkd
ports:
- 8008:8008
- 9443:9443
build:
dockerfile: src/client/spark_api_server/Dockerfile
command: [ "dockerize", "-wait", "tcp://sparkd:8000", "-timeout", "10s", "./spark_api_server", "run" ]
environment:
- ASPNETCORE_ENVIRONMENT=${API_SERVER__ASPNETCORE_ENVIRONMENT}
sparkd-tail:
image: bluetradingsystems/linux_dotnet7
volumes:
- type: volume
read_only: true
source: sparkd_data
target: /home/bts/sparkdata/data/logging.sparkd
volume:
subpath: logging.sparkd
command: ["tail", "-f", "/home/bts/sparkdata/data/logging.sparkd/log"]
sparkd-ping-test:
depends_on:
@@ -77,3 +108,56 @@ services:
USER "root"
RUN apt-get update && apt-get -y install iputils-ping
command: [ "ping", "sparkd" ]
pubsub:
environment:
- DOTNET_ENVIRONMENT=Staging
build:
dockerfile: src/DataStreamSuite/PubSubServer/Dockerfile
args:
- SOURCE_DIR=build/Release/bin/net8.0/linux-x64/publish
- CONF_FILE=src/DataStreamSuite/PubSubServer/spark.conf
ports:
- 22019:22019
command: [ "./PubSubServer" ]
md-publisher:
build:
dockerfile: src/DataStreamSuite/DataStreamPublisher/Dockerfile
args:
- SOURCE_DIR=build/Release/bin/net8.0/linux-x64/publish
- CONF_FILE=src/DataStreamSuite/DataStreamPublisher/spark.conf
depends_on:
- pubsub
- sparkd
command: [ "dockerize", "-wait", "tcp://pubsub:22019", "-wait", "tcp://sparkd:8000", "-timeout", "10s", "./DataStreamPublisher" ]
environment:
- DOTNET_ENVIRONMENT=MarketData
api-server:
depends_on:
- sparkd
ports:
- 8008:8008
- 9443:9443
build:
dockerfile: src/client/spark_api_server/Dockerfile
command: [ "dockerize", "-wait", "tcp://sparkd:8000", "-timeout", "10s", "./spark_api_server", "run" ]
webapp:
ports:
- 8080:8080
- 8443:8443
build:
dockerfile: src/DataStreamSuite/Bts.DataStreamWebApp.Server/Dockerfile
args:
- SOURCE_DIR=build/Release/bin/net8.0/linux-x64/publish
- WEB_ROOT=build/Bts.DataStreamWebApp.Client/production/dist
- CONF_FILE=src/DataStreamSuite/Bts.DataStreamWebApp.Server/spark.conf
command: [ "./Bts.DataStreamWebApp.Server" ]
environment:
- ASPNETCORE_ENVIRONMENT=Staging
volumes:
sparkd_data:
name: "sparkd_${SPARKD__CONFIG_PORT:-8000}"
@@ -124,8 +124,8 @@ service OrderService {
```
### C++ Example
```cpp
#include "lib/api/public/ServiceContext.h"
#include "lib/api/services/OrderService.client.pb.h"
#include "spark/api/public/ServiceContext.h"
#include "spark/api/services/OrderService.client.pb.h"
using namespace bts::api;
ServiceContext ctx;
auto orders = ctx.locate<orderman::OrderService>();
@@ -29,9 +29,9 @@ namespace sample {
```
```cpp
#include "lib/api/public/ServiceContext.h"
#include "spark/lib/domain_model/Security.h"
#include "lib/api/services/MarketDataService.client.pb.h"
#include "spark/api/public/ServiceContext.h"
#include "spark/domain_model/Security.h"
#include "spark/api/services/MarketDataService.client.pb.h"
#include <thread>
using namespace bts::api;
+1 -1
View File
@@ -53,7 +53,7 @@ The status callback is called when the market for this security opens, closes, o
// Subscribe to security trading status
void IAlgoServices::subscribe_status(SecurityKey);
// You implement
virtual void on_status(SecurityKey, domain::protobufs::TradingStatus) override;
virtual void on_status(SecurityKey, co2::TradingStatus) override;
```
# Child Orders
@@ -0,0 +1,38 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y pkg-config
RUN apt-get update && apt-get install -y locales
RUN apt-get update && apt-get install -y ca-certificates
RUN apt-get update && apt-get install -y libssh2-1-dev
RUN apt-get update && apt-get install -y software-properties-common
RUN apt-get update && apt-get install -y sudo
# GCC
ARG gcc_major=13
RUN add-apt-repository ppa:ubuntu-toolchain-r/test \
&& apt-get update \
&& apt-get install -y g++-$gcc_major
RUN apt-get update \
&& apt-get install -y dotnet7
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"
USER bts
WORKDIR /home/bts
Submodule
+1
Submodule docker/linux added at 6537812262.
+1
Submodule docker/windows added at de7d40c09d.
-6
View File
@@ -1,6 +0,0 @@
{
"sdk": {
"version": "6.0.200",
"rollForward": "latestMajor"
}
}
+20
View File
@@ -1,22 +1,42 @@
astroid==2.7.3
autopep8==1.5.7
blinker==1.9.0
boto3==1.37.9
botocore==1.37.9
certifi==2023.5.7
charset-normalizer==3.1.0
click==8.1.8
colorama==0.4.4
Flask==3.1.0
idna==3.4
isort==5.9.3
itsdangerous==2.2.0
Jinja2==3.1.6
jmespath==1.0.1
lazy-object-proxy==1.6.0
MarkupSafe==3.0.2
mccabe==0.6.1
numpy==2.2.6
platformdirs==2.3.0
protobuf==4.21.12
pybars3==0.9.7
pybind11==2.13.6
pybind11-stubgen==2.5.3
pybit==5.11.0
pycodestyle==2.7.0
pycparser==2.20
pycryptodome==3.23.0
pylint==2.10.2
PyMeta3==0.5.1
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
requests==2.31.0
s3transfer==0.11.4
six==1.17.0
termcolor==1.1.0
toml==0.10.2
urllib3==2.0.3
websocket-client==1.8.0
Werkzeug==3.1.3
wrapt==1.12.1
yapf==0.31.0
+164 -103
View File
@@ -1,103 +1,164 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32922.545
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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark.Console", "src\client\Spark.Console\Spark.Console.csproj", "{387566C6-C6F6-412C-A30C-9855BBCB4816}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
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
MinSizeRel|x64 = MinSizeRel|x64
Release|x64 = Release|x64
RelWithDebInfo|x64 = RelWithDebInfo|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Debug|x64.ActiveCfg = Debug|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Debug|x64.Build.0 = Debug|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.MinSizeRel|x64.ActiveCfg = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.MinSizeRel|x64.Build.0 = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Release|x64.ActiveCfg = Release|x64
{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
{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
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.MinSizeRel|x64.Build.0 = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Release|x64.ActiveCfg = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Release|x64.Build.0 = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.ActiveCfg = Debug|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.Build.0 = Debug|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.MinSizeRel|x64.ActiveCfg = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.MinSizeRel|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Release|x64.ActiveCfg = Release|x64
{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
{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
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x64.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x64.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x64.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x64.Build.0 = Release|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Debug|x64.ActiveCfg = Debug|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Debug|x64.Build.0 = Debug|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.MinSizeRel|x64.ActiveCfg = Debug|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.MinSizeRel|x64.Build.0 = Debug|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Release|x64.ActiveCfg = Release|x64
{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
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {422D1620-0166-4C26-9A81-CA8A2EB1AB58}
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32922.545
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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark.Console", "src\client\Spark.Console\Spark.Console.csproj", "{387566C6-C6F6-412C-A30C-9855BBCB4816}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStreamCore", "src\DataStreamSuite\DataStreamCore\DataStreamCore.csproj", "{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStream", "src\DataStreamSuite\DataStream\DataStream.csproj", "{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedStreams", "src\DataStreamSuite\SharedStreams\SharedStreams.csproj", "{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PubSub", "src\DataStreamSuite\PubSub\PubSub.csproj", "{51C40BA9-F226-4A35-939D-1ED6F5FF7299}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4564E677-343A-46CB-AF02-AFC4E38BF94B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "client", "client", "{56A8A70A-DA03-4CED-A144-EC10F1379FD8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataStreamSuite", "DataStreamSuite", "{915EF238-CA0D-4E73-AF50-88CB3F29C7AD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.Extensions.Hosting", "src\DataStreamSuite\Bts.Extensions.Hosting\Bts.Extensions.Hosting.csproj", "{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
MinSizeRel|x64 = MinSizeRel|x64
Release|x64 = Release|x64
RelWithDebInfo|x64 = RelWithDebInfo|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Debug|x64.ActiveCfg = Debug|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Debug|x64.Build.0 = Debug|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.MinSizeRel|x64.ActiveCfg = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.MinSizeRel|x64.Build.0 = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Release|x64.ActiveCfg = Release|x64
{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
{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
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.MinSizeRel|x64.Build.0 = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Release|x64.ActiveCfg = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Release|x64.Build.0 = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.ActiveCfg = Debug|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.Build.0 = Debug|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.MinSizeRel|x64.ActiveCfg = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.MinSizeRel|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Release|x64.ActiveCfg = Release|x64
{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
{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
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x64.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x64.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x64.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x64.Build.0 = Release|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Debug|x64.ActiveCfg = Debug|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Debug|x64.Build.0 = Debug|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.MinSizeRel|x64.ActiveCfg = Debug|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.MinSizeRel|x64.Build.0 = Debug|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Release|x64.ActiveCfg = Release|x64
{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
{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}.Debug|x64.ActiveCfg = Debug|Any CPU
{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}.Debug|x64.Build.0 = Debug|Any CPU
{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}.Release|x64.ActiveCfg = Release|Any CPU
{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}.Release|x64.Build.0 = Release|Any CPU
{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{4E4F491F-C688-43C4-88DA-AC92BC5FA1B8}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}.Debug|x64.ActiveCfg = Debug|Any CPU
{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}.Debug|x64.Build.0 = Debug|Any CPU
{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}.Release|x64.ActiveCfg = Release|Any CPU
{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}.Release|x64.Build.0 = Release|Any CPU
{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{2AC54ADC-3246-427E-9CA9-FEA2DBCB9432}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}.Debug|x64.ActiveCfg = Debug|Any CPU
{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}.Debug|x64.Build.0 = Debug|Any CPU
{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}.Release|x64.ActiveCfg = Release|Any CPU
{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}.Release|x64.Build.0 = Release|Any CPU
{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{4E96E5CC-7791-40B3-A8A1-C34FBB736B3B}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{51C40BA9-F226-4A35-939D-1ED6F5FF7299}.Debug|x64.ActiveCfg = Debug|Any CPU
{51C40BA9-F226-4A35-939D-1ED6F5FF7299}.Debug|x64.Build.0 = Debug|Any CPU
{51C40BA9-F226-4A35-939D-1ED6F5FF7299}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{51C40BA9-F226-4A35-939D-1ED6F5FF7299}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{51C40BA9-F226-4A35-939D-1ED6F5FF7299}.Release|x64.ActiveCfg = Release|Any CPU
{51C40BA9-F226-4A35-939D-1ED6F5FF7299}.Release|x64.Build.0 = Release|Any CPU
{51C40BA9-F226-4A35-939D-1ED6F5FF7299}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{51C40BA9-F226-4A35-939D-1ED6F5FF7299}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}.Debug|x64.ActiveCfg = Debug|Any CPU
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}.Debug|x64.Build.0 = Debug|Any CPU
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}.Release|x64.ActiveCfg = Release|Any CPU
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}.Release|x64.Build.0 = Release|Any CPU
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {422D1620-0166-4C26-9A81-CA8A2EB1AB58}
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{56A8A70A-DA03-4CED-A144-EC10F1379FD8} = {4564E677-343A-46CB-AF02-AFC4E38BF94B}
{915EF238-CA0D-4E73-AF50-88CB3F29C7AD} = {4564E677-343A-46CB-AF02-AFC4E38BF94B}
{4E0E51AA-7B59-4808-B7CC-9EAEDF397460} = {915EF238-CA0D-4E73-AF50-88CB3F29C7AD}
EndGlobalSection
EndGlobal
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -6,23 +6,80 @@ using Spark.Core.Collections;
static class ArgumentComparer {
public static int Compare(
IArgument value1, IArgument value2, IDictionary<string,object?> env) {
IArgument value1, IArgument value2, IDictionary<string, object?> env) {
if (value1 is null) {
throw new ArgumentNullException(nameof(value1));
}
if (value2 is null) {
throw new ArgumentNullException(nameof(value2));
}
var cmp1 = value1.Cast<IComparable>();
var cmp2 = value2.Cast<IComparable>();
var obj1 = value1.GetValue(env);
var obj2 = value2.GetValue(env);
if (obj1 is null) {
if (obj2 is null) {
return 0;
}
return -1;
}
if (obj2 is null) {
return 1;
}
return compareObjs(obj1, obj2);
}
static int compareObjs(object obj1, object obj2) {
try {
var v1 = cmp1.GetValue(env);
var v2 = cmp2.GetValue(env);
return v1.GetType().IsNumeric() ?
new NumericComparer().Compare(v1, v2) : v1.CompareTo(v2);
var typ1 = obj1.GetType();
var typ2 = obj2.GetType();
bool areSameTypes = typ1 == typ2;
if (areSameTypes) {
return ((IComparable)obj1).CompareTo(obj2);
}
if (typ1.IsNumeric()) {
return tryCompareNumeric(obj1, obj2);
} else if (typ2.IsNumeric()) {
return -tryCompareNumeric(obj2, obj1);
} else if (typ1.IsEnum) {
if (typ2 == typeof(string)) {
return -((string)obj2).CompareTo(obj1.ToString());
}
} else if (typ1 == typeof(string)) {
return ((string)obj1).CompareTo(obj2.ToString());
} else if (typ1 == typeof(DateTime)) {
if (typ2 == typeof(TimeSpan)) {
return ((DateTime)obj1).Ticks.CompareTo(((TimeSpan)obj2).Ticks);
}
} else if (typ1 == typeof(TimeSpan)) {
if (typ2 == typeof(DateTime)) {
return ((TimeSpan)obj1).Ticks.CompareTo(((DateTime)obj2).Ticks);
}
}
} catch (Exception ex) {
Log.Ui.error($"Comparison failed: {ex}");
throw;
}
throw new ArgumentException("Failed to compare obj1 and obj2", nameof(obj1));
}
static int tryCompareNumeric(object numericObj1, object obj2) {
var typ2 = obj2.GetType();
if (typ2.IsNumeric()) {
return new NumericComparer().Compare(numericObj1, obj2);
} else if (typ2.IsEnum) {
var undTyp2 = typ2.GetEnumUnderlyingType();
if (undTyp2 == typeof(byte)) {
return new NumericComparer().Compare(numericObj1, (byte)obj2);
}
if (undTyp2 == typeof(short)) {
return new NumericComparer().Compare(numericObj1, (short)obj2);
}
if (undTyp2 == typeof(int)) {
return new NumericComparer().Compare(numericObj1, (int)obj2);
}
if (undTyp2 == typeof(long)) {
return new NumericComparer().Compare(numericObj1, (long)obj2);
}
}
throw new ArgumentException("Failed to compare numeric obj1 and obj2", nameof(obj2));
}
}
@@ -41,7 +41,6 @@ Identifier {Letter}({Letter}*[0-9]*_*)*
OpenParenthesis \(
CloseParenthesis \)
Comma ,
Negate '!'
MapsTo ':'
IsOfType '/'
@@ -94,10 +93,6 @@ IsOfType '/'
return (int)Token.COMMA;
}
{Negate} {
return (int)Token.NEGATE;
}
{MapsTo} {
return (int)Token.MAPS_TO;
}
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.24.3" />
<PackageReference Include="YaccLexTools" Version="1.2.0" />
<PackageReference Include="YaccLexTools" Version="1.2.3" />
</ItemGroup>
<ItemGroup>
@@ -2,6 +2,8 @@ namespace Bts.BooleanExpressions;
using Bts.BooleanExpressions.Arguments;
using Spark.Core.Collections;
class BoundBooleanExpression : IBooleanExpression {
readonly IBooleanExpression expression;
readonly IDictionary<string, FunctionDefinition> definitions;
@@ -17,10 +19,10 @@ class BoundBooleanExpression : IBooleanExpression {
}
public bool Evaluate(IDictionary<string, object?> env) {
foreach (var kv in env) {
identifiers[kv.Key] = kv.Value;
if (identifiers.Count > 0) {
env = AggregateDictionary.Create(env, identifiers);
}
env = new EvaluationEnvironment(identifiers, definitions);
env = new EvaluationEnvironment(env, definitions);
return expression.Evaluate(env);
}
@@ -24,8 +24,6 @@ namespace QUT.GplexBuffers
public BufferException(string message) : base(message) { }
public BufferException(string message, Exception innerException)
: base(message, innerException) { }
protected BufferException(SerializationInfo info, StreamingContext context)
: base(info, context) { }
}
internal abstract class ScanBuff
@@ -60,12 +58,10 @@ namespace QUT.GplexBuffers
return new BuildBuffer(source);
}
#if (!BYTEMODE)
public static ScanBuff GetBuffer(Stream source, int fallbackCodePage)
{
return new BuildBuffer(source, fallbackCodePage);
}
#endif // !BYTEMODE
#endif // !NOFILES
}
@@ -353,17 +349,15 @@ namespace QUT.GplexBuffers
{
FileStream fStrm = (stream as FileStream);
if (fStrm != null) FileName = fStrm.Name;
NextBlk = BlockReaderFactory.Raw(stream);
NextBlk = BlockReaderFactory.Get(stream);
}
#if (!BYTEMODE)
public BuildBuffer(Stream stream, int fallbackCodePage)
{
FileStream fStrm = (stream as FileStream);
if (fStrm != null) FileName = fStrm.Name;
NextBlk = BlockReaderFactory.Get(stream, fallbackCodePage);
}
#endif
/// <summary>
/// Marks a conservative lower bound for the buffer,
@@ -434,43 +428,19 @@ namespace QUT.GplexBuffers
//
internal static class BlockReaderFactory
{
public static BlockReader Raw(Stream stream)
{
return delegate(char[] block, int index, int number)
{
byte[] b = new byte[number];
int count = stream.Read(b, 0, number);
int i = 0;
int j = index;
for (; i < count; i++, j++)
block[j] = (char)b[i];
return count;
};
}
#if (!BYTEMODE)
public static BlockReader Get(Stream stream, int fallbackCodePage)
public static BlockReader Get(Stream stream, int fallbackCodePage = -2)
{
Encoding encoding;
int preamble = Preamble(stream);
if (preamble != 0) // There is a valid BOM here!
encoding = Encoding.GetEncoding(preamble);
else if (fallbackCodePage == -1) // Fallback is "raw" bytes
return Raw(stream);
else if (fallbackCodePage != -2) // Anything but "guess"
else if (fallbackCodePage >= 0) // Anything but "guess"
encoding = Encoding.GetEncoding(fallbackCodePage);
else // This is the "guess" option
{
int guess = new Guesser(stream).GuessCodePage();
stream.Seek(0, SeekOrigin.Begin);
if (guess == -1) // ==> this is a 7-bit file
encoding = Encoding.ASCII;
else if (guess == 65001)
encoding = Encoding.UTF8;
else // ==> use the machine default
encoding = Encoding.Default;
}
encoding = GuessEncoding(stream);
StreamReader reader = new StreamReader(stream, encoding);
return reader.Read;
}
@@ -488,6 +458,14 @@ namespace QUT.GplexBuffers
int b2 = stream.ReadByte();
if (b0 == 0xef && b1 == 0xbb && b2 == 0xbf)
return 65001; // UTF8
int b3 = stream.ReadByte();
// Check for UTF-32 BOM (Byte Order Mark)
if (b0 == 0x00 && b1 == 0x00 && b2 == 0xFE && b3 == 0xFF)
return 12001; // UTF-32BE
if (b0 == 0xFF && b1 == 0xFE && b2 == 0x00 && b3 == 0x00)
return 12000; // UTF-32LE
//
// There is no unicode preamble, so we
// return denoter for the machine default.
@@ -495,7 +473,150 @@ namespace QUT.GplexBuffers
stream.Seek(0, SeekOrigin.Begin);
return 0;
}
#endif // !BYTEMODE
private static Encoding GuessEncoding(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
if (IsUtf16(stream, out Encoding encoding))
{
stream.Seek(0, SeekOrigin.Begin);
return encoding;
}
stream.Seek(0, SeekOrigin.Begin);
if (IsUtf8(stream))
{
stream.Seek(0, SeekOrigin.Begin);
return Encoding.UTF8;
}
stream.Seek(0, SeekOrigin.Begin);
if (IsAscii(stream))
{
stream.Seek(0, SeekOrigin.Begin);
return Encoding.ASCII;
}
#if !NET462_OR_GREATER
stream.Seek(0, SeekOrigin.Begin);
if (IsLatin1(stream))
{
stream.Seek(0, SeekOrigin.Begin);
return Encoding.Latin1;
}
#endif
stream.Seek(0, SeekOrigin.Begin);
return Encoding.Default;
}
private static bool IsUtf8(Stream stream)
{
int b;
while ((b = stream.ReadByte()) != -1)
{
if ((b & 0x80) == 0) // 0xxxxxxx
{
continue;
}
else if ((b & 0xE0) == 0xC0) // 110xxxxx 10xxxxxx
{
if (!IsValidUtf8ContinuationByte(stream)) return false;
}
else if ((b & 0xF0) == 0xE0) // 1110xxxx 10xxxxxx 10xxxxxx
{
if (!IsValidUtf8ContinuationByte(stream) || !IsValidUtf8ContinuationByte(stream)) return false;
}
else if ((b & 0xF8) == 0xF0) // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
{
if (!IsValidUtf8ContinuationByte(stream) || !IsValidUtf8ContinuationByte(stream) || !IsValidUtf8ContinuationByte(stream)) return false;
}
else
{
return false;
}
}
return true;
}
private static bool IsValidUtf8ContinuationByte(Stream stream)
{
int b = stream.ReadByte();
return b != -1 && (b & 0xC0) == 0x80; // 10xxxxxx
}
private static bool IsUtf16(Stream stream, out Encoding encoding)
{
encoding = Encoding.Default;
if (stream.Length >= 2)
{
// Read the first two bytes
int b0 = stream.ReadByte();
int b1 = stream.ReadByte();
// Check for UTF-16 BOM (Byte Order Mark)
if (b0 == 0xFF && b1 == 0xFE)
{
encoding = Encoding.Unicode;
return true; // UTF-16LE
}
if (b0 == 0xFE && b1 == 0xFF)
{
encoding = Encoding.BigEndianUnicode;
return true; // UTF-16BE
}
// Reset the stream position
stream.Seek(0, SeekOrigin.Begin);
// Check for common UTF-16 patterns
byte[] buffer = new byte[2];
while (stream.Read(buffer, 0, 2) == 2)
{
if (buffer[0] == 0x00 && buffer[1] != 0x00)
{
encoding = Encoding.BigEndianUnicode;
return true; // Likely UTF-16BE
}
if (buffer[0] != 0x00 && buffer[1] == 0x00)
{
encoding = Encoding.Unicode;
return true; // Likely UTF-16LE
}
}
}
return false;
}
private static bool IsAscii(Stream stream)
{
int b;
while ((b = stream.ReadByte()) != -1)
{
// Latin1 includes byte values from 0x00 to 0xFF
if (b > 0x7F)
{
return false;
}
}
return true;
}
private static bool IsLatin1(Stream stream)
{
int b;
while ((b = stream.ReadByte()) != -1)
{
// Latin1 includes byte values from 0x00 to 0xFF
if (b > 0xFF)
{
return false;
}
}
return true;
}
}
#endif // !NOFILES
#endregion Buffer classes
@@ -540,180 +661,6 @@ namespace QUT.GplexBuffers
return 0;
}
}
#region guesser
#if (!BYTEMODE)
// ==============================================================
// ============ Encoding Guesser =============
// ==============================================================
/// <summary>
/// This class provides a simple finite state automaton that
/// scans the file looking for (1) valid UTF-8 byte patterns,
/// (2) bytes >= 0x80 which are not part of a UTF-8 sequence.
/// The method then guesses whether it is UTF-8 or maybe some
/// local machine default encoding. This works well for the
/// various Latin encodings.
/// </summary>
internal class Guesser
{
ScanBuff buffer;
public int GuessCodePage() { return Scan(); }
const int maxAccept = 10;
const int initial = 0;
const int eofNum = 0;
const int goStart = -1;
const int INITIAL = 0;
const int EndToken = 0;
#region user code
/*
* Reads the bytes of a file to determine if it is
* UTF-8 or a single-byte code page file.
*/
public long utfX;
public long uppr;
#endregion user code
int state;
int currentStart = startState[0];
int code;
#region ScannerTables
static int[] startState = new int[] { 11, 0 };
#region CharacterMap
static sbyte[] map = new sbyte[256] {
/* '\0' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* '\x10' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* '\x20' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* '0' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* '@' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 'P' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* '`' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 'p' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* '\x80' */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* '\x90' */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* '\xA0' */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* '\xB0' */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* '\xC0' */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* '\xD0' */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* '\xE0' */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
/* '\xF0' */ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5 };
#endregion
static sbyte[][] nextState = new sbyte[][] {
new sbyte[] {0, 0, 0, 0, 0, 0},
new sbyte[] {-1, -1, 10, -1, -1, -1},
new sbyte[] {-1, -1, -1, -1, -1, -1},
new sbyte[] {-1, -1, 8, -1, -1, -1},
new sbyte[] {-1, -1, 5, -1, -1, -1},
new sbyte[] {-1, -1, 6, -1, -1, -1},
new sbyte[] {-1, -1, 7, -1, -1, -1},
null,
new sbyte[] {-1, -1, 9, -1, -1, -1},
null,
null,
new sbyte[] {-1, 1, 2, 3, 4, 2}
};
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
// Reason for suppression: cannot have self-reference in array initializer.
static Guesser()
{
nextState[7] = nextState[2];
nextState[9] = nextState[2];
nextState[10] = nextState[2];
}
int NextState()
{
if (code == ScanBuff.EndOfFile)
return eofNum;
else
return nextState[state][map[code]];
}
#endregion
public Guesser(System.IO.Stream file) { SetSource(file); }
public void SetSource(System.IO.Stream source)
{
this.buffer = new BuildBuffer(source);
code = buffer.Read();
}
int Scan()
{
for (; ; )
{
int next;
state = currentStart;
while ((next = NextState()) == goStart)
code = buffer.Read();
state = next;
code = buffer.Read();
while ((next = NextState()) > eofNum)
{
state = next;
code = buffer.Read();
}
if (state <= maxAccept)
{
#region ActionSwitch
#pragma warning disable 162
switch (state)
{
case eofNum:
switch (currentStart)
{
case 11:
if (utfX == 0 && uppr == 0) return -1; /* raw ascii */
else if (uppr * 10 > utfX) return 0; /* default code page */
else return 65001; /* UTF-8 encoding */
break;
}
return EndToken;
case 1: // Recognized '{Upper128}', Shortest string "\xC0"
case 2: // Recognized '{Upper128}', Shortest string "\x80"
case 3: // Recognized '{Upper128}', Shortest string "\xE0"
case 4: // Recognized '{Upper128}', Shortest string "\xF0"
uppr++;
break;
case 5: // Recognized '{Utf8pfx4}{Utf8cont}', Shortest string "\xF0\x80"
uppr += 2;
break;
case 6: // Recognized '{Utf8pfx4}{Utf8cont}{2}', Shortest string "\xF0\x80\x80"
uppr += 3;
break;
case 7: // Recognized '{Utf8pfx4}{Utf8cont}{3}', Shortest string "\xF0\x80\x80\x80"
utfX += 3;
break;
case 8: // Recognized '{Utf8pfx3}{Utf8cont}', Shortest string "\xE0\x80"
uppr += 2;
break;
case 9: // Recognized '{Utf8pfx3}{Utf8cont}{2}', Shortest string "\xE0\x80\x80"
utfX += 2;
break;
case 10: // Recognized '{Utf8pfx2}{Utf8cont}', Shortest string "\xC0\x80"
utfX++;
break;
default:
break;
}
#pragma warning restore 162
#endregion
}
}
}
} // end class Guesser
#endif // !BYTEMODE
#endregion
#endif // !NOFILES
// End of code copied from embedded resource
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -0,0 +1,20 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.3.1' } },
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
@@ -1,36 +0,0 @@
import importPlugin from "eslint-plugin-import";
import reactPkg from "eslint-plugin-react";
const { configs: reactConfigs } = reactPkg;
import js from "@eslint/js";
import defineConfig from "./vite.config.js";
export default [
js.configs.recommended,
importPlugin.flatConfigs.recommended,
reactConfigs.flat.recommended,
reactConfigs.flat["jsx-runtime"],
{
files: ["**/*.{js,jsx}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
console: "readonly"
}
},
settings: {
"import/resolver": {
vite: {
viteConfig: defineConfig({ mode: "development" })
}
}
},
rules: {
semi: "error",
"prefer-const": "error",
"no-unused-vars": "warn",
"import/no-dynamic-require": "warn",
"import/no-nodejs-modules": "warn",
}
}
];
File diff suppressed because it is too large. Load diff
@@ -10,37 +10,34 @@
"preview": "vite preview"
},
"dependencies": {
"@angular/material": "^18.2.8",
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@fontsource/noto-sans": "^5.1.0",
"@material/data-table": "^15.0.0-canary.423edc3dc.0",
"@mui/icons-material": "^6.1.4",
"@mui/material": "^6.1.4",
"@mui/x-data-grid": "^7.20.0",
"bulma": "^1.0.2",
"@angular/material": "^18.0.4",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@fontsource/noto-sans": "^5.0.22",
"@material/data-table": "^15.0.0-canary.7f224ddd4.0",
"@mui/icons-material": "^5.15.20",
"@mui/material": "^5.15.20",
"@mui/x-data-grid": "^7.7.1",
"bulma": "^1.0.1",
"classnames": "^2.5.1",
"react": "^18.3.1",
"react-data-grid": "^7.0.0-beta.47",
"react-data-grid": "^7.0.0-beta.44",
"react-data-table-component": "^7.6.2",
"react-dom": "^18.3.1",
"react-json-view-lite": "^1.5.0",
"react-json-view-lite": "^1.4.0",
"react-pro-sidebar": "^1.1.0",
"react-use-websocket": "^4.9.0",
"rpc-websockets": "^9.0.4"
"react-use-websocket": "^4.8.1",
"rpc-websockets": "^7.11.2"
},
"devDependencies": {
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.2",
"eslint": "^9.12.0",
"@eslint/js": "^9.12.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.12",
"eslint-import-resolver-vite": "^2.0.1",
"react-mui-sidebar": "^1.3.8",
"vite": "^5.4.9"
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.2",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"react-mui-sidebar": "^1.3.5",
"vite": "^5.2.13"
}
}
@@ -12,7 +12,7 @@ const QueryStatusView = ({ queryState, details, error }) => {
let header = queryState;
let headerContent = null;
const detailElements = [];
let detailElements = [];
let severity = queryState == "Subscribed" ? "success" : "info";
let cursor = details == null ? "default" : "pointer";
if (error != null) {
@@ -20,14 +20,14 @@ const QueryStatusView = ({ queryState, details, error }) => {
cursor = "pointer";
header = `Error Code: ${error.code}`;
headerContent = error.message;
detailElements.push(<Typography key="error-msg" variant="body1">{error.data.error_message}</Typography>);
detailElements.push(<Typography key="error-details" variant="subtitle1">Stack Trace:</Typography>);
(error.data.stack_trace ?? []).forEach((line, idx) => detailElements.push(
<Typography key={`stacktrace-${idx}`} variant="body2" component="pre" sx={{ whiteSpace: 'pre-wrap' }}>
detailElements.push(<Typography variant="body1">{error.data.error_message}</Typography>);
detailElements.push(<Typography variant="subtitle1">Stack Trace:</Typography>);
(error.data.stack_trace ?? []).forEach(line => detailElements.push(
<Typography variant="body2" component="pre" sx={{ whiteSpace: 'pre-wrap' }}>
{line}
</Typography>));
} else if (details != null) {
detailElements.push(<Typography key="status-msg" variant="body1">{details}</Typography>);
detailElements.push(<Typography variant="body1">{details}</Typography>);
}
return (
<Box sx={{ margin: theme.spacing(2, 0, 0, 0) }}>
@@ -29,12 +29,13 @@ if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) {
}
}
const target = env.ASPNETCORE_HTTPS_PORT
? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}`
: env.ASPNETCORE_URLS
? env.ASPNETCORE_URLS.split(';')[0]
: 'https://localhost:8443';
export default defineConfig(({ $$command, mode, $$isSsrBuild, $$isPreview }) => {
const target = env.ASPNETCORE_HTTPS_PORT
? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}`
: env.ASPNETCORE_URLS
? env.ASPNETCORE_URLS.split(';')[0]
: 'https://localhost:8443';
return {
plugins: [plugin()],
resolve: {
@@ -67,10 +68,6 @@ export default defineConfig(({ $$command, mode, $$isSsrBuild, $$isPreview }) =>
'^/ws': {
target,
secure: false
},
'^/browse': {
target,
secure: false
}
},
port: 5173,
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
@@ -12,10 +12,10 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="7.0.20" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="7.0.20" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="8.0.18" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.18" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
</ItemGroup>
<ItemGroup>
@@ -1,37 +1,34 @@
namespace Bts.DataStreamWebApp.Server.Controllers;
using Bts.DataStream;
using Microsoft.AspNetCore.Mvc;
[Route("/ws")]
public class DataStreamSubscriptionController : ControllerBase {
readonly ILogger logger;
readonly IDataStreamFactory streamFactory;
public DataStreamSubscriptionController(ILogger<DataStreamSubscriptionController> logger, IEnumerable<IDataStreamFactory> streamFactories) {
this.logger = logger;
logger.LogInformation("Creating {ControllerType}", GetType().Name);
var streamFactoryList = streamFactories.ToList();
streamFactory = streamFactoryList.Count == 1
? streamFactoryList[0]
: new CompositeDataStreamFactory(streamFactoryList);
}
[Route("/ws")]
public async Task Get(CancellationToken cancellationToken) {
if (!HttpContext.WebSockets.IsWebSocketRequest) {
logger.LogError("Expected websocket request on path '{}'", HttpContext.Request.Path);
return;
}
logger.LogInformation("Establishing websocket connection...");
try {
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
logger.LogInformation("Websocket connection established.");
using var server = new DataStreamWebSocketServer(webSocket, streamFactory, logger);
await server.Run(cancellationToken);
} catch (Exception ex) {
logger.LogError("DataStreamSubscriptionController.Run threw exception: {Exception}", ex);
}
}
}
namespace Bts.DataStreamWebApp.Server.Controllers;
using Bts.DataStreamCore;
using Microsoft.AspNetCore.Mvc;
[Route("/ws")]
public class DataStreamSubscriptionController : ControllerBase {
readonly ILogger logger;
readonly IDataStreamFactory streamFactory;
public DataStreamSubscriptionController(ILogger<DataStreamSubscriptionController> logger, IEnumerable<IDataStreamFactory> streamFactories) {
this.logger = logger;
logger.LogInformation("Creating {ControllerType}", GetType().Name);
streamFactory = DataStreamFactory.Create(streamFactories);
}
[Route("/ws")]
public async Task Get(CancellationToken cancellationToken) {
if (!HttpContext.WebSockets.IsWebSocketRequest) {
logger.LogError("Expected websocket request on path '{}'", HttpContext.Request.Path);
return;
}
logger.LogInformation("Establishing websocket connection...");
try {
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
logger.LogInformation("Websocket connection established.");
using var server = new DataStreamWebSocketServer(webSocket, streamFactory, logger);
await server.Run(cancellationToken);
} catch (Exception ex) {
logger.LogError("DataStreamSubscriptionController.Run threw exception: {Exception}", ex);
}
}
}
@@ -2,7 +2,7 @@ namespace Bts.DataStreamWebApp.Server.Controllers;
using bts.websocket_core.json_rpc;
using Bts.DataStream;
using Bts.DataStreamCore;
using Bts.DataStreamWebApp.Server.Messages;
using System.Net.WebSockets;
@@ -1,13 +1,11 @@
FROM bluetradingsystems/linux_dotnet7
ARG CS_SOURCE_DIR="build/Release/bin/net7.0/linux-x64/publish"
ARG CPP_SOURCE_DIR="install/data-stream-suite-linux"
ARG SOURCE_DIR="build/Release/bin/net7.0/linux-x64/publish"
ARG WEB_ROOT="build/Bts.DataStreamWebApp.Client/production/dist"
ARG CONF_FILE="src/DataStreamSuite/Bts.DataStreamWebApp.Server/spark.conf"
RUN dotnet dev-certs https --trust
COPY --chown=bts:bts "${CPP_SOURCE_DIR}/lib" "bin"
COPY --chown=bts:bts "${CS_SOURCE_DIR}" "bin"
COPY --chown=bts:bts "${SOURCE_DIR}" "bin"
COPY --chown=bts:bts "${WEB_ROOT}" "bin/wwwroot"
COPY --chown=bts:bts "${CONF_FILE}" ".local/share/bts/Bts.DataStreamWebApp.Server/conf/spark.conf"
COPY --chown=bts:bts "${CONF_FILE}" ".local/share/DataStream/conf/spark.conf"
WORKDIR "/home/bts/bin"
@@ -4,7 +4,7 @@ using BCG;
using bts.websocket_core.json_rpc;
using Bts.DataStream;
using Bts.DataStreamCore;
using Bts.DataStreamExpressions;
using System.Diagnostics.CodeAnalysis;
@@ -60,7 +60,7 @@ public class SubscribeRequest {
}
Exception? exception = null;
try {
stream = exp.getView(new Dictionary<string, StatementExpression>(0), streamFactory);
stream = exp.Evaluate(streamFactory) as ViewStream;
} catch (Exception ex) {
exception = ex;
}
@@ -79,8 +79,8 @@ static void ConfigureServices(IServiceCollection services, IConfiguration config
.AddSparkApi(config.GetSection("SparkApi"))
.AddMasterWorker(config.GetSection("MasterWorker"))
.AddViewStreamDatabase()
.AddPrimitiveDataStreamFactory(config.GetSection("PrimitiveStreams"))
.AddOhlcvStreams(config.GetSection("StreamDatabase"))
.AddPrimitiveStreams(config.GetSection("PrimitiveStreams"))
.AddMarketDataStreams()
.AddNaturalLanguageStreams(config.GetSection("NaturalLanguageStream"))
.AddCompositeDataStreamFactory()
.AddPubSubClient(config.GetSection("PubSub"));
@@ -116,7 +116,7 @@
},
{
"natural_language": "Get bbo for 10 year T-Notes in the first minute after the market opened yesterday.",
"dsl": "Bbo(symbol: \"ES\", venue: \"CME\", begin: \"2024-10-10T17:00:00-06:00\", end: \"2024-10-10T17:01:00-06:00\")"
"dsl": "Bbo(symbol: \"ES\", venue: \"CME\", begin: \"2024-10-10T17:00:00-06:00\", end: \"2024-10-10T17:01:00-06:00\", exclusive_end: true)"
},
{
"natural_language": "Retrieve NVIDIA L1 books from Oct 3",
@@ -147,10 +147,10 @@
"interval (sample frequency of the data), ",
"levels (the number of price levels to examine)",
"begin and end (inclusive beginning and end of the time window to query, respectively), ",
"inclusive_end (a bool flag that, when true, makes the end time inclusive). ",
"Arguments are specified by name, for example: OrderImbalance(symbol: \"ZN\", venue: \"CME\", interval: \"PT1M\", begin: \"2024-10-10\", end: \"2024-10-11\"). ",
"Depending on the wording of the request, some arguments may be omitted. ",
"The begin, end, and interval args should be expressed in iso 8601 format. ",
"If the prompt doesn't contain an explicit interval, please choose a reasonable value depending on the duration between begin and end.",
"Interpret dates according to the trading schedule rather than calendar date whenever possible. ",
"In some prompts, the time window will be expressed in relative terms (e.g., \"last Monday\", or \"yesterday\") ",
"which must be translated to exact dates. To correctly interpret the dsl in the examples, assume the current time is 2024-10-11T12:00-05:00."
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
</ItemGroup>
</Project>
@@ -3,15 +3,12 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System.Collections.Generic;
using System.Diagnostics;
public static class HostBuilderExtensions {
public static IHostBuilder CreateBtsBuilder(string[] args) {
// Obtain calling assembly name here, rather than in the lambda where it is actually needed,
// because want the name of the assembly that called this function, not the assembly that will eventually invoke
// the lambda...that will be the Microsoft.Extensions.Hosting assembly.
// Get the name of the assembly that called this function. That will act as the "appsettings"
// json file prefix
// The calling assembly's name serves as the "appsettings" json file prefix.
var exeName = GetCallingAssemblyName();
return Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, configBuilder) =>
@@ -56,4 +53,21 @@ public static class HostBuilderExtensions {
?.GetName()
?.Name
?? throw new InvalidOperationException("Failed to determine name of calling assembly (calling method is null)");
/// <summary>
/// Separates command line args from config args, using '--' as the delimiter. Args
/// before the delimiter are considered command line args, while those after it are
/// config args.
/// </summary>
public static (string[] Args, string[] ConfigArgs) SplitConfigArgs(string[] args) {
int splitIdx = Array.IndexOf(args, "--");
if (splitIdx == -1) {
return (args, Array.Empty<string>());
}
var cfgArgs = new string[args.Length - splitIdx - 1];
Array.Copy(args, splitIdx + 1, cfgArgs, 0, cfgArgs.Length);
var nonCfgArgs = new string[args.Length - cfgArgs.Length - 1];
Array.Copy(args, 0, nonCfgArgs, 0, nonCfgArgs.Length);
return (nonCfgArgs, cfgArgs);
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -0,0 +1,31 @@
namespace bts.BlueOrb;
using BCG;
public class AddColumnRow : IBlueRow {
readonly IBlueRow inputRow;
readonly IBlueTableSchema outputSchema;
readonly object? value;
readonly int valueIndex;
public AddColumnRow(IBlueRow inputRow, IBlueTableSchema outputSchema, object? value, int valueIndex) {
this.inputRow = inputRow;
this.outputSchema = outputSchema;
this.value = value;
this.valueIndex = valueIndex;
}
public object? this[int index] {
get {
if (index == valueIndex) {
return value;
}
if (index >= valueIndex) {
index--;
}
return inputRow[index];
}
}
IBlueTableSchema IBlueRow.schema => outputSchema;
}
@@ -0,0 +1,62 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
public class AddColumnTable : IBlueRowCollection {
readonly IBlueRowCollection inputRows;
readonly Func<IBlueRow, object?> getValue;
readonly IBlueTableSchema outputSchema;
readonly int valueIndex;
public AddColumnTable(IBlueRowCollection inputRows, string columnName, Type columnType, Func<IBlueRow, object?> getValue) {
this.inputRows = inputRows;
this.getValue = getValue;
var inputCols = inputRows.schema.columnInfo;
var outputCols = new ColumnInfo[inputCols.Length + 1];
Array.Copy(inputCols, outputCols, inputCols.Length);
outputCols[^1] = new(columnName, inputCols.Length, columnType);
outputSchema = new BlueTableSchema(outputCols, "AddColumnTable");
valueIndex = inputCols.Length;
}
public IBlueRow this[int id] {
get {
var r = inputRows[id];
return new AddColumnRow(r, outputSchema, getValue(r), valueIndex);
}
}
public IBlueTableSchema schema => outputSchema;
public int Count => inputRows.Count;
public bool containsID(int id) =>
inputRows.containsID(id);
public IEnumerator<IBlueRow> GetEnumerator() {
foreach (var r in inputRows) {
yield return new AddColumnRow(r, outputSchema, getValue(r), valueIndex);
}
}
public IBlueRow GetRow(int index) {
var r = inputRows.GetRow(index);
return new AddColumnRow(r, outputSchema, getValue(r), valueIndex);
}
public int IndexOf(IBlueRow item) {
int id = item.Id();
if (!inputRows.containsID(id)) {
return -1;
}
var r = inputRows[id];
return BlueRowEqualityComparer.Equals(new AddColumnRow(r, outputSchema, getValue(r), valueIndex), item)
? inputRows.IndexOf(r)
: -1;
}
}
@@ -0,0 +1,11 @@
namespace bts.BlueOrb {
using BCG;
[DataStreamUserRespresentation("AddColumnsStream")]
public class AddColumnsStream: ViewStream {
public AddColumnsStream(ViewStream parent_str, string add_col_expr) {
ViewStream rs = new BCG.AddColumnsStream(parent_str, add_col_expr);
mySetRealSource(rs);
}
}
}
@@ -0,0 +1,93 @@
#nullable enable
namespace bts.BlueOrb;
using BCG;
using Bts.DataStream;
using Bts.DataStreamCore;
using Spark.Core.Disposables;
using System;
public class ApplyStream : ViewStream {
readonly ViewStream source;
readonly Func<IBlueRow, object?> apply;
IBlueTableSchema? outputSchema;
BlueTableImmutable? lastFired;
IDisposable? sub;
public ApplyStream(ViewStream source, IFunctionEvaluator applyEvaluator) {
this.source = source;
apply = Apply;
object? Apply(IBlueRow r) {
var res = applyEvaluator.Evaluate(string.Empty, new StreamArgument[] { (null, 0, r) });
return res;
}
}
public ApplyStream(ViewStream source, Func<IBlueRow, object?> apply) {
this.source = source;
this.apply = apply;
}
protected override void myOnFirstSubscriber() {
sub = source.Subscribe(OnSourceChanged);
void OnSourceChanged(IDataPacket dp) {
Lazy<BlueTableImmutable> lastFired = new(() => this.lastFired ??= new(Log.notnull(outputSchema)));
Lazy<BlueTableImmutable.DataPacketBuilder> bd = new(() => lastFired.Value.ToDataPacketBuilder());
foreach (var r in dp.deletedRows) {
var res = apply(r);
if (res is IBlueRow blueRow) {
bd.Value.Remove(blueRow.Id());
} else if (res is IBlueRowCollection blueRows) {
foreach (var br in blueRows) {
bd.Value.Remove(br.Id());
}
} else {
Log.assert(false, $"apply delegate returned obj of unexpected type '{res?.GetType()}'");
}
}
foreach (var r in dp.addedRows) {
var res = apply(r);
if (res is IBlueRow blueRow) {
outputSchema ??= blueRow.schema;
bd.Value.AddOrUpdate(blueRow);
} else if (res is IBlueRowCollection blueRows) {
outputSchema ??= blueRows.schema;
foreach (var br in blueRows) {
bd.Value.AddOrUpdate(br);
}
} else {
if (outputSchema is null) {
// We get the outputSchema from the result of the first apply, if it's null, we
// cannot determine the output data type.
// TODO: maybe support specifying return type in DSL?
Log.notnull(res);
}
var resType = res?.GetType();
if (outputSchema is null) {
Log.notnull(resType);
Log.assert(ColumnInfo.isAllowedType(resType));
outputSchema = new BlueTableSchema(new ColumnInfo[] {
new("id", 0, typeof(int)),
new("result", 1, resType),
}, dp.newRows.schema.tableName + "_Apply");
}
bd.Value.AddOrUpdate(new BlueRow(outputSchema, new object?[] { r.Id(), res }));
// Log.assert(false, $"apply delegate returned obj of unexpected type '{res?.GetType()}'");
}
}
if (bd.IsValueCreated) {
myFireEvent(bd.Value.ToDataPacket(out this.lastFired));
}
}
}
protected override void myOnLastSubscriber() {
Disposable.dispose(ref sub);
}
}
@@ -0,0 +1,62 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
public class ApplyTable : IBlueRowCollection {
readonly IBlueRowCollection source;
readonly IBlueTableSchema outputSchema;
readonly List<IBlueRow> outputRows;
readonly Dictionary<int, int> idToIndex = new();
readonly Func<IBlueRow, IBlueRow> applyFn;
public ApplyTable(IBlueRowCollection source, IBlueTableSchema? outputSchema, Func<IBlueRow, IBlueRow> applyFn) {
this.source = source;
this.applyFn = applyFn;
outputRows = new(source.Count);
foreach (var r in source) {
outputRows.Add(applyFn(r));
}
if (outputRows.Count > 0) {
outputSchema ??= outputRows[0].schema;
}
this.outputSchema = outputSchema ?? source.schema;
}
public IBlueRow this[int id] => outputRows[idToIndex[id]];
IBlueRow IList<IBlueRow>.this[int index] {
get => GetRow(index);
set => throw new NotSupportedException();
}
public IBlueTableSchema schema => outputSchema;
public int Count => source.Count;
public bool containsID(int id) =>
source.containsID(id);
public IEnumerator<IBlueRow> GetEnumerator() =>
outputRows.GetEnumerator();
public IBlueRow GetRow(int index) =>
outputRows[index];
public int IndexOf(IBlueRow item) {
int id = item.Id();
if (!source.containsID(id)) {
return -1;
}
var inputRow = source[id];
int idx = source.IndexOf(inputRow);
if (BlueRowEqualityComparer.Equals(outputRows[idx], item)) {
return idx;
}
return -1;
}
}
@@ -0,0 +1,61 @@
namespace Bts.DataStream.BlueOrb;
using BCG;
using Bts.DataStream;
using Bts.DataStreamCore;
using Bts.PubSub;
using Spark.Core.Disposables;
using System;
using System.Threading;
using System.Threading.Tasks;
public static class AsyncViewStream {
public static ViewStream Create(IWorkScheduler workScheduler, Func<Task<ViewStream?>> factory) {
return ViewStreamFactory.Create(Subscribe);
IDisposable Subscribe(Action<IDataPacket> fire) {
var cts = new CancellationTokenSource();
var tcs = new TaskCompletionSource<IDisposable>();
var syncRoot = new object();
IDisposable? streamSub = null;
workScheduler.Post(Init);
return Disposable.Create(Dispose);
async void Init() {
lock (syncRoot) {
if (cts.IsCancellationRequested) {
return;
}
}
var stream = await factory();
if (stream is null) {
return;
}
var tempStreamSub = stream.Subscribe(fire);
lock (syncRoot) {
if (cts.IsCancellationRequested) {
tempStreamSub.Dispose();
} else {
streamSub = tempStreamSub;
}
}
}
void Dispose() {
IDisposable? tempStreamSub;
lock (syncRoot) {
tempStreamSub = streamSub;
if (tempStreamSub is null) {
cts.Cancel();
return;
}
streamSub = null;
}
tempStreamSub?.Dispose();
}
}
}
}
@@ -0,0 +1,115 @@
namespace bts.BlueOrb;
using BCG;
using bts.collections;
using bts.utils;
using Bts.DataStream;
using Bts.DataStreamCore;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
public class BlueAddIndexColumnTable : IBlueRowCollection {
readonly IBlueTableSchema schema;
readonly IList<object?>[] columnValues;
readonly int count;
BlueAddIndexColumnTable(IBlueTableSchema schema, IList<object?>[] columnValues, int? count = null) {
this.schema = schema;
this.columnValues = columnValues;
this.count = count ?? columnValues[0].Count;
}
IBlueTableSchema IBlueRowCollection.schema => schema;
public int Count => count;
public IBlueRow this[int id] =>
id > 0 && id <= count
? GetRow(id - 1)
: throw new KeyNotFoundException();
public static BlueAddIndexColumnTable Create(string tableName, ColumnInfo[] columnInfos, IList<object?>[] columnValues) {
if (columnInfos.Any(c => c.colName == "id")) {
throw new ArgumentException("'id' column already exists", nameof(columnInfos));
}
if (columnInfos.Length != columnValues.Length) {
throw new ArgumentException("columnValues length differs from columnInfos length", nameof(columnValues));
}
int count = columnValues.FirstOrDefault()?.Count ?? 0;
var origCols = columnInfos;
var origColValues = columnValues;
columnInfos = new ColumnInfo[origCols.Length + 1];
columnInfos[0] = new ColumnInfo("id", 0, typeof(int));
columnValues = new IList<object?>[origCols.Length + 1];
columnValues[0] = new StepList<int>(1, 1, count).WrapSelector(id => (object?)id, null);
for (int i = 1; i < columnInfos.Length; i++) {
columnInfos[i] = new ColumnInfo(origCols[i - 1].colName, i, origCols[i - 1].colType);
columnValues[i] = origColValues[i - 1];
}
var schema = new BlueTableSchema(columnInfos, tableName);
return new(schema, columnValues, count);
}
public static BlueAddIndexColumnTable Create(IBlueTableSchema schema, IList<object?>[] columnValues) {
int count = columnValues.FirstOrDefault()?.Count ?? 0;
int? idColIdx = schema.TryResolveIndices("id");
var idColValues = new StepList<int>(1, 1, count).WrapSelector(id => (object?)id, null);
var columnInfos = schema.columnInfo;
if (idColIdx.HasValue) {
// Replace existing id column. columnValues may or may not contain the old ids (if its
// length is one less column than that of columnInfos, it doesn't contain them).
switch (columnValues.Length - schema.columnInfo.Length) {
case 0:
columnValues[idColIdx.Value] = idColValues;
break;
case -1:
columnValues = ArrayUtils.InsertExact(columnValues, idColIdx.Value, true, idColValues);
break;
default:
throw new ArgumentException("ColumnInfos length disagrees with columnValues length");
}
} else {
if (columnValues.Length != columnInfos.Length) {
throw new ArgumentException("ColumnInfos length disagrees with columnValues length");
}
var origColInfos = columnInfos;
columnInfos = new ColumnInfo[columnInfos.Length + 1];
columnInfos[0] = new("id", 0, typeof(int));
for (int i = 1; i < columnInfos.Length; i++) {
var c = origColInfos[i - 1];
columnInfos[i] = new(c.colName, c.colNo + 1, c.colType);
}
columnValues = ArrayUtils.InsertExact(columnValues, 0, true, idColValues);
}
return new(new BlueTableSchema(schema.tableName, columnInfos), columnValues);
}
public bool containsID(int id) =>
id > 0 && id <= count;
public IList<object?> GetColumn(int index) =>
columnValues[index];
public IBlueRow GetRow(int index) =>
new BlueRowColumnWrapper(schema, index, columnValues);
public int IndexOf(IBlueRow item) {
int id = item.Id();
return id > 0 && id <= count && BlueRowEqualityComparer.Equals(GetRow(id - 1), item)
? id - 1
: -1;
}
public IEnumerator<IBlueRow> GetEnumerator() {
int count = Count;
for (int i = 0; i < count; i++) {
yield return GetRow(i);
}
}
}
@@ -0,0 +1,21 @@
namespace bts.BlueOrb;
using BCG;
using System.Collections.Generic;
public class BlueRowColumnWrapper : IBlueRow {
readonly int index;
readonly IList<object?>[] columns;
public BlueRowColumnWrapper(IBlueTableSchema schema, int index, IList<object?>[] columns) {
this.schema = schema;
this.index = index;
this.columns = columns;
}
public object? this[int index] =>
columns[index][this.index];
public IBlueTableSchema schema { get; }
}
@@ -0,0 +1,52 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using mbts.utils;
using System.Collections.Generic;
using System.Linq;
public class BlueIndexedColumnTable : IBlueRowCollection {
readonly IBlueTableSchema schema;
readonly IList<object?>[] columnValues;
readonly Dictionary<int, int> rowIdToIndex;
public BlueIndexedColumnTable(IBlueTableSchema schema, IList<object?>[] columnValues) {
this.schema = schema;
this.columnValues = columnValues;
int idColumnIndex = schema.ResolveIndices("id");
rowIdToIndex = columnValues[idColumnIndex]
.Select((idObj, idx) => new KeyValuePair<int, int>(ExceptionAssert.Argument.Is<int>(idObj), idx))
.ToDictionary();
}
public IBlueRow this[int id] =>
rowIdToIndex.TryGetValue(id, out var idx)
? GetRow(idx)
: throw new KeyNotFoundException();
IBlueTableSchema IBlueRowCollection.schema => schema;
public int Count => rowIdToIndex.Count;
public bool containsID(int id) =>
rowIdToIndex.ContainsKey(id);
public IEnumerator<IBlueRow> GetEnumerator() {
int count = Count;
for (int i = 0; i < count; i++) {
yield return GetRow(i);
}
}
public IBlueRow GetRow(int index) =>
new BlueRowColumnWrapper(schema, index, columnValues);
public int IndexOf(IBlueRow item) =>
rowIdToIndex.TryGetValue(item.Id(), out var idx) && BlueRowEqualityComparer.Equals(GetRow(idx), item)
? idx
: -1;
}
@@ -0,0 +1,18 @@
namespace bts.BlueOrb {
using System;
using BCG;
[DataStreamUserRespresentation("CSVReaderStream")]
public class CSVReaderStream : ViewStream {
public CSVReaderStream(string file_name)
: this(file_name, false) { }
public CSVReaderStream(string file_name, bool guess_schema) {
ViewStream rs = new BCG.CSVReaderStream(file_name);
if (guess_schema) {
rs = new SchemaGuesserStream(rs);
}
mySetRealSource(rs);
}
}
}
@@ -0,0 +1,13 @@
namespace bts.BlueOrb {
using BCG;
using System;
[DataStreamUserRespresentation("CSVSaverStream")]
public class CSVSaverStream: ViewStream {
public CSVSaverStream(ViewStream parent_str, string file_name) {
BCG.CSVSaverStream rs = new BCG.CSVSaverStream(parent_str, file_name, true, Int32.MaxValue, false);
mySetRealSource(rs);
}
}
}
@@ -0,0 +1,43 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStream;
using Spark.Core.Disposables;
using System;
public class CastStream : ViewStream, IDisposable {
readonly ViewStream inputStream;
readonly (string, Type)[] casts;
IDisposable? sub;
public CastStream(ViewStream inputStream, params (string, Type)[] casts) {
this.inputStream = inputStream;
this.casts = casts;
}
~CastStream() =>
Dispose();
public void Dispose() {
GC.SuppressFinalize(this);
Disposable.dispose(ref sub);
}
protected override void myOnFirstSubscriber() {
inputStream.Subscribe(OnInputChanged);
void OnInputChanged(IDataPacket dp) {
var dpOut = new DataPacket(
new CastTable(dp.newRows, casts),
new CastTable(dp.addedRows, casts),
new CastTable(dp.deletedRows, casts));
myFireEvent(dpOut);
}
}
protected override void myOnLastSubscriber() =>
Disposable.dispose(ref sub);
}
@@ -0,0 +1,123 @@
namespace bts.BlueOrb;
using BCG;
using bts.collections;
using Bts.DataStream;
using Bts.DataStreamCore;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
public class CastTable : IBlueRowCollection {
readonly IBlueRowCollection rows;
readonly IBlueTableSchema outputSchema;
readonly (int Index, Func<IBlueRow, object?> GetValue)[] casts;
readonly IComparer<(int Index, Func<IBlueRow, object?> GetValue)> castComparer =
TupleComparer.Comparer<int, Func<IBlueRow, object?>>(item1Ordinal: 0);
public CastTable(IBlueRowCollection rows, params (string, Type)[] casts) {
this.rows = rows;
var inputSchema = rows.schema;
var inputCols = inputSchema.columnInfo;
var castIndices = inputSchema.ResolveIndices(Array.ConvertAll(casts, c => c.Item1));
this.casts = new (int Index, Func<IBlueRow, object?> GetValue)[casts.Length];
var outputCols = inputCols.ToArray();
for (int i = 0; i < casts.Length; i++) {
var colIdx = castIndices[i];
this.casts[i] = (colIdx, GetCaster(casts[i].Item2));
var inputCol = inputCols[colIdx];
Log.assert(inputCol.colNo == colIdx);
outputCols[colIdx] = new(inputCol.colName, inputCol.colNo, casts[i].Item2);
Func<IBlueRow, object?> GetCaster(Type to) {
if (to == typeof(short)) {
return r => r.GetInt16(colIdx);
}
if (to == typeof(int)) {
return r => r.GetInt32(colIdx);
}
if (to == typeof(long)) {
return r => r.GetInt64(colIdx);
}
if (to == typeof(ushort)) {
return r => r.GetUInt16(colIdx);
}
if (to == typeof(uint)) {
return r => r.GetUInt32(colIdx);
}
if (to == typeof(ulong)) {
return r => r.GetUInt64(colIdx);
}
if (to == typeof(double)) {
return r => r.GetFloat64(colIdx);
}
if (to == typeof(float)) {
return r => r.GetFloat32(colIdx);
}
if (to == typeof(DateTime)) {
return r => r.GetDateTime(colIdx);
}
if (to == typeof(TimeSpan)) {
return r => r.GetTimeSpan(colIdx);
}
throw new NotImplementedException($"CastTable cast not implemented for (out) type {to}");
}
}
outputSchema = new BlueTableSchema(inputSchema.tableName + "_Cast", outputCols);
}
public IBlueRow this[int id] =>
new Row(rows[id], outputSchema, casts, castComparer);
public IBlueTableSchema schema => outputSchema;
public int Count => rows.Count;
public bool containsID(int id) =>
rows.containsID(id);
public IEnumerator<IBlueRow> GetEnumerator() {
foreach (var r in rows) {
yield return new Row(r, outputSchema, casts, castComparer);
}
}
public IBlueRow GetRow(int index) =>
new Row(rows.GetRow(index), outputSchema, casts, castComparer);
public int IndexOf(IBlueRow item) {
var inputRow = rows[item.Id()];
return BlueRowEqualityComparer.Equals(item, new Row(inputRow, outputSchema, casts, castComparer))
? rows.IndexOf(inputRow)
: -1;
}
class Row : IBlueRow {
readonly IBlueRow source;
readonly (int Index, Func<IBlueRow, object?> GetValue)[] casts;
readonly IComparer<(int Index, Func<IBlueRow, object?> GetValue)> castComparer;
readonly IBlueTableSchema outputSchema;
public Row(IBlueRow source, IBlueTableSchema outputSchema, (int Index, Func<IBlueRow, object?> GetValue)[] casts, IComparer<(int Index, Func<IBlueRow, object?> GetValue)> castComparer) {
this.source = source;
this.casts = casts;
this.castComparer = castComparer;
this.outputSchema = outputSchema;
}
public object? this[int index] {
get {
var castIdx = casts.BinarySearch((index, null!), castComparer);
return castIdx > -1
? casts[castIdx].GetValue(source)
: source[index];
}
}
IBlueTableSchema IBlueRow.schema => outputSchema;
}
}
@@ -0,0 +1,22 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStream;
using System.Reactive.Linq;
public class ColumnInjectionStream : ViewStream {
public ColumnInjectionStream(ViewStream inputStream, IInjectedColumn[] outputColumns) {
IBlueTableSchema? outputSchema = null;
mySetRealSource(inputStream.AsObservable().Select(Transform).AsViewStream());
IDataPacket Transform(IDataPacket dp) {
outputSchema ??= ColumnInjectionTable.GetOutputSchema(dp.Schema, outputColumns);
var newRows = new ColumnInjectionTable(dp.newRows, outputSchema, outputColumns);
var addedRows = new ColumnInjectionTable(dp.addedRows, outputSchema, outputColumns);
var deletedRows = new ColumnInjectionTable(dp.deletedRows, outputSchema, outputColumns);
return new DataPacket(newRows, addedRows, deletedRows);
}
}
}
@@ -0,0 +1,156 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
public interface IInjectedColumn {
string Name { get; }
Type Type { get; }
int Ordinal { get; }
ColumnInfo ColumnInfo => new(Name, Ordinal, Type);
object? GetValue(IBlueRow row);
}
public interface IInjectedColumn<T> : IInjectedColumn {
Type IInjectedColumn.Type => typeof(T);
new T? GetValue(IBlueRow row);
object? IInjectedColumn.GetValue(IBlueRow row) =>
GetValue(row);
}
public class InjectedColumn : IInjectedColumn {
readonly Func<IBlueRow, object?> getValue;
public InjectedColumn(string name, int index, Type type, Func<IBlueRow, object?> getValue) {
Name = name;
Ordinal = index;
Type = type;
this.getValue = getValue;
}
public string Name { get; }
public int Ordinal { get; }
public Type Type { get; }
public object? GetValue(IBlueRow row) =>
getValue(row);
}
public class InjectedColumn<T> : IInjectedColumn<T> {
readonly Func<IBlueRow, T?> getValue;
public InjectedColumn(string name, int index, Func<IBlueRow, T?> getValue) {
Name = name;
Ordinal = index;
this.getValue = getValue;
}
public string Name { get; }
public int Ordinal { get; }
public T? GetValue(IBlueRow row) =>
getValue(row);
}
public class ColumnInjectionTable : IBlueRowCollection {
readonly IBlueTableSchema outputSchema;
readonly IInjectedColumn[] outputColumns;
readonly IBlueRowCollection source;
public ColumnInjectionTable(IBlueRowCollection source, IBlueTableSchema outputSchema, IInjectedColumn[] outputColumns) {
this.source = source;
this.outputSchema = outputSchema;
this.outputColumns = outputColumns;
}
public static ColumnInjectionTable Create(IBlueRowCollection source, IEnumerable<IInjectedColumn> injectedColumns) {
var inputCols = source.schema.columnInfo;
var outputColumns = new List<IInjectedColumn>(inputCols.Length + 1);
var outputColInfos = new List<ColumnInfo>(inputCols.Length + 1);
int injectionIdx = 0;
using var en = injectedColumns.GetEnumerator();
bool hasMoreInjections = en.MoveNext();
for (int i = 0; i < inputCols.Length;) {
if (hasMoreInjections && en.Current.Ordinal <= i) {
outputColInfos.Add(new(en.Current.Name, outputColumns.Count, en.Current.Type));
outputColumns.Add(en.Current);
injectionIdx++;
continue;
}
var ci = inputCols[i];
outputColInfos.Add(new(ci.colName, outputColumns.Count, ci.colType));
outputColumns.Add(new InjectedColumn(ci.colName, outputColumns.Count, ci.colType, r => r[ci.colNo]));
}
var outputSchema = new BlueTableSchema(outputColInfos.ToArray(), $"ColumnInjection({source.schema.tableName})");
return new(source, outputSchema, outputColumns.ToArray());
}
public static IBlueTableSchema GetOutputSchema(IBlueTableSchema inputSchema, IEnumerable<IInjectedColumn> injectedColumns) {
var inputCols = inputSchema.columnInfo;
var outputColumns = new List<IInjectedColumn>(inputCols.Length + 1);
var outputColInfos = new List<ColumnInfo>(inputCols.Length + 1);
using var en = injectedColumns.GetEnumerator();
bool hasMoreInjections = en.MoveNext();
for (int i = 0; i < inputCols.Length;) {
if (hasMoreInjections && en.Current.Ordinal <= i) {
outputColInfos.Add(new(en.Current.Name, outputColumns.Count, en.Current.Type));
outputColumns.Add(en.Current);
hasMoreInjections = en.MoveNext();
continue;
}
var ci = inputCols[i];
outputColInfos.Add(new(ci.colName, outputColumns.Count, ci.colType));
outputColumns.Add(new InjectedColumn(ci.colName, outputColumns.Count, ci.colType, r => r[ci.colNo]));
i++;
}
return new BlueTableSchema(outputColInfos.ToArray(), $"ColumnInjection({inputSchema.tableName})");
}
public IBlueRow this[int id] =>
new ColumnInjectionRow(source[id], outputSchema, outputColumns);
public IBlueTableSchema schema => throw new System.NotImplementedException();
public int Count => source.Count;
public bool containsID(int id) =>
source.containsID(id);
public IEnumerator<IBlueRow> GetEnumerator() {
foreach (var r in source) {
yield return new ColumnInjectionRow(r, outputSchema, outputColumns);
}
}
public IBlueRow GetRow(int index) =>
new ColumnInjectionRow(source.GetRow(index), outputSchema, outputColumns);
public int IndexOf(IBlueRow item) {
int id = item.Id();
return source.containsID(id) && BlueRowEqualityComparer.Equals(item, this[id])
? source.IndexOf(source[id])
: -1;
}
class ColumnInjectionRow : IBlueRow {
readonly IBlueRow row;
readonly IInjectedColumn[] columns;
public ColumnInjectionRow(IBlueRow row, IBlueTableSchema schema, IInjectedColumn[] columns) {
this.row = row;
this.schema = schema;
this.columns = columns;
}
public IBlueTableSchema schema { get; }
public object? this[int index] =>
columns[index].GetValue(row);
}
}
@@ -0,0 +1,19 @@
namespace bts.BlueOrb;
using BCG;
public class ColumnMaskRow : IBlueRow {
readonly IBlueRow inputRow;
readonly (string? Name, int Index)[] columnMap;
public ColumnMaskRow(IBlueRow inputRow, (string? Name, int Index)[] columnMap, IBlueTableSchema schema) {
this.inputRow = inputRow;
this.columnMap = columnMap;
this.schema = schema;
}
public IBlueTableSchema schema { get; }
public object? this[int index] =>
inputRow[columnMap[index].Index];
}
@@ -0,0 +1,68 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStream;
using Spark.Core.Disposables;
using System;
using System.Diagnostics.CodeAnalysis;
public class ColumnMaskStream : ViewStream {
readonly ViewStream source;
readonly string? columnMaskExpression;
IDisposable? sub;
IBlueTableSchema? outputSchema;
// Each item in this array represents an output column as a tuple of
// new name, and corresponding index in the input schema.
(string? OutputName, int InputIndex)[]? columnMappings;
(string From, string To)[]? columnNameMappings;
readonly bool includeUnmappedColumns;
public ColumnMaskStream(ViewStream source, string columnMaskExpression) {
this.source = source;
this.columnMaskExpression = columnMaskExpression;
}
public ColumnMaskStream(ViewStream source, bool includeUnmappedColumns, params (string From, string To)[] columnNameMappings) {
this.source = source;
this.columnNameMappings = columnNameMappings;
this.includeUnmappedColumns = includeUnmappedColumns;
}
~ColumnMaskStream() =>
Disposable.dispose(ref sub);
protected override void myOnFirstSubscriber() {
Log.isnull(sub);
sub = source.Subscribe(OnSourceChanged);
void OnSourceChanged(IDataPacket dp) {
ensureOutputSchema(dp.Schema);
var addedRows = new ColumnMaskTable(dp.addedRows, columnMappings, outputSchema);
var deletedRows = new ColumnMaskTable(dp.deletedRows, columnMappings, outputSchema);
var allRows = new ColumnMaskTable(dp.newRows, columnMappings, outputSchema);
myFireEvent(new DataPacket(allRows, addedRows, deletedRows));
}
}
protected override void myOnLastSubscriber() =>
Disposable.dispose(ref sub);
[MemberNotNull(nameof(outputSchema), nameof(columnMappings))]
void ensureOutputSchema(IBlueTableSchema inputSchema) {
if (outputSchema is not null) {
Log.notnull(columnMappings);
return;
}
if (columnMaskExpression is not null) {
outputSchema = ColumnMaskTable.GetOutputSchema(inputSchema, columnMaskExpression, out columnMappings);
} else if (columnNameMappings is not null) {
outputSchema = ColumnMaskTable.GetOutputSchema(inputSchema, includeUnmappedColumns, columnNameMappings, out columnMappings);
} else {
throw new InvalidOperationException("Cannot build output schema");
}
}
}
@@ -0,0 +1,120 @@
namespace bts.BlueOrb;
using BCG;
using bts.collections;
using bts.utils;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
using System.Linq;
public class ColumnMaskTable : IBlueRowCollection {
readonly IBlueRowCollection inputRows;
readonly (string? Name, int Index)[] columnMap;
public ColumnMaskTable(IBlueRowCollection inputRows, (string? Name, int Index)[] columnMap, IBlueTableSchema schema) {
this.inputRows = inputRows;
this.columnMap = columnMap;
this.schema = schema;
}
public IBlueTableSchema schema { get; }
public int Count => inputRows.Count;
public IBlueRow this[int id] =>
new ColumnMaskRow(inputRows[id], columnMap, schema);
public static ColumnMaskTable Create(IBlueRowCollection table, string columnMaskExpression) {
var outputSchema = GetOutputSchema(table.schema, columnMaskExpression, out var columnMappings);
return new ColumnMaskTable(table, columnMappings, outputSchema);
}
public static IBlueTableSchema GetOutputSchema(IBlueTableSchema inputSchema, string columnMaskExpression, out (string? Name, int Index)[] columnMappings) {
bool includeOthers;
if (columnMaskExpression.StartsWith('!')) {
columnMaskExpression = columnMaskExpression[1..].TrimStart();
includeOthers = false;
} else {
includeOthers = true;
}
var map = columnMaskExpression.Split(' ').Select(MapColumn).ToOrderedDictionary(tup => tup.Index, tup => tup.Name);
var inputCols = inputSchema.columnInfo;
if (includeOthers) {
var implicitCols = inputCols.Where(c => !map.ContainsKey(c.colNo)).ToList();
map.AddRange(implicitCols.Select(c => new KeyValuePair<int, string?>(c.colNo, null)));
} else if (!map.Any(c => inputCols[c.Key].colName == "id") && inputCols.TryGetFirst(c => c.colName == "id", out var idColInfo)) {
// Add 'id' col if missing
map.Insert(0, new(idColInfo.colNo, "id"));
}
var outputCols = new ColumnInfo[map.Count];
for (int i = 0; i < outputCols.Length; i++) {
var inputColInfo = inputCols[map.Keys[i]];
outputCols[i] = new(map.Values[i] ?? inputColInfo.colName, i, inputColInfo.colType);
}
columnMappings = map.WrapSelector(kv => (kv.Value, kv.Key), null).ToArray();
return new BlueTableSchema(outputCols, inputSchema.tableName + "_ColumnMask");
(string? Name, int Index) MapColumn(string columnStr) {
var parts = columnStr.Split(':');
return parts.Length switch {
1 => ((string?)null, inputSchema[parts[0]].colNo),
2 => (parts[1], inputSchema[parts[0]].colNo),
_ => throw new InvalidOperationException("Unable to get output schema due to invalid column expression"),
};
}
}
public static IBlueTableSchema GetOutputSchema(IBlueTableSchema inputSchema, bool includeUnmapped, (string From, string To)[] columnNameMappings, out (string? OutputName, int InputIndex)[] columnMappings) {
var inputCols = inputSchema.columnInfo;
ColumnInfo[]? outputCols;
if (includeUnmapped) {
columnMappings = Array.ConvertAll(inputCols, c => ((string?)c.colName, c.colNo));
outputCols = ArrayUtils.Clone(inputCols);
foreach (var (from, to) in columnNameMappings) {
var idx = inputCols.ResolveIndices(from);
var c = outputCols[idx];
Log.assert(c.colNo == idx);
outputCols[idx] = new(to, c.colNo, c.colType);
columnMappings[idx] = (to, idx);
}
} else {
outputCols = new ColumnInfo[columnNameMappings.Length];
columnMappings = Array.ConvertAll(columnNameMappings, m => ((string?)m.To, inputCols.ResolveIndices(m.From)));
Array.Copy(inputCols, outputCols, inputCols.Length);
for (int i = 0; i < columnNameMappings.Length; i++) {
var (from, to) = columnNameMappings[i];
var idx = inputCols.ResolveIndices(from);
outputCols[idx] = new(to, i, inputCols[columnMappings[i].InputIndex].colType);
}
}
return new BlueTableSchema(outputCols, inputSchema.tableName + "_ColumnMask");
}
public bool containsID(int id) =>
inputRows.containsID(id);
public IEnumerator<IBlueRow> GetEnumerator() {
foreach (var r in inputRows) {
yield return new ColumnMaskRow(r, columnMap, schema);
}
}
public IBlueRow GetRow(int index) =>
new ColumnMaskRow(inputRows.GetRow(index), columnMap, schema);
public int IndexOf(IBlueRow item) {
int id = item.Id();
if (inputRows.containsID(id)) {
var inputRow = inputRows[id];
var outputRow = new ColumnMaskRow(inputRow, columnMap, schema);
if (BlueRowEqualityComparer.Equals(item, outputRow)) {
return inputRows.IndexOf(inputRow);
}
}
return -1;
}
}
@@ -0,0 +1,61 @@
// namespace bts.BlueOrb;
// using BCG;
// using System;
// using System.Linq;
// /// <summary>
// /// Represents a row transformation table (all columns except 'id' can be changed).
// /// </summary>
// public class ColumnSelectorRow : IBlueRow {
// readonly IBlueRow source;
// readonly IBlueTableSchema outputSchema;
// readonly Func<object?, object?>[] columnSelectors;
// public ColumnSelectorRow(IBlueRow source, params (int Index, Type Type, Func<object?, object?> GetValue)[] columnSelectors) {
// this.source = source;
// this.columnSelectors = Array.ConvertAll(columnSelectors, c => (c.Index, c.GetValue));
// outputSchema = getSchema(source.schema, columnSelectors);
// }
// public ColumnSelectorRow(IBlueRow source, IBlueTableSchema outputSchema, params (int Index, Func<object?, object?> GetValue)[] columnSelectors) {
// this.source = source;
// this.outputSchema = outputSchema;
// this.columnSelectors = columnSelectors;
// }
// public object? this[int index] {
// get {
// for (int i = 0; i < columnSelectors.Length; i++) {
// if (columnSelectors[i].Index == index) {
// return columnSelectors[i].GetValue(source[index]);
// }
// }
// return source[index];
// }
// }
// public IBlueTableSchema schema => outputSchema;
// public static Func<object?, object?>[] BuildColumnSelectors(IBlueTableSchema schema, params (int Index, Func<object?, object?> GetValue)[] columnSelectors) {
// var cols = schema.columnInfo;
// var rv = Array.ConvertAll(schema.columnInfo, c => new Func<object?, object?>(_ => _));
// for (int i = 0; i < columnSelectors.Length; i++) {
// var cs = columnSelectors[i];
// rv[cs.Index] = cs.GetValue;
// }
// return rv;
// }
// static IBlueTableSchema getSchema(IBlueTableSchema inputSchema, (int Index, Type Type, Func<object?, object?> GetValue)[] columnSelectors) {
// var inputCols = inputSchema.columnInfo;
// var outputCols = inputCols.ToArray();
// for (int i = 0; i < columnSelectors.Length; i++) {
// var (idx, typ, _) = columnSelectors[i];
// var col = inputCols[idx];
// outputCols[idx] = new(col.colName, col.colNo, typ);
// }
// return new BlueTableSchema(outputCols, "ColumnSelector");
// }
// }
@@ -0,0 +1,107 @@
namespace bts.BlueOrb;
using BCG;
using bts.collections;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
using System.Linq;
public class ColumnSelectorRowFactory {
readonly IBlueTableSchema outputSchema;
readonly Func<object?, object?>[] columnSelectors;
public ColumnSelectorRowFactory(IBlueTableSchema outputSchema, Func<object?, object?>[] columnSelectors) {
this.outputSchema = outputSchema;
this.columnSelectors = columnSelectors;
}
public IBlueTableSchema OutputSchema => outputSchema;
public static ColumnSelectorRowFactory Create(IBlueTableSchema inputSchema, params (string Name, Type Type, Func<object?, object?> GetValue)[] columnOverrides) =>
Create(inputSchema, columnOverrides.AsEnumerable());
public static ColumnSelectorRowFactory Create(IBlueTableSchema inputSchema, params (int Index, Type Type, Func<object?, object?> GetValue)[] columnOverrides) =>
Create(inputSchema, columnOverrides.AsEnumerable());
public static ColumnSelectorRowFactory Create(IBlueTableSchema inputSchema, IEnumerable<(string Name, Type Type, Func<object?, object?> GetValue)> columnOverrides) =>
Create(inputSchema, columnOverrides.Select(c => (inputSchema.ResolveIndices(c.Name), c.Type, c.GetValue)));
public static ColumnSelectorRowFactory Create(IBlueTableSchema inputSchema, IEnumerable<(int Index, Type Type, Func<object?, object?> GetValue)> columnOverrides) {
var bd = new Builder(inputSchema);
foreach (var c in columnOverrides) {
bd.AddOrUpdate(c);
}
return bd.ToImmutable();
}
public IBlueRow Create(IBlueRow row) =>
new Row(row, outputSchema, columnSelectors);
public Builder ToBuilder() =>
new(outputSchema);
class Row : IBlueRow {
readonly IBlueRow source;
readonly IBlueTableSchema schema;
readonly Func<object?, object?>[] columnSelectors;
public Row(IBlueRow source, IBlueTableSchema schema, Func<object?, object?>[] columnSelectors) {
this.source = source;
this.schema = schema;
this.columnSelectors = columnSelectors;
}
public object? this[int index] =>
columnSelectors[index](source[index]);
IBlueTableSchema IBlueRow.schema => schema;
}
public class Builder {
readonly IBlueTableSchema inputSchema;
readonly List<(int Index, Type Type, Func<object?, object?> GetValue)> columnOverrides = new();
public Builder(IBlueTableSchema inputSchema) {
this.inputSchema = inputSchema;
}
public IBlueTableSchema ToSchema() =>
ToSchema(inputSchema.tableName + "_ColumnSelector");
public IBlueTableSchema ToSchema(string tableName) {
var inputCols = inputSchema.columnInfo;
var outputCols = inputCols.ToArray();
foreach (var c in columnOverrides) {
var inputCol = inputCols[c.Index];
outputCols[c.Index] = new(inputCol.colName, c.Index, c.Type);
}
return new BlueTableSchema(outputCols, tableName);
}
public ColumnSelectorRowFactory ToImmutable() {
var outputSchema = ToSchema();
var columnSelectors = Array.ConvertAll(outputSchema.columnInfo, c => new Func<object?, object?>(_ => _));
foreach (var cs in columnOverrides) {
columnSelectors[cs.Index] = cs.GetValue;
}
return new(outputSchema, columnSelectors);
}
public void AddOrUpdate((int Index, Type Type, Func<object?, object?> GetValue) item) =>
columnOverrides.Add(item);
public void AddOrUpdate(int index, Type type, Func<object?, object?> getValue) =>
columnOverrides.Add((index, type, getValue));
public void AddOrUpdate<T>(int index, Func<object?, T> getValue) {
columnOverrides.Add((index, typeof(T), GetValueWrapper));
object? GetValueWrapper(object? inputVal) =>
getValue(inputVal);
}
}
}
@@ -0,0 +1,46 @@
using System;
namespace BCG {
[DataStreamUserRespresentation("CountStream")]
public class CountStream : ViewStream {
private DataPacket myLastFiredDataPacket;
private IBlueTableSchema mySchema = new BlueTableSchema("count",
new ColumnInfo("id", 0, typeof(int)),
new ColumnInfo("count", 1, typeof(int)));
private int myId = 0;
private ViewStream myVS;
public CountStream(ViewStream vs) {
myVS = vs;
}
protected override void myOnFirstSubscriber() {
myVS.onChange += myHandleChanges;
}
protected override void myOnLastSubscriber() {
myVS.onChange -= myHandleChanges;
}
private void myHandleChanges(object newValue) {
myFireEvent(
myLastFiredDataPacket =
myLastFiredDataPacket == null ?
new DataPacket(myGetRows(newValue, ++myId), myGetRows(newValue, myId),
new BlueTable(mySchema))
:
new DataPacket(myGetRows(newValue, ++myId), myGetRows(newValue, myId),
myLastFiredDataPacket.newRows)
);
}
private IBlueRowCollection myGetRows(object dp, int id) {
BlueTable retVal = new BlueTable(mySchema);
retVal.addRow
(new BlueRow(mySchema, id, (dp as DataPacket).newRows.Count));
return retVal;
}
}
}
@@ -0,0 +1,117 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using MathNet.Numerics.Statistics;
using System;
using System.Collections.Generic;
using System.Linq;
public class CovarianceTable : IBlueRowCollection {
readonly IBlueTableSchema schema;
readonly Lazy<IBlueRow> row;
public CovarianceTable(IBlueRowCollection rows1, IBlueRowCollection rows2) {
var inputSchema = rows1.schema;
var inputCols = inputSchema.columnInfo;
var outputCols = Array.ConvertAll(rows1.schema.columnInfo, ColumnFactory);
schema = new BlueTableSchema(outputCols, inputSchema.tableName + "_" + rows2.schema.tableName + "_Covariance");
row = new(RowFactory);
static ColumnInfo ColumnFactory(ColumnInfo inputCol) {
var t = inputCol.colType;
if (inputCol.colName != "id") {
if (t == typeof(short) || t == typeof(int) || t == typeof(long)
|| t == typeof(ushort) || t == typeof(uint) || t == typeof(ulong)
|| t == typeof(double) || t == typeof(float)) {
t = typeof(double);
} else if (t == typeof(DateTime)) {
t = typeof(TimeSpan);
}
}
return new(inputCol.colName, inputCol.colNo, t);
}
IBlueRow RowFactory() {
var rowVals = new object[inputCols.Length];
for (int i = 0; i < inputCols.Length; i++) {
var col = inputCols[i];
var colType = col.colType;
if (col.colName == "id") {
rowVals[i] = 1;
} else if (colType == typeof(short)) {
rowVals[i] = GetValue(v => v as short?);
} else if (colType == typeof(int)) {
rowVals[i] = GetValue(v => v as int?);
} else if (colType == typeof(long)) {
rowVals[i] = GetValue(v => v as long?);
} else if (colType == typeof(ushort)) {
rowVals[i] = GetValue(v => v as ushort?);
} else if (colType == typeof(uint)) {
rowVals[i] = GetValue(v => v as uint?);
} else if (colType == typeof(ulong)) {
rowVals[i] = GetValue(v => v as ulong?);
} else if (colType == typeof(double)) {
rowVals[i] = GetValue(v => v as double?);
} else if (colType == typeof(float)) {
rowVals[i] = GetValue(v => v as float?);
} else if (colType == typeof(DateTime)) {
rowVals[i] = TimeSpan.FromSeconds(GetValue(v => (v as DateTime?)?.Ticks / 10_000_000));
}
double GetValue(Func<object?, double?> convert) {
var vals1 = rows1.GetColumn(i)
.Select(convert)
.SelectWhereValues();
var vals2 = rows2.GetColumn(i)
.Select(convert)
.SelectWhereValues();
return vals1.Covariance(vals2);
}
}
return new Row(schema, rowVals);
}
}
public IBlueRow this[int id] =>
id == 1 ? row.Value : throw new KeyNotFoundException();
IBlueTableSchema IBlueRowCollection.schema => schema;
public int Count => 1;
IBlueRow IList<IBlueRow>.this[int index] {
get => GetRow(index);
set => throw new NotSupportedException();
}
public bool containsID(int id) =>
id == 1;
public IEnumerator<IBlueRow> GetEnumerator() {
yield return row.Value;
}
public IBlueRow GetRow(int index) =>
index == 0 ? row.Value : throw new IndexOutOfRangeException();
public int IndexOf(IBlueRow item) =>
BlueRowEqualityComparer.Equals(item, row.Value) ? 0 : -1;
class Row : IBlueRow {
readonly IBlueTableSchema schema;
readonly object?[] values;
public Row(IBlueTableSchema schema, object?[] values) {
this.schema = schema;
this.values = values;
}
public object? this[int index] => values[index];
IBlueTableSchema IBlueRow.schema => schema;
}
}
@@ -0,0 +1,93 @@
namespace bts.BlueOrb;
using BCG;
using bts.collections;
using Bts.DataStream;
using Bts.DataStreamCore;
using Spark.Core.Disposables;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
public class CummulativeSumStream : ViewStream, IDisposable {
readonly ViewStream inputStream;
readonly CummulativeSumTable.StringBehavior stringBehavior;
IBlueTableSchema? schema;
IDisposable? sub;
SingleRowTable? lastFired;
object?[]? lastFiredValues;
Func<object?, object?, object?>[]? columnOperators;
int idColumnIdx = -1;
public CummulativeSumStream(ViewStream inputStream, CummulativeSumTable.StringBehavior stringBehavior = CummulativeSumTable.StringBehavior.LastValue) {
this.inputStream = inputStream;
this.stringBehavior = stringBehavior;
}
~CummulativeSumStream() =>
Disposable.dispose(ref sub);
public void Dispose() {
GC.SuppressFinalize(this);
Disposable.dispose(ref sub);
}
protected override void myOnFirstSubscriber() {
Disposable.dispose(ref sub);
sub = inputStream.Subscribe(OnInputStreamChanged);
void OnInputStreamChanged(IDataPacket dp) {
ensureInit(dp);
var values = lastFiredValues?.ToArray() ?? new object?[schema.columnInfo.Length];
foreach (var r in dp.addedRows) {
for (int i = 0; i < values.Length; i++) {
values[i] = columnOperators[i](values[i], r[i]);
}
}
var table = new SingleRowTable(new BlueRow(schema, values));
DataPacket dpOut = new(table, table, lastFired as IBlueRowCollection ?? new BlueTable(schema));
lastFired = table;
lastFiredValues = values;
myFireEvent(dpOut);
}
}
protected override void myOnLastSubscriber() =>
Disposable.dispose(ref sub);
[MemberNotNull(nameof(schema), nameof(columnOperators))]
void ensureInit(IDataPacket dp) {
if (schema is not null) {
Log.notnull(columnOperators);
return;
}
schema = dp.Schema;
idColumnIdx = schema.ResolveIndices("id");
var colInfos = schema.columnInfo;
columnOperators = new Func<object?, object?, object?>[schema.columnInfo.Length];
for (int i = 0; i < colInfos.Length; i++) {
if (i == idColumnIdx) {
columnOperators[i] = (x, y) => 1;
continue;
}
columnOperators[i] = schema.columnInfo[i].type_code() switch {
TypeCode.Int32 => (x, y) => (x as int? ?? 0) + (BlueDataUtility.GetInt32(y) ?? 0),
TypeCode.Int64 => (x, y) => (x as long? ?? 0) + (BlueDataUtility.GetInt64(y) ?? 0),
TypeCode.UInt32 => (x, y) => (x as uint? ?? 0) + (BlueDataUtility.GetUInt32(y) ?? 0),
TypeCode.UInt64 => (x, y) => (x as ulong? ?? 0) + (BlueDataUtility.GetUInt64(y) ?? 0),
TypeCode.Single => (x, y) => (x as float? ?? 0) + (BlueDataUtility.GetFloat32(y) ?? 0),
TypeCode.Double => (x, y) => (x as double? ?? 0) + (BlueDataUtility.GetFloat64(y) ?? 0),
TypeCode.String => stringBehavior switch {
CummulativeSumTable.StringBehavior.Concatenate => (x, y) => (x as string ?? string.Empty) + (y as string ?? string.Empty),
_ => (accum, next) => next,
},
TypeCode.DateTime => (x, y) => y,
_ => throw new InvalidOperationException($"Failed to add row values for col '{schema.columnInfo[i]}'"),
};
}
}
}
@@ -0,0 +1,119 @@
namespace bts.BlueOrb;
using BCG;
using bts.collections;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
using System.Linq;
public class CummulativeSumTable : IBlueRowCollection {
readonly IBlueRowCollection input;
readonly IList<object?>[] columns;
readonly StringBehavior stringBehavior;
public CummulativeSumTable(IBlueRowCollection input, StringBehavior stringBehavior = StringBehavior.LastValue) {
this.input = input;
var colInfos = input.schema.columnInfo.OrderBy(c => c.colNo).ToArray();
if (colInfos.Length > 0) {
Log.assert(colInfos[0].colNo == 0 && colInfos[^1].colNo == colInfos.Length - 1);
}
columns = colInfos.WrapSelector(c => GetColumn(c.colNo), null).ToArray();
}
/// <summary>
/// By default, we take the last value for strings intstead of adding (concatenating) them.
/// Otherwise it would be too easy to create massive concatenated strings by mistake.
/// </summary>
public enum StringBehavior {
LastValue,
Concatenate
}
public IBlueRow this[int id] =>
id == 1 ? GetRow(0) : throw new KeyNotFoundException();
public IBlueTableSchema schema => input.schema;
public int Count => 1;
public bool containsID(int id) =>
id == 1;
public IList<object?> GetColumn(int colIndex) {
var count = input.Count;
var colInfo = input.schema.columnInfo[colIndex];
if (colInfo.colName == "id") {
return new object?[] { 1 };
}
object? res = null;
var colVals = input.GetColumn(colIndex);
if (colInfo.colType == typeof(TimeSpan)) {
// TimeSpan is a special case because it doesn't have a TypeCode.
res = TimeSpanImpl(colVals);
} else {
var typeCode = colInfo.type_code();
const int MAX_STRING_LENGTH = 1024;
res = typeCode switch {
TypeCode.Int32 => colVals.Cast<int>().Sum(),
TypeCode.Int64 => colVals.Cast<long>().Sum(),
TypeCode.UInt32 => colVals.Cast<uint>().Sum(),
TypeCode.UInt64 => colVals.Cast<ulong>().Aggregate(0UL, (s, v) => s + v),
TypeCode.Single => colVals.Cast<float>().Sum(),
TypeCode.Double => colVals.Cast<double>().Sum(),
TypeCode.DateTime => colVals.Cast<DateTime>().Aggregate(DateTime.MinValue, (s, v) => new DateTime(s.Ticks + v.Ticks)),
TypeCode.String => stringBehavior switch {
StringBehavior.Concatenate => colVals.Cast<string>().Aggregate(string.Empty, (s, v) => {
if (s.Length + v.Length > MAX_STRING_LENGTH) {
throw new InvalidOperationException("Cannot sum strings because the result is too long");
}
return s + v;
}),
_ => count == 0 ? null : colVals[^1] as string,
},
_ => null,
};
}
return new object?[] { res };
static object? TimeSpanImpl(IList<object?> colVals) {
var ticks = colVals.Cast<TimeSpan>().Select(ts => ts.Ticks).Sum();
return TimeSpan.FromTicks(ticks);
}
}
public IEnumerator<IBlueRow> GetEnumerator() {
yield return GetRow(0);
}
public IBlueRow GetRow(int index) =>
new BlueRowColumnWrapper(input.schema, index, columns);
public int IndexOf(IBlueRow item) {
int id = item.Id();
var inputRow = input[id];
int idx = input.IndexOf(inputRow);
if (idx != -1) {
var row = GetRow(idx);
if (BlueRowEqualityComparer.Equals(item, row)) {
return idx;
}
}
return -1;
}
}
public class CummulativeSumTableEvaluator : IFunctionEvaluator {
public object? Evaluate(string functionId, IEnumerable<StreamArgument> args) {
if (args.TryGetFirst(out var a)) {
var table = a.Value as IBlueRowCollection;
if (table is not null) {
return new CummulativeSumTable(table);
}
}
return null;
}
}
@@ -1,19 +1,19 @@
namespace bts.SilverPine {
namespace bts.BlueOrb {
using bts;
using BCG;
using System;
[DataStreamUserRespresentation("DateTimeExpanderStream")]
public class DateTimeExpanstionStream : ViewStream {
public DateTimeExpanstionStream(ViewStream parent_stream, string inp_col) {
Func<IBlueTableSchema, IBlueTableSchema> get_schema = (s) => {
IBlueTableSchema rv = s.add_column(System.TypeCode.DateTime, $"{inp_col}_date");
rv = rv.add_column(System.TypeCode.String, $"{inp_col}_day");
rv = rv.add_column(System.TypeCode.String, $"{inp_col}_day_of_week");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_day_of_month");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_month");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_hour");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_min");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_minute");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_seconds_of_day");
return rv;
};
@@ -24,15 +24,15 @@ namespace bts.SilverPine {
IBlueTableSchema s = get_schema(r.schema);
MutableBlueRow rv = new MutableBlueRow(s, r);
rv.setValue($"{inp_col}_date", dt.Date);
rv.setValue($"{inp_col}_day", dt.DayOfWeek.ToString());
rv.setValue($"{inp_col}_day_of_week", dt.DayOfWeek.ToString());
rv.setValue($"{inp_col}_day_of_month", dt.Day);
rv.setValue($"{inp_col}_month", dt.Date.Month);
rv.setValue($"{inp_col}_hour", dt.Hour);
rv.setValue($"{inp_col}_min", dt.Minute);
rv.setValue($"{inp_col}_minute", dt.Minute);
rv.setValue($"{inp_col}_seconds_of_day", (dt - dt.Date).TotalSeconds);
return rv;
});
mySetRealSource(rs);
}
}
}
}
@@ -0,0 +1,163 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStream;
using Bts.DataStreamCore;
using Spark.Core.Collections;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
public class DiffStream : ViewStream, IDisposable {
readonly ViewStream source;
readonly int period;
IBlueTableSchema? outputSchema;
IDisposable? sub;
BlueTableImmutable? lastFired;
public DiffStream(ViewStream source, int period) {
this.source = source;
this.period = period;
}
~DiffStream() =>
Dispose();
public void Dispose() =>
Disposable.dispose(ref sub);
protected override void myOnFirstSubscriber() {
Log.isnull(sub);
sub = source.Subscribe(OnSourceChange);
void OnSourceChange(IDataPacket dp) {
if (outputSchema is null) {
var inputSchema = dp.Schema;
var outputCols = new ColumnInfo[inputSchema.columnInfo.Length];
for (int i = 0; i < inputSchema.columnInfo.Length; i++) {
outputCols[i] = GetOutputColumn(inputSchema.columnInfo[i]);
}
outputSchema = new BlueTableSchema($"{inputSchema.tableName}_Diff{period}", outputCols);
}
lastFired ??= new(outputSchema);
var newTable = new DiffTable(dp.newRows, period);
var allRows = new List<object?[]>();
foreach (IBlueRow r in newTable) {
var rvals = new object?[r.schema.columnInfo.Length];
int x = 0;
foreach (var col in r.schema.columnInfo) {
var val = r[col.colNo];
rvals[x++] = val;
}
allRows.Add(rvals);
}
var bd = lastFired.ToDataPacketBuilder();
var removedIndices = new List<int>();
var addedIndices = new List<int>();
var ops = new List<(int Index, bool IsAdd)>();
foreach (var r in dp.deletedRows) {
int id = r.Id();
var rOut = lastFired[id];
var idx = lastFired.IndexOf(rOut);
Log.assert(idx >= 0 && idx < lastFired.Count);
removedIndices.Add(idx);
ops.Add((idx, false));
}
foreach (var r in dp.addedRows) {
var idx = dp.newRows.IndexOf(r);
Log.assert(idx >= 0 && idx < dp.newRows.Count);
addedIndices.Add(idx);
ops.Add((idx, true));
}
ops.Sort(Comparer<(int Index, bool IsAdd)>.Create((a, b) => a.Index.CompareTo(b.Index)));
int rmCount = 0;
var removedRows = new Dictionary<int, IBlueRow>();
for (int i = 0; i < ops.Count; i++) {
var op = ops[i];
if (op.IsAdd) {
continue;
}
rmCount++;
int j = op.Index/* - rmCount*/;
var stop = Math.Min(j + period, lastFired.Count/* - rmCount*/);
// Remove the row that was removed in the incoming DataPacket.
bd.Remove(lastFired.GetRow(j).Id());
for (j++; j < stop; j++) {
// Remove rows that changed as a result. We will add the updated versions
// of these rows in the next pass.
var r = lastFired.GetRow(j);
bd.Remove(r.Id());
}
}
int addCount = ops.Count - rmCount;
for (int i = ops.Count - 1; i >= 0; i--) {
var op = ops[i];
if (op.IsAdd) {
addCount--;
int j = op.Index;
var stop = Math.Min(j + period, newTable.Count);
for (; j < stop; j++) {
var r = newTable.GetRow(j);
bd.AddOrUpdate(r);
}
} else {
rmCount--;
int j = op.Index - rmCount + addCount;
var stop = Math.Min(j + period, newTable.Count);
for (; j < stop; j++) {
// Add rows that changed due to the removed row (the row with which they compute
// the diff has changed).
var r = newTable.GetRow(j);
bd.AddOrUpdate(r);
}
}
}
var dpOut = bd.ToDataPacket(out lastFired, false);
myFireEvent(dpOut);
}
ColumnInfo GetOutputColumn(ColumnInfo inputColumn) {
return inputColumn.type_code() switch {
TypeCode.Boolean => inputColumn.Clone(),
TypeCode.Byte or TypeCode.SByte or TypeCode.Int16 => WithType(TypeCode.Int32),
TypeCode.UInt32 or TypeCode.UInt64 => WithType(TypeCode.Int64),
_ => inputColumn.Clone(),
};
ColumnInfo WithType(TypeCode typeCode) =>
new(inputColumn.colName, inputColumn.colNo, typeCode);
}
}
protected override void myOnLastSubscriber() =>
Disposable.dispose(ref sub);
static class ExpressionBuilder {
public static Func<IBlueRow, IBlueRow, int?> Int32DiffExpression(int colIndex) {
var colIndexParam = Expression.Constant(colIndex, typeof(int));
var rowParam = Expression.Parameter(typeof(IBlueRow), "row");
var getValMethod = typeof(ExpressionBuilder).GetMethod(nameof(getInt32Value))
?? throw new InvalidOperationException("Failed to find int32 value getter via reflection");
var getVal = Expression.Lambda<Func<IBlueRow, int?>>(Expression.Call(null, getValMethod, rowParam, colIndexParam), rowParam).Compile();
return ComputeDiff;
int? ComputeDiff(IBlueRow leftRow, IBlueRow rightRow) {
var leftVal = getVal(leftRow);
var rightVal = getVal(rightRow);
if (leftVal.HasValue && rightVal.HasValue) {
return leftVal.Value - rightVal.Value;
}
return null;
}
}
static int? getInt32Value(IBlueRow row, int colIndex) {
var obj = row[colIndex];
return obj as int?;
}
}
}
@@ -0,0 +1,101 @@
namespace bts.BlueOrb;
using BCG;
using bts.collections;
using Bts.DataStreamCore;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
public class DiffTable : IBlueRowCollection {
readonly IBlueRowCollection input;
readonly int period;
readonly IList<object?>[] columns;
public DiffTable(IBlueRowCollection input, int period) {
this.input = input;
this.period = period;
var colInfos = input.schema.columnInfo.OrderBy(c => c.colNo).ToArray();
if (colInfos.Length > 0) {
Log.assert(colInfos[0].colNo == 0 && colInfos[^1].colNo == colInfos.Length - 1);
}
columns = colInfos.WrapSelector(c => GetColumn(c.colNo), null).ToArray();
}
public IBlueRow this[int id] =>
GetRow(input.IndexOf(input[id]));
public IBlueTableSchema schema => input.schema;
public int Count => input.Count;
public bool containsID(int id) =>
input.containsID(id);
public IList<object?> GetColumn(int colIndex) {
var count = input.Count;
var colInfo = input.schema.columnInfo[colIndex];
if (colInfo.colName == "id") {
return new StepList<int>(0, 1, count).as_mutable().WrapSelector(GetId, null);
}
var typeCode = colInfo.type_code();
return new StepList<int>(0, 1, count).as_mutable().WrapSelector(GetValue, null);
object? GetId(int rowIndex) {
var row = input.GetRow(rowIndex);
return row.Id();
}
object? GetValue(int rowIndex) {
var rightIndex = rowIndex - period;
if (rowIndex < 0 || rowIndex >= count || rightIndex < 0 || rightIndex >= count) {
return null;
}
var leftRow = input.GetRow(rowIndex);
var rightRow = input.GetRow(rightIndex);
return GetDiff(leftRow[colIndex], rightRow[colIndex], typeCode);
}
}
public IEnumerator<IBlueRow> GetEnumerator() {
var count = input.Count;
for (int i = 0; i < count; i++) {
yield return GetRow(i);
}
}
public IBlueRow GetRow(int index) =>
new BlueRowColumnWrapper(input.schema, index, columns);
public int IndexOf(IBlueRow item) {
int id = item.Id();
var inputRow = input[id];
int idx = input.IndexOf(inputRow);
if (idx != -1) {
var row = GetRow(idx);
if (BlueRowEqualityComparer.Equals(item, row)) {
return idx;
}
}
return -1;
}
static object? GetDiff(object? leftVal, object? rightVal, TypeCode colType) {
if (leftVal is null || rightVal is null) {
return null;
}
return colType switch {
TypeCode.Int32 => (int)leftVal - (int)rightVal,
TypeCode.Int64 => (long)leftVal - (long)rightVal,
TypeCode.Double => (double)leftVal - (double)rightVal,
TypeCode.String => ((string)leftVal).Replace((string)rightVal, string.Empty),
TypeCode.DateTime => new DateTime(((DateTime)leftVal).Ticks - ((DateTime)rightVal).Ticks),
_ => null,
};
}
}
@@ -0,0 +1,506 @@
namespace bts.BlueOrb;
using Apache.Arrow;
using Apache.Arrow.Ipc;
using Apache.Arrow.Types;
using BCG;
using bts.collections;
using Bts.DataStreamCore;
using Spark.Core.Disposables;
using Spark.Core.Threading;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
[DataStreamUserRespresentation("featherReader")]
public class FeatherReaderStream : ViewStream {
readonly string file;
FileStream fileStream;
// Let the table leak in case some dangling references linger around after
// myOnLastSubscriber is called. This should be fine since it disposes itself
// in its finalizer.
FeatherTable? table;
public FeatherReaderStream(string file) =>
this.file = file;
protected override void myOnFirstSubscriber() {
if (!Path.Exists(file)) {
throw new InvalidOperationException("Feather file does not exist");
}
fileStream = File.OpenRead(file);
table = new(new ArrowStreamReader(fileStream));
myFireEvent(new DataPacket(table, table, new BlueTableImmutable(table.schema)));
}
interface IFeatherColumn {
int Count { get; }
object? GetValue(int i);
}
interface IFeatherColumn<T> : IFeatherColumn {
new T? GetValue(int i);
object? IFeatherColumn.GetValue(int i) =>
GetValue(i);
}
class FeatherColumn : IFeatherColumn {
readonly IArrowArray array;
public FeatherColumn(IArrowArray array) {
this.array = array;
}
public int Count => array.Length;
public static IFeatherColumn Create(IArrowArray array) =>
array switch {
Int32Array a => new FeatherInt32Column(a),
Int64Array a => new FeatherInt64Column(a),
DoubleArray a => new FeatherFloat64Column(a),
StringArray a => new FeatherStringColumn(a),
BooleanArray a => new FeatherBooleanColumn(a),
Date64Array a => new FeatherDateTimeColumn(a),
Time64Array a => new FeatherTimeStampColumn(a),
_ => throw new NotSupportedException($"Unsupported type: {array.GetType()}")
};
public object? GetValue(int i) =>
array switch {
Int32Array a => a.GetValue(i),
Int64Array a => a.GetValue(i),
DoubleArray a => a.GetValue(i),
StringArray a => a.GetString(i),
BooleanArray a => a.GetValue(i),
Date64Array a => a.GetValue(i),
Time64Array a => a.GetValue(i),
_ => throw new NotSupportedException($"Unsupported type: {array.GetType()}")
};
}
class FeatherInt32Column : IFeatherColumn<int?> {
readonly Int32Array array;
public FeatherInt32Column(Int32Array array) =>
this.array = array;
public int Count => array.Length;
public int? GetValue(int i) => array.GetValue(i);
}
class FeatherInt64Column : IFeatherColumn<long?> {
readonly Int64Array array;
public FeatherInt64Column(Int64Array array) =>
this.array = array;
public int Count => array.Length;
public long? GetValue(int i) => array.GetValue(i);
}
class FeatherFloat64Column : IFeatherColumn<double?> {
readonly DoubleArray array;
public FeatherFloat64Column(DoubleArray array) =>
this.array = array;
public int Count => array.Length;
public double? GetValue(int i) => array.GetValue(i);
}
class FeatherFloat32Column : IFeatherColumn<float?> {
readonly FloatArray array;
public FeatherFloat32Column(FloatArray array) =>
this.array = array;
public int Count => array.Length;
public float? GetValue(int i) => array.GetValue(i);
}
class FeatherStringColumn : IFeatherColumn<string?> {
readonly StringArray array;
public FeatherStringColumn(StringArray array) =>
this.array = array;
public int Count => array.Length;
public string? GetValue(int i) => array.GetString(i);
}
class FeatherBooleanColumn : IFeatherColumn<bool?> {
readonly BooleanArray array;
public FeatherBooleanColumn(BooleanArray array) =>
this.array = array;
public int Count => array.Length;
public bool? GetValue(int i) => array.GetValue(i);
}
class FeatherDateTimeColumn : IFeatherColumn<DateTime?> {
readonly Date64Array array;
public FeatherDateTimeColumn(Date64Array array) =>
this.array = array;
public int Count => array.Length;
public DateTime? GetValue(int i) {
var nanos = array.GetValue(i);
if (!nanos.HasValue) {
return null;
}
return new(nanos.Value / 100);
}
}
class FeatherTimeStampColumn : IFeatherColumn<DateTime?> {
readonly Time64Array array;
public FeatherTimeStampColumn(Time64Array array) =>
this.array = array;
public int Count => array.Length;
public DateTime? GetValue(int i) {
var nanos = array.GetValue(i);
if (!nanos.HasValue) {
return null;
}
return new(nanos.Value / 100);
}
}
class FeatherBlueRow : IBlueRow {
readonly int id;
readonly int rowIndex;
readonly IFeatherColumn[] data;
public FeatherBlueRow(int id, int rowIndex, IFeatherColumn[] data, IBlueTableSchema schema) {
this.id = id;
this.rowIndex = rowIndex;
this.data = data;
Schema = schema;
}
public object? this[int index] =>
index == 0
? id
: data[index - 1].GetValue(rowIndex);
public IBlueTableSchema Schema { get; }
IBlueTableSchema IBlueRow.schema => Schema;
}
class FeatherColumnDataList : IReadOnlyList<object?> {
// Represents 1 column split into batches (hence it being an array)
readonly IReadOnlyList<IFeatherColumn> columns;
int? count;
public FeatherColumnDataList(IReadOnlyList<IFeatherColumn> columns) {
this.columns = columns;
}
public object? this[int index] {
get {
for (int i = 0; i < columns.Count; i++) {
var col = columns[i];
if (index < col.Count) {
return col.GetValue(index);
}
index -= col.Count;
}
throw new IndexOutOfRangeException();
}
}
public int Count =>
count ??= columns.Sum(c => c.Count);
public IEnumerator<object?> GetEnumerator() {
for (int i = 0; i < columns.Count; i++) {
var col = columns[i];
for (int j = 0; j < col.Count; j++) {
yield return col.GetValue(i);
}
}
}
IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator();
}
class FeatherTable : IImmutableRowCollection, IDisposable {
ImmutableList<(RecordBatch Records, IFeatherColumn[] Columns)> recordBatches = ImmutableList<(RecordBatch Records, IFeatherColumn[] Columns)>.Empty;
readonly IReadOnlyDictionary<string, Field> fields;
readonly AtomicGate gate = new();
int count;
ArrowStreamReader? reader;
public FeatherTable(ArrowStreamReader reader) {
this.reader = reader;
if (reader.Schema is null) {
var batch = reader.ReadNextRecordBatch();
Log.notnull(batch);
Log.notnull(reader.Schema);
var fcols = new IFeatherColumn[batch.ColumnCount];
for (int i = 0; i < fcols.Length; i++) {
fcols[i] = FeatherColumn.Create(batch.Column(i));
}
recordBatches = recordBatches.Add((batch, fcols));
count = batch.Length;
}
fields = reader.Schema.Fields;
var columns = new ColumnInfo[fields.Count + 1];
columns[0] = new("id", 0, typeof(int));
for (int i = 0; i < fields.Count; i++) {
var f = reader.Schema.GetFieldByIndex(i);
Log.assert(f.Name != "id");
columns[i + 1] = new(f.Name, i + 1, getFieldType(f));
}
schema = new BlueTableSchema("FeatherReader", columns);
}
~FeatherTable() =>
Disposable.DisposeThreadSafe(ref reader);
public IBlueTableSchema schema { get; }
public int Count {
get {
if (reader is not null) {
readBatches().Wait();
Log.isnull(reader);
}
return count;
}
}
public IBlueRow this[int id] => GetRow(id - 1);
public bool containsID(int id) {
while (id > count && reader is not null) {
readBatches(1).Wait();
}
return id <= count;
}
public int IndexOf(IBlueRow item) {
int id = item.Id();
while (id > count && reader is not null) {
readBatches(1).Wait();
}
return id <= count && BlueRowEqualityComparer.Equals(GetRow(id - 1), item)
? id - 1
: -1;
}
public IBlueRow GetRow(int index) {
if (index < 0) {
throw new IndexOutOfRangeException();
}
if (count <= index) {
readRows(index + 1).Wait();
}
int id = index + 1;
foreach (var (records, columns) in recordBatches) {
if (index < records.Length) {
return new FeatherBlueRow(id, index, columns, schema);
}
index -= records.Length;
}
throw new IndexOutOfRangeException();
}
public IList<object?> GetColumn(int index) =>
new FeatherColumnDataList(new MyColList(this, index)).as_mutable();
public IEnumerator<IBlueRow> GetEnumerator() {
int i = 0;
foreach (var (records, columns) in recordBatches) {
yield return new FeatherBlueRow(i + 1, i, columns, schema);
i++;
}
while (reader is not null) {
var task = readBatches(1);
task.Wait();
if (!task.Result) {
break;
}
yield return new FeatherBlueRow(i + 1, i, recordBatches[i].Columns, schema);
i++;
}
}
public void Dispose() =>
Disposable.DisposeThreadSafe(ref reader);
static Type getFieldType(Field f) =>
f.DataType.TypeId switch {
ArrowTypeId.Null => throw new NotImplementedException(),
ArrowTypeId.Boolean => typeof(bool),
ArrowTypeId.UInt8 => typeof(byte),
ArrowTypeId.Int8 => typeof(sbyte),
ArrowTypeId.UInt16 => typeof(ushort),
ArrowTypeId.Int16 => typeof(short),
ArrowTypeId.UInt32 => typeof(uint),
ArrowTypeId.Int32 => typeof(int),
ArrowTypeId.UInt64 => typeof(ulong),
ArrowTypeId.Int64 => typeof(long),
ArrowTypeId.HalfFloat => throw new NotImplementedException(),
ArrowTypeId.Float => typeof(float),
ArrowTypeId.Double => typeof(double),
ArrowTypeId.String => typeof(string),
ArrowTypeId.Binary => throw new NotImplementedException(),
ArrowTypeId.FixedSizedBinary => throw new NotImplementedException(),
ArrowTypeId.Date32 => throw new NotImplementedException(),
ArrowTypeId.Date64 => throw new NotImplementedException(),
ArrowTypeId.Timestamp => throw new NotImplementedException(),
ArrowTypeId.Time32 => throw new NotImplementedException(),
ArrowTypeId.Time64 => throw new NotImplementedException(),
ArrowTypeId.Interval => throw new NotImplementedException(),
ArrowTypeId.Decimal128 => throw new NotImplementedException(),
ArrowTypeId.Decimal256 => throw new NotImplementedException(),
ArrowTypeId.List => throw new NotImplementedException(),
ArrowTypeId.Struct => throw new NotImplementedException(),
ArrowTypeId.Union => throw new NotImplementedException(),
ArrowTypeId.Dictionary => throw new NotImplementedException(),
ArrowTypeId.Map => throw new NotImplementedException(),
_ => throw new ArgumentException("Unsupported field type", nameof(f)),
};
async Task<bool> readRows(int minCount) {
if (reader is null) {
return count >= minCount;
}
int n = 0;
using (gate.BeginTransaction()) {
if (reader is not null) {
int origCount = count;
while ((count + n) < minCount) {
Log.assert(count == origCount);
var batch = await reader.ReadNextRecordBatchAsync();
if (batch is null) {
Disposable.dispose(ref reader);
break;
}
var columns = new IFeatherColumn[batch.ColumnCount];
for (int i = 0; i < columns.Length; i++) {
columns[i] = FeatherColumn.Create(batch.Column(i));
}
recordBatches.Add((batch, columns));
n += batch.Length;
}
count += n;
}
}
return count >= minCount;
}
async Task<bool> readBatches(int? count = null) {
if (reader is null) {
return !count.HasValue || count.Value == 0;
}
if (count.HasValue && count.Value <= recordBatches.Count) {
return true;
}
int n = 0;
using (gate.BeginTransaction()) {
if (reader is not null) {
int origCount = this.count;
var recordBatchesBd = recordBatches.ToBuilder();
while (!count.HasValue || count-- > recordBatchesBd.Count) {
Log.assert(count == origCount);
var batch = await reader.ReadNextRecordBatchAsync();
if (batch is null) {
Disposable.dispose(ref reader);
break;
}
var columns = new IFeatherColumn[batch.ColumnCount];
for (int i = 0; i < columns.Length; i++) {
columns[i] = FeatherColumn.Create(batch.Column(i));
}
recordBatchesBd.Add((batch, columns));
n += batch.Length;
}
recordBatches = recordBatchesBd.ToImmutable();
this.count += n;
}
}
return !count.HasValue || count.Value <= recordBatches.Count;
}
class MyColList : IReadOnlyList<IFeatherColumn> {
readonly FeatherTable owner;
readonly int columnIndex;
public MyColList(FeatherTable owner, int columnIndex) {
this.owner = owner;
this.columnIndex = columnIndex;
}
public IFeatherColumn this[int index] {
get {
if (index < 0) {
throw new IndexOutOfRangeException();
}
if (index >= owner.recordBatches.Count) {
var task = owner.readBatches(index + 1);
task.Wait();
if (!task.Result) {
throw new IndexOutOfRangeException();
}
}
return owner.recordBatches[index].Columns[columnIndex];
}
}
public int Count {
get {
if (owner.reader is not null) {
owner.readBatches().Wait();
}
return owner.recordBatches.Count;
}
}
public IEnumerator<IFeatherColumn> GetEnumerator() {
int i = 0;
foreach (var batch in owner.recordBatches) {
yield return batch.Columns[columnIndex];
i++;
}
while (owner.reader is not null) {
var task = owner.readBatches(1);
task.Wait();
if (!task.Result) {
break;
}
yield return owner.recordBatches[i].Columns[columnIndex];
i++;
}
}
IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator();
}
}
}
@@ -0,0 +1,159 @@
namespace bts.BlueOrb {
using BCG;
using Bts.DataStreamCore;
using mbts.utils;
using System;
using System.Text.RegularExpressions;
[DataStreamUserRespresentation("FilterStream")]
public class FilterStream : ViewStream {
public class RegExpFilter : IStreamFilter {
private Regex[] myRegExps;
private string[] myCols;
public RegExpFilter(string col, string regExp)
: this(new string[] { col }, new string[] { regExp }) { }
public RegExpFilter(string[] cols, string[] regExps) {
myCols = cols;
myRegExps = new Regex[regExps.Length];
for (int i = 0; i < regExps.Length; i++)
myRegExps[i] = new Regex(regExps[i]);
}
#region IStreamFilter Members
public bool filter(IBlueRow dr) {
bool rv = true;
for (int i = 0; i < myCols.Length; i++) {
rv &= myRegExps[i].IsMatch(dr[myCols[i]] is DBNull ? "" : dr[myCols[i]]?.ToString() ?? string.Empty);
if (!rv)
return rv;
}
return rv;
}
#endregion
}
BlueTableImmutable? myLastFiredRows;
IBlueRowCollection? myLastReceivedRows;
private IStreamFilter myFilter;
private ViewStream myParentStream;
private string myFilterString;
private bool myUseNewFilter = false;// Configuration.get<DataStreamConfig>().UseNewFilter;
#region constructors
public delegate bool OnFilter(IBlueRow r);
private class OnFilterWrapper : IStreamFilter {
public OnFilterWrapper(OnFilter f) { this.f = f; }
public bool filter(IBlueRow dr) { return f(dr); }
private OnFilter f;
}
public FilterStream(ViewStream vs, OnFilter f)
: this(vs, new OnFilterWrapper(f)) { }
public FilterStream(ViewStream vs, IStreamFilter filter) {
myParentStream = vs;
myFilter = filter;
myFilterString = filter.ToString() ?? string.Empty;
if (filter is IDynamicStreamFilter dynFilter) {
dynFilter.onFilterChange += new FilterChangeDelegate(myHandleFilterChange);
}
}
public FilterStream(ViewStream vs, string filterExpression)
: this(vs, new BooleanExpressionFilter(filterExpression)) {
myFilterString = filterExpression;
}
public FilterStream(ViewStream vs, string col, string regExp)
: this(vs, new RegExpFilter(col, regExp)) {
myFilterString = col + " , " + regExp;
}
public FilterStream(ViewStream vs)
: this(vs, new BinaryFilter(true)) {
myFilterString = true.ToString();
}
#endregion
#region overridden methods
protected override void myOnFirstSubscriber() =>
myParentStream.onChange += myHandleCallbacks;
protected override void myOnLastSubscriber() {
myLastFiredRows = null;
myParentStream.onChange -= myHandleCallbacks;
}
protected override void myFireEvent(object o) {
var dp = ExceptionAssert.Argument.Is<IDataPacket>(o);
if (myLastValue == null || dp.addedRows.Count + dp.deletedRows.Count > 0)
base.myFireEvent(o);
}
#endregion
private void myHandleCallbacks(object newValue) {
var arrivedPacket = ExceptionAssert.Argument.Is<IDataPacket>(newValue);
myLastReceivedRows = arrivedPacket.newRows;
var bd = (myLastFiredRows ?? new BlueTableImmutable(arrivedPacket.Schema)).ToDataPacketBuilder();
foreach (IBlueRow r in arrivedPacket.deletedRows) {
// Don't bother evaluating the filter, just try to remove the row. The latter is
// cheaper than the former (it's a noop if this row wasn't in the filtered table).
bd.Remove(r.Id());
}
foreach (IBlueRow r in arrivedPacket.newRows) {
if (myFilter.filter(r)) {
bd.AddOrUpdate(r);
}
}
var dpOut = bd.ToDataPacket(out myLastFiredRows);
myFireEvent(dpOut);
}
public IStreamFilter filter {
set {
{
myFilter = value;
if (numberOfSubscribers > 0) {
if (myLastReceivedRows != null) {
var bd = (myLastFiredRows ?? new BlueTableImmutable(myLastReceivedRows.schema)).ToDataPacketBuilder();
foreach (IBlueRow r in myLastReceivedRows) {
int id = r.Id();
bool includeNow = myFilter.filter(r);
bool includeBefore = myLastFiredRows?.containsID(id) ?? false;
if (includeNow == includeBefore) {
continue;
}
if (includeNow) {
bd.AddOrUpdate(r);
} else {
bd.Remove(id);
}
}
var dpOut = bd.ToDataPacket(out myLastFiredRows);
myFireEvent(dpOut);
}
}
}
}
get {
return myFilter;
}
}
//public string filterString { set { filter = new BooleanExpressionFilter(value); } }
public override string ToString() {
return GetType().ToString() + " " + myFilterString;
}
private void myHandleFilterChange() {
filter = myFilter;
}
}
}
@@ -0,0 +1,72 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
public class FilterTable : IBlueRowCollection {
readonly IBlueRowCollection source;
readonly HashSet<int> excludedIndices = new();
readonly Dictionary<int, (int Index, int NumSkips, bool Included, IBlueRow Row)> rowMap = new();
readonly List<int> filteredIndexToId = new();
public FilterTable(IBlueRowCollection source, Func<IBlueRow, bool> filter) {
this.source = source;
int idx = 0;
int numSkips = 0;
foreach (var r in source) {
int id = r.Id();
bool included = filter(r);
rowMap.Add(id, (idx, numSkips, included, r));
if (!included) {
excludedIndices.Add(idx);
numSkips++;
} else {
filteredIndexToId.Add(id);
}
idx++;
}
}
public IBlueRow this[int id] {
get {
var r = rowMap[id];
return r.Included ? r.Row : throw new KeyNotFoundException();
}
}
public IBlueTableSchema schema => source.schema;
public int Count => filteredIndexToId.Count;
public bool containsID(int id) =>
rowMap.TryGetValue(id, out var r) && r.Included;
public IEnumerator<IBlueRow> GetEnumerator() {
int idx = 0;
foreach (var r in source) {
if (!excludedIndices.Contains(idx)) {
yield return r;
}
idx++;
}
}
public IBlueRow GetRow(int index) {
if (index < 0 || index >= source.Count) {
throw new IndexOutOfRangeException();
}
var id = filteredIndexToId[index];
return rowMap[id].Row;
}
public int IndexOf(IBlueRow item) =>
rowMap.TryGetValue(item.Id(), out var r)
&& r.Included
&& BlueRowEqualityComparer.Equals(r.Row, item)
? r.Index - r.NumSkips
: -1;
}
@@ -0,0 +1,222 @@
namespace Bts.SharedStreams.StreamFactories;
using BCG;
using bts;
using bts.api.core;
using bts.BlueOrb;
using Bts.DataStream;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
public class GroupByApplyStream : ViewStream {
readonly ViewStream inputStream;
readonly IFunctionEvaluator groupBy;
readonly IFunctionEvaluator apply;
public GroupByApplyStream(ViewStream inputStream, IFunctionEvaluator groupBy, IFunctionEvaluator apply) {
this.inputStream = inputStream;
this.groupBy = groupBy;
this.apply = apply;
}
public static IObservable<TableUpdate<IBlueRow, int>> CreateGroupByApplyTable(ViewStream inputStream, IFunctionEvaluator groupBy, IFunctionEvaluator apply) =>
CreateApplyTable(CreateGroupByTable(inputStream, groupBy), apply);
public static IObservable<TableUpdate<IBlueRow, int>> CreateApplyTable<TGroupKey>(IObservable<TableUpdate<TableUpdateGrouping<IBlueRow, int, TGroupKey>, TGroupKey>> inputStream, Func<TGroupKey, IBlueRowCollection, IBlueRowCollection?> apply) where TGroupKey : IEquatable<TGroupKey> {
var groupTables = ImmutableDictionary<TGroupKey, GroupTableBuilder>.Empty;
var inputToOutputIds = ImmutableDictionary<int, int>.Empty;
var outputToInputIds = ImmutableDictionary<int, int>.Empty;
int idCounter = 0;
return Observable.Create<TableUpdate<IBlueRow, int>>(Subscribe);
int GetNextId() =>
Interlocked.Increment(ref idCounter);
IDisposable Subscribe(IObserver<TableUpdate<IBlueRow, int>> observer) {
int hashCounter = 0;
var hashIndex = new Dictionary<TGroupKey, int>();
var tableOut = TableUpdate<IBlueRow, int>.mk_empty(r => r.Id());
return inputStream.Subscribe(OnNext, OnError, OnCompleted);
void OnNext(TableUpdate<TableUpdateGrouping<IBlueRow, int, TGroupKey>, TGroupKey> upd) {
var bd = tableOut.to_builder();
foreach (var group in upd.removed) {
var hash = hashIndex[group.group_key];
bd.remove(hash);
}
var groupTablesBd = groupTables.ToBuilder();
foreach (var group in upd.added.Concat(upd.changed)) {
Log.assert(group.table.state.Count > 0);
var hash = hashIndex.GetOrAdd(group.group_key, _ => hashCounter++);
var groupRowCollection = new TableUpdateGroupingRowCollection<TGroupKey>(group, group.table.state.Values.First().schema);
var applyArgs = new (string?, int?, object?)[] { ("rows", 0, groupRowCollection) };
var resRows = apply(group.group_key, groupRowCollection);
if (resRows is null) {
bd.remove(hash);
continue;
}
UpdateGroupTable(group.group_key, resRows);
}
groupTables = groupTablesBd.ToImmutable();
tableOut = bd.to_table();
observer.OnNext(tableOut);
void UpdateGroupTable(TGroupKey groupKey, IBlueRowCollection groupRows) {
var indexedRows = groupTablesBd.GetOrAdd(groupKey, _ => new(GetNextId)).Reset(groupRows);
foreach (var r in indexedRows) {
bd.add_or_update(r);
}
}
}
}
void OnError(Exception ex) {
Log.DataFlow.error($$"""[stream_err.json]{ "typ": "{{nameof(GroupByApplyStream)}}", "inpTyp": {{inputStream.GetType()}}, "err": "{{ex}}" }""");
}
void OnCompleted() {
Log.DataFlow.warn($$"""[stream_fin.json]{ "typ": "{{nameof(GroupByApplyStream)}}", "inpTyp": "{{inputStream.GetType()}}" }""");
}
}
public static IObservable<TableUpdate<IBlueRow, int>> CreateApplyTable<TGroupKey>(IObservable<TableUpdate<TableUpdateGrouping<IBlueRow, int, TGroupKey>, TGroupKey>> inputStream, IFunctionEvaluator apply) where TGroupKey : IEquatable<TGroupKey> {
var groupTables = ImmutableDictionary<TGroupKey, GroupTableBuilder>.Empty;
var inputToOutputIds = ImmutableDictionary<int, int>.Empty;
var outputToInputIds = ImmutableDictionary<int, int>.Empty;
int idCounter = 0;
return Observable.Create<TableUpdate<IBlueRow, int>>(Subscribe);
int GetNextId() =>
Interlocked.Increment(ref idCounter);
IDisposable Subscribe(IObserver<TableUpdate<IBlueRow, int>> observer) {
int hashCounter = 0;
var hashIndex = new Dictionary<TGroupKey, int>();
var tableOut = TableUpdate<IBlueRow, int>.mk_empty(r => r.Id());
return inputStream.Subscribe(OnNext, OnError, OnCompleted);
void OnNext(TableUpdate<TableUpdateGrouping<IBlueRow, int, TGroupKey>, TGroupKey> upd) {
var bd = tableOut.to_builder();
foreach (var group in upd.removed) {
var hash = hashIndex[group.group_key];
bd.remove(hash);
}
var groupTablesBd = groupTables.ToBuilder();
foreach (var group in upd.added.Concat(upd.changed)) {
Log.assert(group.table.state.Count > 0);
var hash = hashIndex.GetOrAdd(group.group_key, _ => hashCounter++);
var groupRowCollection = new TableUpdateGroupingRowCollection<TGroupKey>(group, group.table.state.Values.First().schema);
var applyArgs = new StreamArgument[] { ("rows", 0, groupRowCollection) };
var res = apply.Evaluate(string.Empty, applyArgs);
if (res is IBlueRow resSingleRow) {
UpdateGroupTable(group.group_key, new SingleRowTable(resSingleRow));
} else if (res is IBlueRowCollection resRows) {
UpdateGroupTable(group.group_key, resRows);
} else {
throw new InvalidOperationException("Invalid result returned by 'Apply' fn");
}
}
groupTables = groupTablesBd.ToImmutable();
tableOut = bd.to_table();
observer.OnNext(tableOut);
void UpdateGroupTable(TGroupKey groupKey, IBlueRowCollection groupRows) {
var indexedRows = groupTablesBd.GetOrAdd(groupKey, _ => new(GetNextId)).Reset(groupRows);
foreach (var r in indexedRows) {
bd.add_or_update(r);
}
}
}
}
void OnError(Exception ex) {
Log.DataFlow.error($$"""[stream_err.json]{ "typ": "{{nameof(GroupByApplyStream)}}", "inpTyp": {{inputStream.GetType()}}, "err": "{{ex}}" }""");
}
void OnCompleted() {
Log.DataFlow.warn($$"""[stream_fin.json]{ "typ": "{{nameof(GroupByApplyStream)}}", "inpTyp": "{{inputStream.GetType()}}" }""");
}
}
public static IObservable<TableUpdate<TableUpdateGrouping<IBlueRow, int, EquatableKeyWrapper>, EquatableKeyWrapper>> CreateGroupByTable(ViewStream inputStream, IFunctionEvaluator groupBy) {
return Observable.Create<TableUpdate<TableUpdateGrouping<IBlueRow, int, EquatableKeyWrapper>, EquatableKeyWrapper>>(Subscribe);
IDisposable Subscribe(IObserver<TableUpdate<TableUpdateGrouping<IBlueRow, int, EquatableKeyWrapper>, EquatableKeyWrapper>> observer) {
TableUpdate<TableUpdateGrouping<IBlueRow, int, EquatableKeyWrapper>, EquatableKeyWrapper> tableOut = TableUpdate<TableUpdateGrouping<IBlueRow, int, EquatableKeyWrapper>, EquatableKeyWrapper>.mk_empty(r => r.group_key);
return inputStream.AsObservable().AsTables().GroupTable(r => r.Id(), GroupKey).Subscribe(observer);
EquatableKeyWrapper GroupKey(IBlueRow r) {
var groupByArgs = new StreamArgument[] { ("row", 0, r) };
var groupKey = new EquatableKeyWrapper(groupBy.Evaluate(string.Empty, groupByArgs) ?? DBNull.Value);
return groupKey;
}
}
}
public static IObservable<TableUpdate<TableUpdateGrouping<IBlueRow, int, EquatableKeyArrayWrapper>, EquatableKeyArrayWrapper>> CreateGroupByTable(ViewStream inputStream, string[] columns, bool useColumnIndices = true) {
Func<IBlueRow, EquatableKeyArrayWrapper> groupBy;
if (!useColumnIndices) {
groupBy = GroupKeyByName;
EquatableKeyArrayWrapper GroupKeyByName(IBlueRow r) =>
new(Array.ConvertAll(columns, c => r[c]));
} else {
int[]? columnIndices = null;
groupBy = GroupKeyByIndex;
EquatableKeyArrayWrapper GroupKeyByIndex(IBlueRow r) {
columnIndices ??= r.schema.ResolveIndices(columns);
return new(Array.ConvertAll(columnIndices, c => r[c]));
}
}
return CreateGroupByTable(inputStream, groupBy);
}
public static IObservable<TableUpdate<TableUpdateGrouping<IBlueRow, int, TKey>, TKey>> CreateGroupByTable<TKey>(ViewStream inputStream, Func<IBlueRow, TKey> groupBy) where TKey : IEquatable<TKey> {
return Observable.Create<TableUpdate<TableUpdateGrouping<IBlueRow, int, TKey>, TKey>>(Subscribe);
IDisposable Subscribe(IObserver<TableUpdate<TableUpdateGrouping<IBlueRow, int, TKey>, TKey>> observer) =>
inputStream.AsObservable().AsTables().GroupTable(r => r.Id(), groupBy).Subscribe(observer);
}
class GroupTableBuilder {
readonly Func<int> getUniqueId;
ImmutableDictionary<int, int> inputToOutputIds = ImmutableDictionary<int, int>.Empty;
ImmutableDictionary<int, int> outputToInputIds = ImmutableDictionary<int, int>.Empty;
IndexTable? current;
public GroupTableBuilder(Func<int> getUniqueId) =>
this.getUniqueId = getUniqueId;
public IndexTable? Current => current;
public IndexTable Reset(IBlueRowCollection rows) {
var inputToOutputIdsBd = inputToOutputIds.ToBuilder();
var outputToInputIdsBd = outputToInputIds.ToBuilder();
foreach (var r in rows) {
int inputId = r.Id();
if (!inputToOutputIdsBd.TryGetValue(inputId, out var outputId)) {
outputId = getUniqueId();
inputToOutputIdsBd.Add(inputId, outputId);
outputToInputIdsBd.Add(outputId, inputId);
}
Log.assert(outputToInputIdsBd.TryGetValue(outputId, out var oinputId) && oinputId == inputId);
Log.assert(inputToOutputIdsBd.TryGetValue(inputId, out var ioutputId) && ioutputId == outputId);
}
inputToOutputIds = inputToOutputIdsBd.ToImmutable();
outputToInputIds = outputToInputIdsBd.ToImmutable();
current = new(rows, outputToInputIds, inputToOutputIds);
return current;
}
}
}
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;
using bts;
//using bts.SilverPine;
using BCG;
using mbts.utils;
namespace bts.BlueOrb {
[DataStreamUserRespresentation("GroupByStream")]
public class GroupByStream : ViewStream {
public interface INonIncGroupByEvaluator {
// grouped_rows
IBlueRowCollection? evaluate(IDictionary<int, List<IBlueRow>> grouped_rows);
string[]? get_group_by_cols();
}
private class DelNonIncGroupByEvaluator : INonIncGroupByEvaluator {
Func<string[]?> _groupby_cols_func;
Func<List<IBlueRow>, List<IBlueRow>> _evaluate_func;
public DelNonIncGroupByEvaluator(Func<string[]?> groupby_cols_func, Func<List<IBlueRow>, List<IBlueRow>> evaluate_func) {
_groupby_cols_func = groupby_cols_func;
_evaluate_func = evaluate_func;
}
public IBlueRowCollection? evaluate(IDictionary<int, List<IBlueRow>> grouped_rows) {
BlueTable? rv = null;
foreach (var kv in grouped_rows) {
List<IBlueRow> g_rows = kv.Value;
List<IBlueRow> out_rows = _evaluate_func.Invoke(g_rows);
if (out_rows is null || out_rows.Count == 0) {
continue;
}
rv ??= new BlueTable(out_rows.ElementAt(0).schema);
rv.addRows(out_rows);
}
return rv;
}
public string[]? get_group_by_cols() {
return _groupby_cols_func();
}
}
readonly ViewStream? _main_stream;
readonly INonIncGroupByEvaluator? _groupby_func;
readonly string[]? _groupby_cols;
readonly Stopwatch _timer = new();
BlueRowHasher? _row_hasher;
int _n_total_fires = 0;
int _n_expensive_fires = 0;
public GroupByStream(ViewStream main_stream, INonIncGroupByEvaluator groupby_func) {
_main_stream = main_stream;
_groupby_func = groupby_func;
_groupby_cols = _groupby_func.get_group_by_cols();
_row_hasher = new BlueRowHasher(_groupby_cols == null ? 0 : _groupby_cols.Length);
}
public GroupByStream(ViewStream main_stream, Func<string[]?> groupby_cols_func, Func<List<IBlueRow>, List<IBlueRow>> evaluate_func)
: this(main_stream, new DelNonIncGroupByEvaluator(groupby_cols_func, evaluate_func)) { }
public GroupByStream(ViewStream parent_str, string groupby_cols, string standard_group_by_text)
: this(parent_str, groupby_cols, standard_group_by_text, String.Empty) { }
// this is the main constructor that does everything
public GroupByStream(ViewStream parent_str, string groupby_cols, string standard_group_by_ops, string optional_sort_col) {
MultiRowOpHelper std_group_by = MultiRowOpHelper.parse_ops(standard_group_by_ops);
Func<string[]?> groupby_cols_func = () => {
if (String.IsNullOrEmpty(groupby_cols))
return null;
return groupby_cols.Split(',').Select(x => x.Trim()).ToArray();
};
Func<List<IBlueRow>, List<IBlueRow>> evaluate_func = (group_rows) => {
if (!String.IsNullOrEmpty(optional_sort_col))
group_rows.Sort((r1, r2) => BlueDataUtility.compare_rows(r2, r1, optional_sort_col));
// this si where the group by aggregation happens
IBlueRow rv = MultiRowOpHelper.group_by_compute(group_rows, std_group_by);
if (rv == null)
return new List<IBlueRow>();
return new IBlueRow[] { rv }.ToList();
};
ViewStream rs = new GroupByStream(parent_str, groupby_cols_func, evaluate_func);
mySetRealSource(rs);
}
public GroupByStream(ViewStream parent_str, string standard_group_by_text) :
this(parent_str, String.Empty, standard_group_by_text) {
}
protected override void myOnFirstSubscriber() { Log.notnull(_main_stream).onChange += _main_stream_onchange; }
protected override void myOnLastSubscriber() { Log.notnull(_main_stream).onChange -= _main_stream_onchange; }
private void _main_stream_onchange(object nv) {
var dp = ExceptionAssert.Argument.Is<IDataPacket>(nv);
///grouping together
_timer.Restart();
IDictionary<int, List<IBlueRow>> grouped_rows = (_groupby_cols == null || _groupby_cols.Length == 0) ?
new Dictionary<int, List<IBlueRow>>() { { 0, dp.newRows.to_list() } } :
BlueDataUtility.group_together(dp.newRows, _groupby_cols, ref _row_hasher);
_timer.Stop();
var groupElapsedMs = _timer.ElapsedMilliseconds;
if (groupElapsedMs >= 50) {
Log.DataFlow.info($"Time to group {dp.newRows.Count} rows into {grouped_rows.Count} groups: {_timer.ElapsedMilliseconds}ms");
}
// Log.log(bts.Log.Level.Info, bts.log.Subsystem.gen, String.Format("Time to group {0} rows into {1} groups = {2}", dp.newRows.Count, grouped_rows.Count, _timer.lastIntervalTime));
/// reducing
_timer.Restart();
var total_state = Log.notnull(_groupby_func).evaluate(grouped_rows);
if (total_state == null)
// mayeb returna n empty table ?
return;
_timer.Stop();
var applyElapsedMs = _timer.ElapsedMilliseconds;
_n_total_fires++;
if (applyElapsedMs >= 50) {
_n_expensive_fires++;
Log.DataFlow.info(
$"Time to evaluate {grouped_rows.Count} groups {_timer.ElapsedMilliseconds}ms, ratio {_n_expensive_fires}/{_n_total_fires}");
}
myFireEvent(new DataPacket(total_state, total_state, myLastValue == null ? new BlueTable(total_state.schema) : (myLastValue as DataPacket).newRows));
}
}
}
@@ -0,0 +1,83 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using System;
using System.Collections.Generic;
using System.Linq;
public class GroupByTable<TKey> : IBlueRowCollection {
readonly IBlueTableSchema outputSchema;
readonly IBlueRowCollection inputRows;
readonly List<IBlueRow> rows;
readonly Dictionary<int, int> idToIndex;
public GroupByTable(IBlueRowCollection inputRows, Func<IBlueRow, TKey> getKey, IEqualityComparer<TKey> keyEqualityComparer, IComparer<TKey> keyComparer, string? groupKeyCol = "group") {
this.inputRows = inputRows;
var cols = inputRows.schema.columnInfo.ToList();
rows = new(inputRows.Count);
int groupKeyColIdx = -1;
if (!string.IsNullOrEmpty(groupKeyCol)) {
groupKeyColIdx = cols.Count;
cols.Add(new(groupKeyCol, groupKeyColIdx, typeof(TKey)));
}
for (int i = 0; i < cols.Count; i++) {
Log.assert(cols[i].colNo == i);
}
outputSchema = new BlueTableSchema(cols.ToArray(), "GroupBy");
rows.AddRange(inputRows
.GroupBy(getKey, keyEqualityComparer)
.OrderBy(g => g.Key, keyComparer)
.SelectMany(rg => rg.Select(r => new Row(rg.Key, r, outputSchema, groupKeyColIdx))));
idToIndex = rows.Select((r, i) => (r, i)).ToDictionary(t => t.r.Id(), t => t.i);
}
public IBlueRow this[int id] =>
rows[idToIndex[id]];
public IBlueTableSchema schema => outputSchema;
public int Count => rows.Count;
public bool containsID(int id) =>
idToIndex.ContainsKey(id);
public IEnumerator<IBlueRow> GetEnumerator() =>
rows.GetEnumerator();
public IBlueRow GetRow(int index) =>
rows[index];
public int IndexOf(IBlueRow item) =>
idToIndex.TryGetValue(item.Id(), out var idx) && BlueRowEqualityComparer.Equals(item, rows[idx])
? idx
: -1;
public class Row : IBlueRow {
readonly IBlueRow inputRow;
readonly IBlueTableSchema outputSchema;
readonly int keyColIdx;
public Row(TKey key, IBlueRow inputRow, IBlueTableSchema outputSchema, int keyColIdx = -1) {
Key = key;
this.inputRow = inputRow;
this.outputSchema = outputSchema;
this.keyColIdx = keyColIdx;
}
public object? this[int index] {
get {
if (index == keyColIdx) {
return Key;
}
return inputRow[index];
}
}
public TKey Key { get; }
public IBlueTableSchema schema => outputSchema;
}
}
@@ -0,0 +1,229 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
public class ImmutableOrderedTable : IBlueRowCollection {
readonly IBlueTableSchema schema;
readonly ImmutableSortedSet<KeyValuePair<long, IBlueRow>> rows;
readonly IReadOnlyDictionary<long, IBlueRow> ordinalToRow;
readonly ImmutableDictionary<int, long> idToOrdinal;
long newRowOrdinal = 0;
public ImmutableOrderedTable(IBlueTableSchema schema)
: this(
schema,
ImmutableSortedSet.Create(KeyValuePairComparer.KeyComparer<long, IBlueRow>()),
ImmutableDictionary<int, long>.Empty, 0) { }
ImmutableOrderedTable(IBlueTableSchema schema, ImmutableSortedSet<KeyValuePair<long, IBlueRow>> rows, ImmutableDictionary<int, long> idToOrdinal, long newRowOrdinal) {
this.schema = schema;
this.rows = rows;
this.idToOrdinal = idToOrdinal;
this.newRowOrdinal = newRowOrdinal;
ordinalToRow = ImmutableSortedSetDictionary.Create(rows);
}
public static OrderedTable FromRowCollection(IBlueRowCollection rowCollection) {
var rowList = new List<IBlueRow>(rowCollection.Count);
var idToIndex = new Dictionary<int, int>();
foreach (var r in rowCollection) {
idToIndex[r.Id()] = rowList.Count;
rowList.Add(r);
}
return new(rowCollection.schema, rowList, idToIndex);
}
public IBlueTableSchema Schema => schema;
public int Count => rows.Count;
public IBlueRow this[int id] =>
idToOrdinal.TryGetValue(id, out var ord) ? ordinalToRow[ord] : throw new KeyNotFoundException();
IBlueTableSchema IBlueRowCollection.schema => schema;
public bool containsID(int id) =>
idToOrdinal.ContainsKey(id);
public IEnumerator<IBlueRow> GetEnumerator() {
foreach (var (_, row) in rows) {
yield return row;
}
}
public IBlueRow GetRow(int index) =>
rows[index].Value;
public int IndexOf(IBlueRow item) =>
idToOrdinal.TryGetValue(item.Id(), out var ord) ? rows.IndexOf(new(ord, null!)) : -1;
public Builder ToBuilder() =>
new(schema, rows, idToOrdinal, newRowOrdinal);
public class Builder : IBlueRowCollection {
readonly IBlueTableSchema schema;
ImmutableSortedSet<KeyValuePair<long, IBlueRow>> originalRows;
ImmutableSortedSet<KeyValuePair<long, IBlueRow>>.Builder rows;
ImmutableDictionary<int, long> originalIdToOrdinal;
ImmutableDictionary<int, long>.Builder idToOrdinal;
IReadOnlyDictionary<long, IBlueRow> originalOrdinalToRow;
IReadOnlyDictionary<long, IBlueRow> ordinalToRow;
readonly HashSet<int> removes = new();
readonly HashSet<int> adds = new();
long newRowOrdinal;
public Builder(IBlueTableSchema schema, ImmutableSortedSet<KeyValuePair<long, IBlueRow>> rows, ImmutableDictionary<int, long> idToOrdinal, long newRowOrdinal) {
this.schema = schema;
originalRows = rows;
this.rows = rows.ToBuilder();
originalIdToOrdinal = idToOrdinal;
this.idToOrdinal = idToOrdinal.ToBuilder();
originalOrdinalToRow = ImmutableSortedSetDictionary.Create(originalRows);
ordinalToRow = ImmutableSortedSetDictionary.CreateBuilder(this.rows);
this.newRowOrdinal = newRowOrdinal;
}
public IBlueRow this[int id] {
get {
if (idToOrdinal.TryGetValue(id, out var ord) && !removes.Contains(id)) {
return ordinalToRow[ord];
}
throw new KeyNotFoundException();
}
}
public int Count => rows.Count + adds.Count - removes.Count;
IBlueTableSchema IBlueRowCollection.schema => schema;
public void Add(IBlueRow row) =>
addOrUpdate(row, false);
public void AddOrUpdate(IBlueRow row) =>
addOrUpdate(row, true);
public bool containsID(int id) =>
(idToOrdinal.ContainsKey(id) && !removes.Contains(id)) || adds.Contains(id);
public IEnumerator<IBlueRow> GetEnumerator() {
foreach (var row in rows) {
if (!removes.Contains(row.Value.Id())) {
yield return row.Value;
}
}
}
public IBlueRow GetRow(int index) =>
rows[index].Value;
public int IndexOf(IBlueRow item) {
if (idToOrdinal.TryGetValue(item.Id(), out var ord)) {
return rows.IndexOf(new(ord, item));
}
return -1;
}
public bool Remove(int id) {
if (!idToOrdinal.TryGetValue(id, out var ord)) {
return false;
}
idToOrdinal.Remove(id);
Log.assert(rows.Remove(new(ord, null!)));
Log.assert(ordinalToRow.Count == rows.Count);
adds.Remove(id);
if (originalIdToOrdinal.ContainsKey(id)) {
removes.Add(id);
}
return true;
}
bool ICollection<IBlueRow>.Remove(IBlueRow item) {
int id = item.Id();
if (!idToOrdinal.TryGetValue(id, out var ord) || !BlueRowEqualityComparer.Equals(item, ordinalToRow[ord])) {
return false;
}
rows.Remove(new(ord, null!));
adds.Remove(id);
if (originalIdToOrdinal.ContainsKey(id)) {
removes.Add(id);
}
return true;
}
public ImmutableOrderedTable ToImmutable(out IDataPacket dp) {
var newRows = new ImmutableOrderedTable(schema, rows.ToImmutable(), idToOrdinal.ToImmutable(), newRowOrdinal);
dp = new DataPacket(
newRows,
new BlueTableListWrapper(schema, adds.Select(id => ordinalToRow[idToOrdinal[id]]).ToList()),
new BlueTableListWrapper(schema, removes.Select(id => originalOrdinalToRow[originalIdToOrdinal[id]]).ToList()));
return newRows;
}
void addOrUpdate(IBlueRow row, bool canUpdate) {
if (newRowOrdinal == long.MaxValue) {
resetOrdinal();
}
int id = row.Id();
if (idToOrdinal.TryGetValue(id, out var ord)) {
if (!canUpdate) {
throw new ArgumentException("Row id already present", nameof(row));
}
rows.Remove(new(ord, row));
}
ord = newRowOrdinal++;
idToOrdinal[id] = ord;
if (originalIdToOrdinal.ContainsKey(id)) {
removes.Add(id);
}
rows.Add(new(ord, row));
adds.Add(id);
}
void resetOrdinal() {
Log.DataFlow.warn($$"""[immutable_ordered_table_reset_ordinal.json]{ "tableName": "{{schema.tableName}}", "origNumRows": {{originalRows.Count}}, "numRows": {{rows.Count}}, "ts": {{DateTime.Now}} }""");
var originalRowsBd = originalRows.Clear().ToBuilder();
var originalIdToOrdinalBd = originalIdToOrdinal.Clear().ToBuilder();
var newRowsBd = ImmutableSortedSet.CreateBuilder(rows.KeyComparer);
var newIdToOrdinalBd = ImmutableDictionary.CreateBuilder<int, long>(idToOrdinal.KeyComparer);
long ord = 0;
long? prevRowOrd = null;
foreach (var kv in originalRows) {
if (prevRowOrd.HasValue) {
Log.assert(prevRowOrd.Value < kv.Key);
}
prevRowOrd = kv.Key;
originalRowsBd.Add(new(ord, kv.Value));
originalIdToOrdinalBd.Add(new(kv.Value.Id(), ord));
ord++;
}
prevRowOrd = null;
foreach (var kv in rows) {
if (prevRowOrd.HasValue) {
Log.assert(prevRowOrd.Value < kv.Key);
}
prevRowOrd = kv.Key;
newRowsBd.Add(new(ord, kv.Value));
newIdToOrdinalBd.Add(new(kv.Value.Id(), ord));
ord++;
}
rows = newRowsBd;
originalRows = originalRowsBd.ToImmutable();
originalIdToOrdinal = originalIdToOrdinalBd.ToImmutable();
idToOrdinal = newIdToOrdinalBd;
originalOrdinalToRow = ImmutableSortedSetDictionary.Create(originalRows);
ordinalToRow = ImmutableSortedSetDictionary.CreateBuilder(rows);
newRowOrdinal = ord;
}
}
}
@@ -0,0 +1,23 @@
namespace bts.BlueOrb;
using BCG;
public class IndexRow : IBlueRow {
readonly IBlueRow inputRow;
readonly int id;
readonly int idIndex;
public IndexRow(IBlueRow inputRow, int id, int idIndex, IBlueTableSchema schema) {
this.inputRow = inputRow;
this.id = id;
this.idIndex = idIndex;
this.schema = schema;
}
public IBlueRow InputRow => inputRow;
public IBlueTableSchema schema { get; }
public object? this[int index] =>
index == idIndex ? id : inputRow[index];
}
@@ -0,0 +1,53 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStream;
using Bts.DataStreamCore;
using Spark.Core.Disposables;
using System;
using System.Collections.Immutable;
public class IndexStream : ViewStream {
readonly ViewStream inputStream;
ImmutableDictionary<int, int> inputToOutputIds = ImmutableDictionary<int, int>.Empty;
ImmutableDictionary<int, int> outputToInputIds = ImmutableDictionary<int, int>.Empty;
int idCounter = 0;
IDisposable? sub;
public IndexStream(ViewStream inputStream) =>
this.inputStream = inputStream;
~IndexStream() =>
Disposable.dispose(ref sub);
protected override void myOnFirstSubscriber() {
base.myOnFirstSubscriber();
sub = inputStream.Subscribe(OnInputChanged);
void OnInputChanged(IDataPacket dp) {
var inputToOutputIdsBd = inputToOutputIds.ToBuilder();
var outputToInputIdsBd = outputToInputIds.ToBuilder();
foreach (var r in dp.addedRows) {
int inputId = r.Id();
if (!inputToOutputIds.TryGetValue(inputId, out int outputId)) {
outputId = ++idCounter;
inputToOutputIdsBd[inputId] = outputId;
outputToInputIdsBd[outputId] = inputId;
}
}
inputToOutputIds = inputToOutputIdsBd.ToImmutable();
outputToInputIds = outputToInputIdsBd.ToImmutable();
myFireEvent(new DataPacket(
new IndexTable(dp.newRows, outputToInputIds, inputToOutputIds),
new IndexTable(dp.addedRows, outputToInputIds, inputToOutputIds),
new IndexTable(dp.deletedRows, outputToInputIds, inputToOutputIds)));
}
}
protected override void myOnLastSubscriber() =>
Disposable.dispose(ref sub);
}
@@ -0,0 +1,93 @@
namespace bts.BlueOrb;
using BCG;
using Bts.DataStreamCore;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
public class IndexTable : IBlueRowCollection {
readonly IBlueRowCollection inputRows;
readonly IDictionary<int, int> outputToInputIdMap;
readonly IDictionary<int, int> inputToOutputIdMap;
readonly int idColIndex;
public IndexTable(IBlueRowCollection inputRows, IDictionary<int, int> outputToInputIdMap, IDictionary<int, int> inputToOutputIdMap) {
this.inputRows = inputRows;
this.outputToInputIdMap = outputToInputIdMap;
this.inputToOutputIdMap = inputToOutputIdMap;
if (!inputRows.schema.columnInfo.TryGetFirst(c => c.colName == "id", out var idColInfo)) {
throw new ArgumentException("No 'id' column", nameof(inputRows));
}
idColIndex = idColInfo.colNo;
}
public IBlueTableSchema schema => inputRows.schema;
public int Count => inputRows.Count;
public IBlueRow this[int id] =>
outputToInputIdMap.TryGetValue(id, out var inputId)
? new IndexRow(inputRows[inputId], id, idColIndex, schema)
: throw new KeyNotFoundException();
IBlueRow IList<IBlueRow>.this[int index] {
get => GetRow(index);
set => throw new NotSupportedException();
}
public static IndexTable Create(IBlueRowCollection inputRows) {
var outputToInputIdMap = new Dictionary<int, int>();
var inputToOutputIdMap = new Dictionary<int, int>();
foreach (var r in inputRows) {
int inputId = r.Id();
int outputId = outputToInputIdMap.Count + 1;
outputToInputIdMap[outputId] = inputId;
inputToOutputIdMap[inputId] = outputId;
}
return new(inputRows, outputToInputIdMap, inputToOutputIdMap);
}
public static BlueTableImmutable CreateImmutable(IBlueTableSchema schema, IEnumerable<IBlueRow> rows) {
int idIndex = schema.ResolveIndices("id");
var rowSetBd = ImmutableSortedSet.Create(KeyValuePairComparer.KeyComparer<int, IBlueRow>()).ToBuilder();
foreach (var r in rows) {
int idx = rowSetBd.Count + 1;
var inputRow = (r as IndexRow)?.InputRow ?? r;
rowSetBd.Add(new(idx, new IndexRow(inputRow, idx, idIndex, schema)));
}
return new(schema, rowSetBd.ToImmutable());
}
public bool containsID(int id) =>
inputRows.containsID(id);
public IEnumerator<IBlueRow> GetEnumerator() {
foreach (var r in inputRows) {
yield return new IndexRow(r, inputToOutputIdMap[r.Id()], idColIndex, schema);
}
}
public IBlueRow GetRow(int index) {
var r = inputRows.GetRow(index);
return new IndexRow(r, inputToOutputIdMap[r.Id()], idColIndex, schema);
}
public int IndexOf(IBlueRow item) {
int id = item.Id();
if (outputToInputIdMap.TryGetValue(id, out var inputId)) {
var inputRow = inputRows[inputId];
var r = new IndexRow(inputRow, id, idColIndex, schema);
if (BlueRowEqualityComparer.Equals(r, item)) {
var idx = inputRows.IndexOf(inputRow);
Log.assert(idx >= 0);
return idx;
}
}
return -1;
}
}
@@ -0,0 +1,196 @@
namespace bts.BlueOrb {
using BCG;
using RowHash = System.Int32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Data.Entity.Core.Mapping;
[DataStreamUserRespresentation("JoinStream")]
public class SimpleJoinStream : ViewStream {
public class JoinBookkeepingData {
// initialized in cons
private readonly List<string> _join_col_names;
private readonly bool _is_left;
private readonly BlueRowHasher _hasher;
// initialized on first udpate
private IBlueTableSchema _schema;
private int[] _join_cols;
private IDictionary<RowHash, List<IBlueRow>> _hash_row_map = new Dictionary<RowHash, List<IBlueRow>>();
public JoinBookkeepingData(ViewStream vs, List<string> join_cols, bool is_left, BlueRowHasher row_hasher) {
_join_col_names = join_cols.Where(c => !String.IsNullOrEmpty(c)).ToList();
_is_left = is_left;
_hasher = row_hasher;
}
public IBlueTableSchema schema => _schema;
public IDictionary<RowHash, List<IBlueRow>> rows => _hash_row_map;
public List<IBlueRow> get_matching_rows(int hash) {
List<IBlueRow> rv = null;
_hash_row_map.TryGetValue(hash, out rv);
return rv;
}
private RowHash _get_hash(IBlueRow r) {
if (_join_cols == null || _join_cols.Length == 0) {
return 0;
}
return _hasher.get_hash(r, _join_cols);
}
public void update_data(DataPacket dp) {
if (!ready) {
_schema = dp.newRows.schema;
_join_cols = _join_col_names.Count == 0 ? new int[] { } : _join_col_names.Select(n => dp.schema[n].colNo).ToArray();
}
_hash_row_map.Clear();
foreach (IBlueRow r in dp.newRows) {
int hash = _get_hash(r);
List<IBlueRow> hash_rows = null;
if (!_hash_row_map.TryGetValue(hash, out hash_rows)) {
hash_rows = new List<IBlueRow>();
_hash_row_map.Add(hash, hash_rows);
}
hash_rows.Add(r);
}
}
public bool ready {
get {
return _schema != null;
}
}
}
private ViewStream _ls, _rs;
private JoinBookkeepingData _ldata, _rdata;
private ViewStreamDatabase _vsd;
private IBlueTableSchema _schema = null;
public SimpleJoinStream(ViewStream ls, string ljc, ViewStream rs, string rjc) :
this(ViewStreamDatabase.instance, ls, ljc, rs, rjc) { }
public SimpleJoinStream(ViewStreamDatabase vsd, ViewStream ls, string ljc, ViewStream rs, string rjc) {
_ls = ls;
_rs = rs;
List<string> lj_col_list = ljc.Split(',').Where(x => !String.IsNullOrEmpty(x)).ToList();
List<string> rj_col_list = rjc.Split(',').Where(x => !String.IsNullOrEmpty(x)).ToList();
Log.assert(lj_col_list.Count == rj_col_list.Count);
int n_cols = lj_col_list.Count;
BlueRowHasher hasher = new BlueRowHasher(n_cols);
_ldata = new JoinBookkeepingData(ls, lj_col_list, true, hasher);
_rdata = new JoinBookkeepingData(rs, rj_col_list, false, hasher);
}
protected override void myOnFirstSubscriber() {
_ls.onChange += _ls_onchange;
_rs.onChange += _rs_onchange;
}
private void _ls_onchange(object dp_obj) {
DataPacket dp = dp_obj as DataPacket;
_ldata.update_data(dp);
_form_and_fire();
}
private void _rs_onchange(object dp_obj) {
DataPacket dp = dp_obj as DataPacket;
_rdata.update_data(dp);
_form_and_fire();
}
protected override void myOnLastSubscriber() {
_ls.onChange -= _ls_onchange;
_rs.onChange -= _rs_onchange;
}
private void _build_schema() {
if (_schema != null)
return;
List<ColumnInfo> cis = new List<ColumnInfo>();
cis.Add(new ColumnInfo("id", 0, TypeCode.Int32));
foreach (ColumnInfo ci in _ldata.schema.columnInfo) {
string col_name = _rdata.schema.contains_column(ci.colName) ? $"left_{ci.colName}" : ci.colName;
cis.Add(new ColumnInfo(col_name, cis.Count, ci.colType));
}
foreach (ColumnInfo ci in _rdata.schema.columnInfo) {
string col_name = _ldata.schema.contains_column(ci.colName) ? $"right_{ci.colName}" : ci.colName;
cis.Add(new ColumnInfo(col_name, cis.Count, ci.colType));
}
_schema = new BlueTableSchema($"{_ldata.schema.tableName}_join_{_rdata.schema.tableName}", cis.ToArray());
}
private void _form_and_fire() {
if (!_ldata.ready || !_rdata.ready)
return;
_build_schema();
List<object[]> res_rows = new List<object[]>();
Action<object[], IBlueRow, bool> copy_row = (rval, r, is_left) => {
int offset = 1;
JoinBookkeepingData sdata = _ldata;
if (is_left) {
offset = 1;
sdata = _ldata;
} else {
sdata = _rdata;
offset = 1 + _ldata.schema.columnInfo.Length;
}
for (int i = 0; i < sdata.schema.columnInfo.Length; i++) {
rval[offset + i] = r[i];
}
};
Func<int, IBlueRow, object[]> build_row_array = (id, l_row) => {
object[] rv = new object[_schema.columnNames.Length];
for (int i = 0; i < _schema.columnNames.Length; i++) {
rv[i] = DBNull.Value;
}
rv[0] = id;
copy_row(rv, l_row, true);
return rv;
};
// this is where the join happens
foreach (var kv in _ldata.rows) {
RowHash hash = kv.Key;
List<IBlueRow> l_rows = kv.Value;
List<IBlueRow> r_rows = _rdata.get_matching_rows(hash);
foreach (IBlueRow lr in l_rows) {
if (r_rows != null) {
foreach (IBlueRow r in r_rows) {
object[] rowvals = build_row_array(res_rows.Count, lr);
copy_row(rowvals, r, false);
res_rows.Add(rowvals);
}
} else {
object[] rowvals = build_row_array(res_rows.Count, lr);
res_rows.Add(rowvals);
}
}
}
BlueTable rv = new BlueTable(_schema);
foreach (object[] rval in res_rows) {
rv.addRow(new BlueRow(_schema, rval));
}
var dp = BlueDataUtility.build_dp(rv, myLastValue as DataPacket);
myFireEvent(dp);
}
}
[DataStreamUserRespresentation("FullJoinStream")]
public class FullJoinStream : ViewStream {
public FullJoinStream(ViewStream l_str, string ljcs, ViewStream r_str, string rjcs) {
ViewStream js = new SimpleJoinStream(l_str, ljcs, r_str, rjcs);
ViewStream rs = new bts.BlueOrb.FilterStream(js, r => !DBNull.Value.Equals(r["right_id"]));
mySetRealSource(rs);
}
}
}
@@ -0,0 +1,28 @@
//namespace bts.Blueorb {
// using BCG;
// public class JsonSaverStream : ViewStream {
// private ViewStream _stream;
// private string _file_name;
// public JsonSaverStream(ViewStream vs, string file_name) {
// _stream = vs;
// _file_name = file_name;
// }
// protected override void myOnFirstSubscriber() {
// _stream.onChange += _on_data;
// }
// private void _on_data(object dp_obj) {
// if (dp_obj == null) {
// return;
// }
// DataPacket dp = dp_obj as DataPacket;
// dp.newRows.write_to_json_file(dp.newRows, _file_name);
// }
// protected override void myOnLastSubscriber() {
// _stream.onChange -= _on_data;
// }
// }
//}
@@ -0,0 +1,91 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Amazon.Runtime.Internal.Util;
using BCG;
using BCG.Utilities;
using bts;
namespace bts.BlueOrb{
public class LiteralStream : ViewStream {
public static List<Tuple<ColumnInfo, object>> parse_columns(string key_value_pairs) {
List<Tuple<ColumnInfo, object>> rv = new List<Tuple<ColumnInfo, object>>();
string[] pairs = key_value_pairs.Split(' ');
foreach (string p in pairs) {
string[] kvp = p.Split(':');
string col_name = kvp[0];
string val = kvp[1];
var tup = BlueDataUtility.guess_type(val);
var ci = new ColumnInfo(col_name, rv.Count, tup.Item1);
rv.Add(Tuple.Create(ci, tup.Item2));
}
return rv;
}
//literal("table1", "x:123, y:1.001, z:abc;x:123, y:1.001, z:abc")
// col_name: col_value ... ; col_name: col_value
public LiteralStream(string table_name, string rows) {
Func<string, int, IBlueRow> get_row = (json_row, id) => {
Dictionary<string, object> map = new Dictionary<string, object>();
string[] toks = json_row.Split(',', StringSplitOptions.TrimEntries);
//Logger.assert(col_vals.Length == 2);
foreach (string t in toks) {
var col_vals = t.Split(':', StringSplitOptions.TrimEntries);
string col = col_vals[0];
string val = col_vals[1];
map.Add(col, val);
}
map["id"] = id;
return new BlueRow(map);
};
string[] row_arr = rows.Split(';', StringSplitOptions.TrimEntries);
BlueTable table = null;
foreach (string json_string in row_arr) {
IBlueRow r = //BlueDataUtility.convert_json_to_row(json_string, table == null ? 0 : table.Count);
get_row(json_string, table == null ? 0 : table.Count);
table = table ?? new BlueTable(r.schema);
table.addRow(r);
}
ViewStream str = new BlueTableStream(table);
str = new SchemaGuesserStream(str);
mySetRealSource(str);
}
public LiteralStream(string tableName, string schema, string value) {
string[] columns = schema.Split(' ');
ColumnInfo[] ci = new ColumnInfo[columns.Length + 1];
ci[0] = new ColumnInfo("id", 0, typeof(int));
int n = 1;
foreach (string c in columns) {
string[] columnAndType = c.Split('/');
if (columnAndType.Length == 1)
ci[n] = new ColumnInfo(c, n, typeof(double));
else
ci[n] = new ColumnInfo(columnAndType[0], n, Type.GetType("System." + columnAndType[1], true));
n++;
}
BlueTable t = new BlueTable(new BlueTableSchema(tableName, ci));
string[] rows = value.Split(';');
n = 0;
foreach (string r in rows)
try {
if (r == string.Empty)
continue;
t.addRow(
BlueDataUtility.createRowFromCSVFileRow(r, t.schema, ++n));
} catch (Exception e) {
throw (new Exception("Error occured at line: " + n.ToString() + " " + e.Message));
}
mySetRealSource(new BlueRowCollectionStaticStream(t));
}
}
}
Loaded 100 of 2573 files, more files were not shown because too many files have changed in this diff. Show more