diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 1c6ecb9..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_4_14.xml b/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_4_14.xml deleted file mode 100644 index ac6986a..0000000 --- a/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_4_14.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__ch_qos_logback_logback_core_1_4_14.xml b/.idea/libraries/Maven__ch_qos_logback_logback_core_1_4_14.xml deleted file mode 100644 index 7a38623..0000000 --- a/.idea/libraries/Maven__ch_qos_logback_logback_core_1_4_14.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_classmate_1_6_0.xml b/.idea/libraries/Maven__com_fasterxml_classmate_1_6_0.xml deleted file mode 100644 index fb8a548..0000000 --- a/.idea/libraries/Maven__com_fasterxml_classmate_1_6_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_15_3.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_15_3.xml deleted file mode 100644 index b194d9d..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_15_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_15_3.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_15_3.xml deleted file mode 100644 index b1af432..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_15_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_15_3.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_15_3.xml deleted file mode 100644 index 86cd658..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_15_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_15_3.xml b/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_15_3.xml deleted file mode 100644 index bffd290..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_15_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_15_3.xml b/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_15_3.xml deleted file mode 100644 index 50fc069..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_15_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_15_3.xml b/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_15_3.xml deleted file mode 100644 index 65bce52..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_15_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__io_micrometer_micrometer_commons_1_12_2.xml b/.idea/libraries/Maven__io_micrometer_micrometer_commons_1_12_2.xml deleted file mode 100644 index cf8ca3f..0000000 --- a/.idea/libraries/Maven__io_micrometer_micrometer_commons_1_12_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__io_micrometer_micrometer_observation_1_12_2.xml b/.idea/libraries/Maven__io_micrometer_micrometer_observation_1_12_2.xml deleted file mode 100644 index 54e7a97..0000000 --- a/.idea/libraries/Maven__io_micrometer_micrometer_observation_1_12_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__jakarta_annotation_jakarta_annotation_api_2_1_1.xml b/.idea/libraries/Maven__jakarta_annotation_jakarta_annotation_api_2_1_1.xml deleted file mode 100644 index 3f660ae..0000000 --- a/.idea/libraries/Maven__jakarta_annotation_jakarta_annotation_api_2_1_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__jakarta_validation_jakarta_validation_api_3_0_2.xml b/.idea/libraries/Maven__jakarta_validation_jakarta_validation_api_3_0_2.xml deleted file mode 100644 index 96ee2a5..0000000 --- a/.idea/libraries/Maven__jakarta_validation_jakarta_validation_api_3_0_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_21_1.xml b/.idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_21_1.xml deleted file mode 100644 index ba17f0e..0000000 --- a/.idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_21_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_21_1.xml b/.idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_21_1.xml deleted file mode 100644 index 30065dd..0000000 --- a/.idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_21_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_core_10_1_18.xml b/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_core_10_1_18.xml deleted file mode 100644 index 25acdc6..0000000 --- a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_core_10_1_18.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_el_10_1_18.xml b/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_el_10_1_18.xml deleted file mode 100644 index b3b3c18..0000000 --- a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_el_10_1_18.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_websocket_10_1_18.xml b/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_websocket_10_1_18.xml deleted file mode 100644 index ff7564a..0000000 --- a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_websocket_10_1_18.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_2.xml b/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_2.xml deleted file mode 100644 index 6ac1c42..0000000 --- a/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_hibernate_validator_hibernate_validator_8_0_1_Final.xml b/.idea/libraries/Maven__org_hibernate_validator_hibernate_validator_8_0_1_Final.xml deleted file mode 100644 index 69ad872..0000000 --- a/.idea/libraries/Maven__org_hibernate_validator_hibernate_validator_8_0_1_Final.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_jboss_logging_jboss_logging_3_5_3_Final.xml b/.idea/libraries/Maven__org_jboss_logging_jboss_logging_3_5_3_Final.xml deleted file mode 100644 index d63874a..0000000 --- a/.idea/libraries/Maven__org_jboss_logging_jboss_logging_3_5_3_Final.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_10_0.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_10_0.xml deleted file mode 100644 index 44d38c7..0000000 --- a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_10_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_10_1.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_10_1.xml deleted file mode 100644 index 3135587..0000000 --- a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_10_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_10_1.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_10_1.xml deleted file mode 100644 index 68b7903..0000000 --- a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_10_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_10_1.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_10_1.xml deleted file mode 100644 index dad8df0..0000000 --- a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_10_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_10_1.xml b/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_10_1.xml deleted file mode 100644 index a0f728d..0000000 --- a/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_10_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_10_1.xml b/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_10_1.xml deleted file mode 100644 index 90b8509..0000000 --- a/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_10_1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_opentest4j_opentest4j_1_3_0.xml b/.idea/libraries/Maven__org_opentest4j_opentest4j_1_3_0.xml deleted file mode 100644 index 87e7ec8..0000000 --- a/.idea/libraries/Maven__org_opentest4j_opentest4j_1_3_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_projectlombok_lombok_1_18_30.xml b/.idea/libraries/Maven__org_projectlombok_lombok_1_18_30.xml deleted file mode 100644 index 291270e..0000000 --- a/.idea/libraries/Maven__org_projectlombok_lombok_1_18_30.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_projectlombok_lombok_1_18_42.xml b/.idea/libraries/Maven__org_projectlombok_lombok_1_18_42.xml deleted file mode 100644 index 0a48dda..0000000 --- a/.idea/libraries/Maven__org_projectlombok_lombok_1_18_42.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_slf4j_jul_to_slf4j_2_0_11.xml b/.idea/libraries/Maven__org_slf4j_jul_to_slf4j_2_0_11.xml deleted file mode 100644 index 01d6d92..0000000 --- a/.idea/libraries/Maven__org_slf4j_jul_to_slf4j_2_0_11.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_slf4j_slf4j_api_2_0_11.xml b/.idea/libraries/Maven__org_slf4j_slf4j_api_2_0_11.xml deleted file mode 100644 index 893675a..0000000 --- a/.idea/libraries/Maven__org_slf4j_slf4j_api_2_0_11.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_3_2_2.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_3_2_2.xml deleted file mode 100644 index b90568e..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_3_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_autoconfigure_3_2_2.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_autoconfigure_3_2_2.xml deleted file mode 100644 index 28aabc8..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_autoconfigure_3_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_3_2_2.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_3_2_2.xml deleted file mode 100644 index a8563e6..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_3_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_json_3_2_2.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_json_3_2_2.xml deleted file mode 100644 index 8a26ba4..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_json_3_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_logging_3_2_2.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_logging_3_2_2.xml deleted file mode 100644 index 4b4c5f7..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_logging_3_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_tomcat_3_2_2.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_tomcat_3_2_2.xml deleted file mode 100644 index df7e906..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_tomcat_3_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_validation_3_2_2.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_validation_3_2_2.xml deleted file mode 100644 index a227960..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_validation_3_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_web_3_2_2.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_web_3_2_2.xml deleted file mode 100644 index d801436..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_web_3_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_aop_6_1_3.xml b/.idea/libraries/Maven__org_springframework_spring_aop_6_1_3.xml deleted file mode 100644 index d0f8376..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_aop_6_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_beans_6_1_3.xml b/.idea/libraries/Maven__org_springframework_spring_beans_6_1_3.xml deleted file mode 100644 index ad680b1..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_beans_6_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_context_6_1_3.xml b/.idea/libraries/Maven__org_springframework_spring_context_6_1_3.xml deleted file mode 100644 index d1f8c22..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_context_6_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_core_6_1_3.xml b/.idea/libraries/Maven__org_springframework_spring_core_6_1_3.xml deleted file mode 100644 index 95226e9..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_core_6_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_expression_6_1_3.xml b/.idea/libraries/Maven__org_springframework_spring_expression_6_1_3.xml deleted file mode 100644 index 822f494..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_expression_6_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_jcl_6_1_3.xml b/.idea/libraries/Maven__org_springframework_spring_jcl_6_1_3.xml deleted file mode 100644 index 526a8ca..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_jcl_6_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_web_6_1_3.xml b/.idea/libraries/Maven__org_springframework_spring_web_6_1_3.xml deleted file mode 100644 index d30a176..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_web_6_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_webmvc_6_1_3.xml b/.idea/libraries/Maven__org_springframework_spring_webmvc_6_1_3.xml deleted file mode 100644 index 6d24fc7..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_webmvc_6_1_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_yaml_snakeyaml_2_2.xml b/.idea/libraries/Maven__org_yaml_snakeyaml_2_2.xml deleted file mode 100644 index 998e133..0000000 --- a/.idea/libraries/Maven__org_yaml_snakeyaml_2_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 03f397c..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index e03607b..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/InMemoryWalletService.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/InMemoryWalletService.class" deleted file mode 100644 index 9dbce51..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/InMemoryWalletService.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MarketController.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MarketController.class" deleted file mode 100644 index 01508e2..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MarketController.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngine.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngine.class" deleted file mode 100644 index e303a3f..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngine.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngineApplication.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngineApplication.class" deleted file mode 100644 index 9d9c000..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngineApplication.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngineConfig.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngineConfig.class" deleted file mode 100644 index 8566c3b..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/MatchingEngineConfig.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/OrderService.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/OrderService.class" deleted file mode 100644 index db23766..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/OrderService.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/Reservation.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/Reservation.class" deleted file mode 100644 index 79bf7e0..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/Reservation.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/WalletController.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/WalletController.class" deleted file mode 100644 index 5d8e922..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/WalletController.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/WalletService.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/WalletService.class" deleted file mode 100644 index e4c2454..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/WalletService.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/shelved.patch" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/shelved.patch" deleted file mode 100644 index d6a1148..0000000 --- "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]/shelved.patch" +++ /dev/null @@ -1,805 +0,0 @@ -Index: matching-engine/src/main/java/org/example/matching/MatchingEngineApplication.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MatchingEngineApplication {\n public static void main(String[] args) {\n SpringApplication.run(MatchingEngineApplication.class, args);\n }\n}\n -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/MatchingEngineApplication.java b/matching-engine/src/main/java/org/example/matching/MatchingEngineApplication.java ---- a/matching-engine/src/main/java/org/example/matching/MatchingEngineApplication.java -+++ b/matching-engine/src/main/java/org/example/matching/MatchingEngineApplication.java -@@ -6,6 +6,7 @@ - @SpringBootApplication - public class MatchingEngineApplication { - public static void main(String[] args) { -+ - SpringApplication.run(MatchingEngineApplication.class, args); - } - } -Index: matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching.api.service;\n\nimport lombok.RequiredArgsConstructor;\nimport org.example.matching.api.dto.OrderRequest;\nimport org.springframework.stereotype.Service;\n\n@Service\n@RequiredArgsConstructor\npublic class LiquidBotService {\n private final OrderService orderService;\n public void seedMarket(String yesTicker, String noTicker) {\n // Providing liquidity for YES\n placeBotOrder(yesTicker, \"BUY\", 45, 1000); // Bot buys from users at 45c\n placeBotOrder(yesTicker, \"SELL\", 55, 1000); // Bot sells to users at 55c\n\n // Providing liquidity for NO\n placeBotOrder(noTicker, \"BUY\", 45, 1000); // Bot buys from users at 45c\n placeBotOrder(noTicker, \"SELL\", 55, 1000); // Bot sells to users at 55c\n }\n private void placeBotOrder(String ticker, String side, long price, long qty){\n OrderRequest req = new OrderRequest();\n req.setUserId(\"HOUSE_BOT\");\n req.setInstrument(ticker);\n req.setSide(side);\n req.setPrice(price);\n req.setQuantity(qty);\n orderService.processOrder(req);\n }\n} -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java b/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java ---- a/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java -+++ b/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java -@@ -1,29 +1,84 @@ - package org.example.matching.api.service; - - import lombok.RequiredArgsConstructor; -+import org.example.matching.Wallets.WalletService; -+import org.example.matching.api.dto.MarketEvent; - import org.example.matching.api.dto.OrderRequest; -+import org.example.matching.api.service.OrderService; -+import org.example.matching.matching.MatchingEngine; - import org.springframework.stereotype.Service; - -+import java.util.concurrent.atomic.AtomicLong; -+ - @Service - @RequiredArgsConstructor - public class LiquidBotService { - private final OrderService orderService; -- public void seedMarket(String yesTicker, String noTicker) { -- // Providing liquidity for YES -- placeBotOrder(yesTicker, "BUY", 45, 1000); // Bot buys from users at 45c -- placeBotOrder(yesTicker, "SELL", 55, 1000); // Bot sells to users at 55c -+ private final WalletService walletService; -+ private final MatchingEngine matchingEngine; -+ private final MarketManagmentService marketManagmentService; -+ -+ private static final String BOT_ID = "HOUSE_BOT"; -+ private static final long INITIAL_INVENTORY = 10000; -+ private static final int SPREAD = 4; -+ -+ /** -+ * Entry point from OrderService. -+ */ -+ public void updateMarketTrigger(String currentTicker) { -+ // 1. Correctly fetch the EVENT object using the ticker -+ MarketEvent event = marketManagmentService.getEventByTicker(currentTicker); -+ if (event == null) return; -+ -+ // 2. Correctly fetch the PARTNER ticker name (String) -+ String partnerTicker = marketManagmentService.getPartnerTicker(currentTicker); -+ if (partnerTicker == null) return; - -- // Providing liquidity for NO -- placeBotOrder(noTicker, "BUY", 45, 1000); // Bot buys from users at 45c -- placeBotOrder(noTicker, "SELL", 55, 1000); // Bot sells to users at 55c -+ // 3. Identify roles and pull liquidity 'L' -+ String yesTicker = event.getYesTicker(); -+ String noTicker = event.getNoTicker(); -+ long L = event.getLiquidity(); -+ -+ recalculateAndPlaceOrders(yesTicker, noTicker, L); - } -- private void placeBotOrder(String ticker, String side, long price, long qty){ -+ -+ private void recalculateAndPlaceOrders(String yesTicker, String noTicker, long L) { -+ // Get YES Inventory -+ long yesInventory = walletService.getWallet(BOT_ID) -+ .getAvailableShares() -+ .getOrDefault(yesTicker, new AtomicLong(0)) -+ .get(); -+ -+ // KALSHI MATH: Sigmoid Curve -+ long netSold = INITIAL_INVENTORY - yesInventory; -+ -+ // Price = 100 / (1 + e^(-netSold / L)) -+ double exponent = (double) netSold / L; -+ long fairPriceYes = (long) (100.0 / (1.0 + Math.exp(-exponent))); -+ -+ fairPriceYes = Math.max(1, Math.min(99, fairPriceYes)); -+ long fairPriceNo = 100 - fairPriceYes; -+ -+ // Cleanup and Quote -+ matchingEngine.cancelAllOrdersForUser(BOT_ID, yesTicker); -+ matchingEngine.cancelAllOrdersForUser(BOT_ID, noTicker); -+ -+ placeBotOrder(yesTicker, "BUY", fairPriceYes - (SPREAD / 2), 500); -+ placeBotOrder(yesTicker, "SELL", fairPriceYes + (SPREAD / 2), 500); -+ -+ placeBotOrder(noTicker, "BUY", fairPriceNo - (SPREAD / 2), 500); -+ placeBotOrder(noTicker, "SELL", fairPriceNo + (SPREAD / 2), 500); -+ } -+ -+ private void placeBotOrder(String ticker, String side, long price, long qty) { -+ long safePrice = Math.max(1, Math.min(99, price)); - OrderRequest req = new OrderRequest(); -- req.setUserId("HOUSE_BOT"); -+ req.setUserId(BOT_ID); - req.setInstrument(ticker); - req.setSide(side); -- req.setPrice(price); -+ req.setPrice(safePrice); - req.setQuantity(qty); -+ req.setIdempotencyKey("BOT-" + ticker + "-" + side + "-" + System.nanoTime()); - orderService.processOrder(req); - } - } -\ No newline at end of file -Index: matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching.api.controller;\n\nimport lombok.RequiredArgsConstructor;\nimport org.example.matching.api.dto.OrderBookResponse;\nimport org.example.matching.api.service.MarketDataService;\nimport org.example.matching.matching.MatchingEngine;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Map;\n\n@RequestMapping(\"/api/market\")\n@RestController\n@RequiredArgsConstructor\npublic class MarketController {\n private final MatchingEngine matchingEngine;\n private final MarketDataService marketDataService;\n\n// public MarketController(MatchingEngine matchingEngine) {\n// this.matchingEngine = matchingEngine;\n// }\n\n @GetMapping(\"/orderbook/{instrument}\")\n public ResponseEntity getOrderBook(@PathVariable String instrument) {\n OrderBookResponse snapshot = matchingEngine.getSnapshot(instrument);\n return ResponseEntity.ok(snapshot);\n }\n\n @GetMapping(\"/ticker/{instrument}\")\n public ResponseEntity> getTicker(@PathVariable String instrument){\n return ResponseEntity.ok(marketDataService.getSnapshots(instrument.toUpperCase()));\n }\n}\n -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java b/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java ---- a/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java -+++ b/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java -@@ -1,15 +1,15 @@ - package org.example.matching.api.controller; - - import lombok.RequiredArgsConstructor; -+import org.example.matching.api.dto.MarketEvent; - import org.example.matching.api.dto.OrderBookResponse; - import org.example.matching.api.service.MarketDataService; -+import org.example.matching.api.service.MarketManagmentService; - import org.example.matching.matching.MatchingEngine; - import org.springframework.http.ResponseEntity; --import org.springframework.web.bind.annotation.GetMapping; --import org.springframework.web.bind.annotation.PathVariable; --import org.springframework.web.bind.annotation.RequestMapping; --import org.springframework.web.bind.annotation.RestController; -+import org.springframework.web.bind.annotation.*; - -+import java.util.List; - import java.util.Map; - - @RequestMapping("/api/market") -@@ -19,10 +19,12 @@ - private final MatchingEngine matchingEngine; - private final MarketDataService marketDataService; - -+ private final MarketManagmentService marketService; - // public MarketController(MatchingEngine matchingEngine) { - // this.matchingEngine = matchingEngine; - // } - -+ - @GetMapping("/orderbook/{instrument}") - public ResponseEntity getOrderBook(@PathVariable String instrument) { - OrderBookResponse snapshot = matchingEngine.getSnapshot(instrument); -@@ -33,4 +35,19 @@ - public ResponseEntity> getTicker(@PathVariable String instrument){ - return ResponseEntity.ok(marketDataService.getSnapshots(instrument.toUpperCase())); - } -+ @GetMapping("/active") -+ public List getActiveMarkets() { -+ // This pulls from your MarketManagementService -+ return (List) marketService.getAllOpenEvents(); -+ } -+ @PostMapping("/events/create") -+ public ResponseEntity createEvent( -+ @RequestParam String id, -+ @RequestParam String question, -+ @RequestParam String yesTicker, -+ @RequestParam String noTicker, -+ @RequestParam int minutes) { -+ return ResponseEntity.ok(marketService.createEvent(id, question, yesTicker, noTicker, minutes)); -+ } -+ - } -Index: matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>//package org.example.matching.matching;\n//\n//import org.example.matching.journal.EventJournal;\n//import org.example.matching.model.Order;\n//import org.example.matching.model.OrderBook;\n//import org.example.matching.model.Trade;\n//\n//import java.util.List;\n//\n//public class MatchingEngine {\n//\n// private final OrderBook orderBook;\n// private final EventJournal journal;\n//\n// public MatchingEngine() {\n// this.orderBook = new OrderBook();\n// this.journal = new EventJournal();\n//\n// }\n//\n// public List placeOrder(Order order) {\n//\n// journal.append(\"ORDER \" +\n// order.getId() + \" \" +\n// order.getUserId() + \" \" +\n//\n// order.getPrice() + \" \" +\n// order.getQuantity()\n// +\" \"+ order.getTimestamp() + \" \"+ order.getSide());\n//\n// List trades = orderBook.placeOrder(order);\n//\n// for (Trade trade : trades) {\n// journal.append(\"TRADE \" +\n// trade.getBuyOrderId() + \" \" +\n// trade.getSellOrderId() + \" \" +\n// trade.getPrice() + \" \" +\n// trade.getQuantity()\n// +trade.getTimestamp());\n// }\n//\n// return trades;\n// }\n//\n// public String dumpBook() {\n// return orderBook.dumpBook();\n// }\n//}\n\n\npackage org.example.matching.matching;\n\nimport jakarta.annotation.PostConstruct;\nimport org.example.matching.api.dto.OrderBookResponse;\nimport org.example.matching.api.dto.PriceLevel;\nimport org.example.matching.journal.EventJournal;\nimport org.example.matching.model.Order;\nimport org.example.matching.model.OrderBook;\nimport org.example.matching.model.Trade;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Deque;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.concurrent.ConcurrentHashMap;\n\n\n@Service\npublic class MatchingEngine {\n\n private final Map orderBooks = new ConcurrentHashMap<>();\n private final EventJournal journal;\n\n public MatchingEngine() {\n this.journal = new EventJournal();\n }\n\n @PostConstruct\n public void init() {\n // Pre-create books for stocks you want to test\n orderBooks.put(\"AAPL\", new OrderBook());\n orderBooks.put(\"TSLA\", new OrderBook());\n orderBooks.put(\"BTC\", new OrderBook());\n }\n\n // Helper method to get the right order book\n private OrderBook getOrderBook(String instrument) {\n return orderBooks.computeIfAbsent(instrument, k -> new OrderBook());\n }\n // Add this inside your MatchingEngine class\n public OrderBook getOrderBookForMarketData(String instrument) {\n return orderBooks.getOrDefault(instrument, new OrderBook());\n }\n\n // live mode records to journal; record=false used during replay\n public List placeOrder(Order order, boolean record) {\n if (record) {\n journal.appendOrder(order.getId(), order.getUserId(), order.getSide().name(), order.getPrice(), order.getQuantity(), order.getTimestamp());\n }\n\n OrderBook book = getOrderBook(order.getInstrument() != null ? order.getInstrument() : \"DEFAULT\");\n List trades = book.placeOrder(order);\n\n if (record) {\n for (Trade t : trades) {\n journal.appendTrade(t.getBuyOrderId(), t.getSellOrderId(), t.getPrice(), (int) t.getQuantity(), t.getTimestamp());\n }\n }\n return trades;\n }\n\n // convenience for normal usage\n public List placeOrder(Order order) {\n return placeOrder(order, true);\n }\n\n // used by replay to rebuild book without re-journaling\n public void replayOrder(Order order) {\n placeOrder(order, false);\n }\n\n // replay all lines from journal (optional helper)\n public void replayJournal() {\n List lines = journal.readAllLines();\n for (String raw : lines) {\n if (raw == null || raw.isBlank()) continue;\n String[] parts = raw.split(\"\\\\s+\");\n if (parts.length < 2) continue;\n if (\"ORDER\".equals(parts[0]) && parts.length >= 7) {\n // ORDER id user side price qty ts\n String id = parts[1];\n String user = parts[2];\n String side = parts[3];\n long price = Long.parseLong(parts[4]);\n int qty = Integer.parseInt(parts[5]);\n long ts = Long.parseLong(parts[6]);\n // create order via your Order constructor (check arg ordering)\n Order o = new Order(id, user, price, qty, ts, org.example.matching.model.OrderSide.valueOf(side), \"DEFAULT\");\n replayOrder(o);\n }\n // ignore TRADE lines during replay\n }\n }\n /**\n * Returns a list of price levels for a specific side (bids or asks).\n * It sums up the quantities of all orders at each price point.\n */\n private List getPriceLevels(TreeMap> side) {\n return side.entrySet().stream()\n .map(entry -> {\n long price = entry.getKey();\n // Sum the quantity of every Order sitting in the Deque for this price\n long totalQty = entry.getValue().stream()\n .mapToLong(Order::getQuantity)\n .sum();\n return new PriceLevel(price, totalQty);\n })\n .toList();\n }\n\n\n /**\n * Creates a full snapshot of the book.\n */\n public OrderBookResponse getSnapshot(String instrument) {\n OrderBook book = getOrderBook(instrument);\n return OrderBookResponse.builder()\n .instrument(instrument)\n .bids(getPriceLevels(book.getBids()))\n .asks(getPriceLevels(book.getAsks()))\n .build();\n }\n\n public String dumpBook() {\n StringBuilder sb = new StringBuilder();\n for (Map.Entry entry : orderBooks.entrySet()) {\n sb.append(\"=== ORDER BOOK: \").append(entry.getKey()).append(\" ===\\n\");\n sb.append(entry.getValue().dumpBook());\n sb.append(\"\\n\");\n }\n return sb.toString();\n }\n}\n -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java b/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java ---- a/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java -+++ b/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java -@@ -51,6 +51,10 @@ - package org.example.matching.matching; - - import jakarta.annotation.PostConstruct; -+import lombok.AllArgsConstructor; -+import lombok.NoArgsConstructor; -+import lombok.RequiredArgsConstructor; -+import org.example.matching.Wallets.WalletService; - import org.example.matching.api.dto.OrderBookResponse; - import org.example.matching.api.dto.PriceLevel; - import org.example.matching.journal.EventJournal; -@@ -59,29 +63,47 @@ - import org.example.matching.model.Trade; - import org.springframework.stereotype.Service; - --import java.util.Deque; --import java.util.List; --import java.util.Map; --import java.util.TreeMap; -+import java.util.*; - import java.util.concurrent.ConcurrentHashMap; - - - @Service - public class MatchingEngine { -- -+private final WalletService walletService; - private final Map orderBooks = new ConcurrentHashMap<>(); - private final EventJournal journal; - - public MatchingEngine() { -+ this.walletService = null; - this.journal = new EventJournal(); - } - -+ public MatchingEngine(WalletService walletService, EventJournal eventJournal) { -+ this.walletService = walletService; -+ this.journal = eventJournal; -+ } -+// public MatchingEngine() { -+// this.journal = new EventJournal(); -+// } -+ - @PostConstruct - public void init() { - // Pre-create books for stocks you want to test - orderBooks.put("AAPL", new OrderBook()); - orderBooks.put("TSLA", new OrderBook()); - orderBooks.put("BTC", new OrderBook()); -+ -+ } -+ @PostConstruct -+ public void initBot() { -+ // Only initialize bot if walletService is available (not in test mode) -+ if (walletService != null) { -+ // Give the bot enough money to buy shares from users -+ walletService.creditUserCash("HOUSE_BOT", 1000000); // $10,000.00 -+ // Give the bot enough shares to sell to users -+ walletService.creditUserShares("HOUSE_BOT", "FED_Y", 5000); -+ walletService.creditUserShares("HOUSE_BOT", "FED_N", 5000); -+ } - } - - // Helper method to get the right order book -@@ -159,7 +181,21 @@ - .toList(); - } - -+ public void cancelAllOrdersForUser(String userId, String instrument) { -+ OrderBook book = orderBooks.get(instrument); -+ if(book==null){ -+ return; -+ } -+ book.getBids().values().forEach(list-> -+ list.removeIf(order -> order.getUserId().equals(userId))); -+ book.getBids().entrySet().removeIf(entry -> entry.getValue().isEmpty()); - -+ // Remove from Asks (Sell orders) -+ book.getAsks().values().forEach(list -> -+ list.removeIf(order -> order.getUserId().equals(userId)) -+ ); -+ book.getAsks().entrySet().removeIf(entry -> entry.getValue().isEmpty()); -+ } - /** - * Creates a full snapshot of the book. - */ -@@ -172,6 +208,18 @@ - .build(); - } - -+ public List clearBook(String instrument){ -+ OrderBook book = orderBooks.get(instrument); -+ List unfulfilled = new ArrayList<>(); -+ if(book!=null){ -+ book.getBids().values().forEach(list -> unfulfilled.addAll(list)); -+ book.getAsks().values().forEach(list -> unfulfilled.addAll(list)); -+ orderBooks.remove(instrument); -+ -+ }return unfulfilled; -+ -+ } -+ - public String dumpBook() { - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : orderBooks.entrySet()) { -Index: matching-engine/src/main/java/org/example/matching/api/service/OrderService.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching.api.service;\n\nimport lombok.RequiredArgsConstructor;\nimport org.example.matching.Wallets.RiskManager;\nimport org.example.matching.Wallets.WalletService;\nimport org.example.matching.api.dto.OrderMapper;\nimport org.example.matching.api.dto.OrderRequest;\nimport org.example.matching.api.dto.OrderResponse;\nimport org.example.matching.journal.EventJournal;\nimport org.example.matching.matching.MatchingEngine;\nimport org.example.matching.model.Order;\nimport org.example.matching.model.Trade;\nimport org.example.matching.orderbook.OrderRepository;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n@Service\n@RequiredArgsConstructor\npublic class OrderService {\n\n private final MarketDataService marketDataService;\n private final RiskManager riskManager;\n private final MatchingEngine matchingEngine;\n private final WalletService walletService;\n private final EventJournal eventJournal;\n private final OrderRepository orderRepository;\n\n // In-memory idempotency store: Key -> Previous Response\n private final Map idempotencyStore = new ConcurrentHashMap<>();\n\n public OrderResponse processOrder(OrderRequest request) {\n if (idempotencyStore.containsKey(request.getIdempotencyKey())) {\n return idempotencyStore.get(request.getIdempotencyKey());\n }\n \n Order order = OrderMapper.toDomain(request);\n \n if (!riskManager.checkAndReserve(order)) {\n return buildResponse(order, \"REJECTED\", \"Insufficient funds or shares\");\n }\n\n orderRepository.save(order);\n eventJournal.appendRaw(\"ORDER_PLACED: \" + order.getId());\n\n // Matching Engine execution\n List trades = matchingEngine.placeOrder(order);\n\n for (Trade trade : trades) {\n // so once trade is done manage the cash and shares of the users using their ids and stuff in walletService below\n walletService.settleTrade(trade);\n marketDataService.UpdateTrade(order.getInstrument(),trade.getPrice(),trade.getQuantity());\n eventJournal.appendRaw(\"TRADE_SETTLED: \" + trade.getBuyOrderId() + \" <-> \" + trade.getSellOrderId());\n }\n var book = matchingEngine.getOrderBookForMarketData(order.getInstrument());\n marketDataService.updateBookTops(\n order.getInstrument(),\n book.getBestBid(),\n book.getBestAsk()\n );\n\n OrderResponse response = buildResponse(order, \"ACCEPTED\", \"Success\");\n idempotencyStore.put(request.getIdempotencyKey(), response);\n return response;\n }\n \n private OrderResponse buildResponse(Order order, String status, String msg) {\n return OrderResponse.builder()\n .orderId(order.getId())\n .status(status)\n .message(msg)\n .timestamp(System.currentTimeMillis())\n .build();\n }\n}\n\n -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java b/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java ---- a/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java -+++ b/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java -@@ -11,6 +11,7 @@ - import org.example.matching.model.Order; - import org.example.matching.model.Trade; - import org.example.matching.orderbook.OrderRepository; -+import org.springframework.context.annotation.Lazy; - import org.springframework.stereotype.Service; - - import java.util.List; -@@ -18,7 +19,6 @@ - import java.util.concurrent.ConcurrentHashMap; - - @Service --@RequiredArgsConstructor - public class OrderService { - - private final MarketDataService marketDataService; -@@ -27,6 +27,23 @@ - private final WalletService walletService; - private final EventJournal eventJournal; - private final OrderRepository orderRepository; -+ private final LiquidBotService liquidBotService; -+ -+ public OrderService(MarketDataService marketDataService, -+ RiskManager riskManager, -+ MatchingEngine matchingEngine, -+ WalletService walletService, -+ EventJournal eventJournal, -+ OrderRepository orderRepository, -+ @Lazy LiquidBotService liquidBotService) { -+ this.marketDataService = marketDataService; -+ this.riskManager = riskManager; -+ this.matchingEngine = matchingEngine; -+ this.walletService = walletService; -+ this.eventJournal = eventJournal; -+ this.orderRepository = orderRepository; -+ this.liquidBotService = liquidBotService; -+ } - - // In-memory idempotency store: Key -> Previous Response - private final Map idempotencyStore = new ConcurrentHashMap<>(); -@@ -54,12 +71,9 @@ - marketDataService.UpdateTrade(order.getInstrument(),trade.getPrice(),trade.getQuantity()); - eventJournal.appendRaw("TRADE_SETTLED: " + trade.getBuyOrderId() + " <-> " + trade.getSellOrderId()); - } -- var book = matchingEngine.getOrderBookForMarketData(order.getInstrument()); -- marketDataService.updateBookTops( -- order.getInstrument(), -- book.getBestBid(), -- book.getBestAsk() -- ); -+ if(!order.getUserId().equals("HOUSE_BOT")){ -+ liquidBotService.updateMarketTrigger(order.getInstrument()); -+ } - - OrderResponse response = buildResponse(order, "ACCEPTED", "Success"); - idempotencyStore.put(request.getIdempotencyKey(), response); -Index: matching-engine/src/main/java/org/example/matching/model/Reservation.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.Setter;\n\n@Getter @Setter @AllArgsConstructor\npublic class Reservation {\n private final String OrderId;\n private final String userId;\n private final long priceatReserve;\n private int remainingQty;\n private String instrument; // Add instrument field\n\n private long reservedCash;\n private long reservedShares;\n private final Boolean isBuy;\n\n public Reservation(String orderId, String userId, long priceatReserve, int remainingQty, Boolean isBuy) {\n OrderId = orderId;\n this.userId = userId;\n this.priceatReserve = priceatReserve;\n this.remainingQty = remainingQty;\n this.isBuy = isBuy;\n this.instrument = \"MARKET\"; // Default instrument\n }\n\n public Reservation(String orderId, String userId, long priceatReserve, int remainingQty, Boolean isBuy, String instrument) {\n OrderId = orderId;\n this.userId = userId;\n this.priceatReserve = priceatReserve;\n this.remainingQty = remainingQty;\n this.isBuy = isBuy;\n this.instrument = instrument != null ? instrument : \"MARKET\";\n }\n\n public long reduceBy(int qty) {\n if (qty <= 0 || remainingQty <= 0) {\n return 0;\n }\n\n // Don't consume more than what is left in this specific reservation\n int consumed = Math.min(qty, remainingQty);\n long cashUsed =0;\n if (isBuy) {\n // Calculate how much cash was 'used' for this portion of the trade\n cashUsed = (long) consumed * priceatReserve;\n\n // Subtract the used cash from the total reserved cash\n this.reservedCash -= cashUsed;\n\n if (this.reservedCash < 0) this.reservedCash = 0;\n } else {\n // For a sell order, we just reduce the count of shares held\n this.reservedShares -= consumed;\n\n if (this.reservedShares < 0) this.reservedShares = 0;\n }\n\n // Finally, reduce the quantity so we know how much of the order is left to fill\n this.remainingQty -= consumed;\n return cashUsed;\n }\n\n // Additional getters needed by the codebase\n public String getOrderId() {\n return OrderId;\n }\n\n public String getUserId() {\n return userId;\n }\n\n public Boolean getIsBuy() {\n return isBuy;\n }\n\n public long getReservedCash() {\n return reservedCash;\n }\n\n public long getReservedShares() {\n return reservedShares;\n }\n\n public int getRemainingQty() {\n return remainingQty;\n }\n\n public void setReservedCash(long reservedCash) {\n this.reservedCash = reservedCash;\n }\n\n public void setReservedShares(long reservedShares) {\n this.reservedShares = reservedShares;\n }\n} -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/model/Reservation.java b/matching-engine/src/main/java/org/example/matching/model/Reservation.java ---- a/matching-engine/src/main/java/org/example/matching/model/Reservation.java -+++ b/matching-engine/src/main/java/org/example/matching/model/Reservation.java -@@ -59,7 +59,9 @@ - - // Finally, reduce the quantity so we know how much of the order is left to fill - this.remainingQty -= consumed; -- return cashUsed; -+ -+ // Return the actual quantity consumed for settlement calculations -+ return consumed; - } - - // Additional getters needed by the codebase -Index: matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching.Wallets;\n\nimport org.example.matching.model.Order;\nimport org.example.matching.model.Trade;\nimport org.example.matching.model.Wallet;\n\npublic interface WalletService {\n boolean reserveForOrder(Order order);\n void releaseReservation(String orderId);\n void settleTrade(Trade trade);\n \n // Additional methods needed for testing and API\n void creditUserShares(String userId, long shares);\n void creditUserShares(String userId, String instrument, long shares);\n void creditUserCash(String userId, long cash);\n Wallet getWallet(String userId);\n}\n -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java b/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java ---- a/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java -+++ b/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java -@@ -1,14 +1,17 @@ - package org.example.matching.Wallets; - -+import lombok.RequiredArgsConstructor; - import org.example.matching.model.Order; - import org.example.matching.model.Trade; - import org.example.matching.model.Wallet; - -+ - public interface WalletService { - boolean reserveForOrder(Order order); - void releaseReservation(String orderId); - void settleTrade(Trade trade); -- -+ -+ java.util.Collection getAllWallets(); - // Additional methods needed for testing and API - void creditUserShares(String userId, long shares); - void creditUserShares(String userId, String instrument, long shares); -Index: matching-engine/journals/engine.log -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>ORDER c78afcd8-aa27-4770-866b-840efa1dcad2 seller1 SELL 100 10 1772752218574\nORDER 8a44bcdf-4b7a-4bfb-b94c-792f629cd9f1 buyer1 BUY 105 4 1772752218582\nTRADE 8a44bcdf-4b7a-4bfb-b94c-792f629cd9f1 c78afcd8-aa27-4770-866b-840efa1dcad2 100 4 1772752218582\nORDER 2c267f3a-4da6-4060-bb5b-de3fa97f6029 buyer2 BUY 100 6 1772752218583\nTRADE 2c267f3a-4da6-4060-bb5b-de3fa97f6029 c78afcd8-aa27-4770-866b-840efa1dcad2 100 6 1772752218583\nORDER a692a971-2eae-4de6-b469-2bff7c12c7ec seller1 SELL 100 10\nORDER a692a971-2eae-4de6-b469-2bff7c12c7ec seller1 SELL 100 10 1772752218586\nORDER 9a4428f5-1e36-4bf6-a7e7-2b0900bb62e8 buyer1 BUY 105 4\nORDER 9a4428f5-1e36-4bf6-a7e7-2b0900bb62e8 buyer1 BUY 105 4 1772752218586\nTRADE 9a4428f5-1e36-4bf6-a7e7-2b0900bb62e8 a692a971-2eae-4de6-b469-2bff7c12c7ec 100 4 1772752218586\nTRADE 9a4428f5-1e36-4bf6-a7e7-2b0900bb62e8 a692a971-2eae-4de6-b469-2bff7c12c7ec 100 4\nORDER a8eaddb2-4f1f-4df0-a28b-c365b37c6911 buyer1 BUY 100 6\nORDER a8eaddb2-4f1f-4df0-a28b-c365b37c6911 buyer1 BUY 100 6 1772752218586\nTRADE a8eaddb2-4f1f-4df0-a28b-c365b37c6911 a692a971-2eae-4de6-b469-2bff7c12c7ec 100 6 1772752218587\nTRADE a8eaddb2-4f1f-4df0-a28b-c365b37c6911 a692a971-2eae-4de6-b469-2bff7c12c7ec 100 6\nORDER 5362fffb-146f-40b0-a9e1-fb84dd970ac7 seller1 SELL 100 10 1772760358808\nORDER 0211741a-8fa1-4a25-8b1d-1237a52f9211 buyer1 BUY 105 4 1772760358814\nTRADE 0211741a-8fa1-4a25-8b1d-1237a52f9211 5362fffb-146f-40b0-a9e1-fb84dd970ac7 100 4 1772760358815\nORDER 898a8498-ad4e-434b-8abe-5f5d20ae3173 buyer2 BUY 100 6 1772760358816\nTRADE 898a8498-ad4e-434b-8abe-5f5d20ae3173 5362fffb-146f-40b0-a9e1-fb84dd970ac7 100 6 1772760358816\nREJECT 6b224bfc-c1e8-46d7-b0a3-ac0de2b49358 INSUFFICIENT_FUNDS\nORDER f9712aa9-acba-43aa-adda-cb5a7adf145d buyer1 BUY 105 4\nORDER f9712aa9-acba-43aa-adda-cb5a7adf145d buyer1 BUY 105 4 1772760358817\nORDER c2cd976b-335c-4592-a644-d0d07db87af1 buyer1 BUY 100 6\nORDER c2cd976b-335c-4592-a644-d0d07db87af1 buyer1 BUY 100 6 1772760358817\nORDER 37c7fb05-1c35-4f6c-9407-3e01713a1ea9 seller1 SELL 100 10 1772760473119\nORDER 05f435ca-b3b9-4288-a979-062eef0f5e06 buyer1 BUY 105 4 1772760473125\nTRADE 05f435ca-b3b9-4288-a979-062eef0f5e06 37c7fb05-1c35-4f6c-9407-3e01713a1ea9 100 4 1772760473125\nORDER ce8d19be-00e6-4e0b-8056-2f468e5d540a buyer2 BUY 100 6 1772760473126\nTRADE ce8d19be-00e6-4e0b-8056-2f468e5d540a 37c7fb05-1c35-4f6c-9407-3e01713a1ea9 100 6 1772760473126\nREJECT debaf786-7218-4204-9bed-55e5d79c5e16 INSUFFICIENT_FUNDS\nORDER d5f67c56-67c3-4dac-967d-8b54cc41d1b6 buyer1 BUY 105 4\nORDER d5f67c56-67c3-4dac-967d-8b54cc41d1b6 buyer1 BUY 105 4 1772760473129\nORDER a204eca1-a5db-4d75-855f-64ca63a755db buyer1 BUY 100 6\nORDER a204eca1-a5db-4d75-855f-64ca63a755db buyer1 BUY 100 6 1772760473129\nORDER_PLACED: e72d00ac-eb7b-42ab-bf3b-f9e568c32ea4\nORDER e72d00ac-eb7b-42ab-bf3b-f9e568c32ea4 user-seller SELL 100 10 1772760703367\nORDER_PLACED: cbeb1b27-a8fa-45b2-9bd0-459cc73c3466\nORDER cbeb1b27-a8fa-45b2-9bd0-459cc73c3466 user1 BUY 100 10 1772760707390\nTRADE cbeb1b27-a8fa-45b2-9bd0-459cc73c3466 e72d00ac-eb7b-42ab-bf3b-f9e568c32ea4 100 10 1772760707390\nTRADE_SETTLED: cbeb1b27-a8fa-45b2-9bd0-459cc73c3466 <-> e72d00ac-eb7b-42ab-bf3b-f9e568c32ea4\nORDER_PLACED: e5bb5cfc-bb06-4a16-9af5-5e91f72c7a0c\nORDER e5bb5cfc-bb06-4a16-9af5-5e91f72c7a0c user-seller SELL 150 10 1772761274302\nORDER_PLACED: aa3558d9-7e18-41d7-b6e5-00e3e0c69788\nORDER aa3558d9-7e18-41d7-b6e5-00e3e0c69788 user1 BUY 151 10 1772761274334\nTRADE aa3558d9-7e18-41d7-b6e5-00e3e0c69788 e5bb5cfc-bb06-4a16-9af5-5e91f72c7a0c 150 10 1772761274335\nTRADE_SETTLED: aa3558d9-7e18-41d7-b6e5-00e3e0c69788 <-> e5bb5cfc-bb06-4a16-9af5-5e91f72c7a0c\nORDER_PLACED: 89a0af09-0b11-467d-ba14-c015ea52007c\nORDER 89a0af09-0b11-467d-ba14-c015ea52007c user-seller SELL 150 10 1772837899360\nORDER_PLACED: 14e62193-7644-460c-bc0e-e6f9f9eb8ff9\nORDER 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 user1 BUY 151 10 1772837899385\nTRADE 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 89a0af09-0b11-467d-ba14-c015ea52007c 150 10 1772837899386\nTRADE_SETTLED: 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 <-> 89a0af09-0b11-467d-ba14-c015ea52007c\n -=================================================================== -diff --git a/matching-engine/journals/engine.log b/matching-engine/journals/engine.log ---- a/matching-engine/journals/engine.log -+++ b/matching-engine/journals/engine.log -@@ -51,3 +51,157 @@ - ORDER 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 user1 BUY 151 10 1772837899385 - TRADE 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 89a0af09-0b11-467d-ba14-c015ea52007c 150 10 1772837899386 - TRADE_SETTLED: 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 <-> 89a0af09-0b11-467d-ba14-c015ea52007c -+ORDER_PLACED: 202f458a-7394-4199-85bd-918530c96e13 -+ORDER 202f458a-7394-4199-85bd-918530c96e13 HOUSE_BOT BUY 26 500 1773086278334 -+ORDER_PLACED: 84e69fc8-4bd9-4e3f-a0d1-6a76fb82dfed -+ORDER 84e69fc8-4bd9-4e3f-a0d1-6a76fb82dfed HOUSE_BOT SELL 30 500 1773086278337 -+ORDER_PLACED: 2981da91-ad7c-46e6-8ae3-cd233950442b -+ORDER 2981da91-ad7c-46e6-8ae3-cd233950442b HOUSE_BOT BUY 70 500 1773086278337 -+ORDER_PLACED: eb55fe77-896d-4022-9fa4-9a789c2ec530 -+ORDER eb55fe77-896d-4022-9fa4-9a789c2ec530 HOUSE_BOT SELL 74 500 1773086278337 -+ORDER_PLACED: c2463dd5-c0f6-4e0e-aff6-c9b9ddcd090c -+ORDER c2463dd5-c0f6-4e0e-aff6-c9b9ddcd090c trader1 SELL 150 10 1773095406065 -+ORDER_PLACED: ae60b85e-47c3-4ae0-a796-3274fea0381c -+ORDER ae60b85e-47c3-4ae0-a796-3274fea0381c trader2 BUY 155 5 1773095428409 -+TRADE ae60b85e-47c3-4ae0-a796-3274fea0381c c2463dd5-c0f6-4e0e-aff6-c9b9ddcd090c 150 5 1773095428410 -+TRADE_SETTLED: ae60b85e-47c3-4ae0-a796-3274fea0381c <-> c2463dd5-c0f6-4e0e-aff6-c9b9ddcd090c -+ORDER_PLACED: 30021f61-8157-4daf-96d2-6cbac68bdb73 -+ORDER 30021f61-8157-4daf-96d2-6cbac68bdb73 HOUSE_BOT BUY 26 500 1773095456405 -+ORDER_PLACED: 9bea18c3-c632-4f63-a4a6-adcc5af0e4a2 -+ORDER 9bea18c3-c632-4f63-a4a6-adcc5af0e4a2 HOUSE_BOT SELL 30 500 1773095456406 -+ORDER_PLACED: 5379120e-0bca-4afd-9eae-c15eb3194b8f -+ORDER 5379120e-0bca-4afd-9eae-c15eb3194b8f HOUSE_BOT BUY 70 500 1773095456406 -+ORDER_PLACED: 8502d3a8-b4ec-495e-a2a4-ce52a0be6a05 -+ORDER 8502d3a8-b4ec-495e-a2a4-ce52a0be6a05 HOUSE_BOT SELL 74 500 1773095456406 -+ORDER_PLACED: 6c986989-e62e-4f5c-a4de-12fe7639f1ec -+ORDER 6c986989-e62e-4f5c-a4de-12fe7639f1ec HOUSE_BOT BUY 26 500 1773095462080 -+ORDER_PLACED: 36489e0f-8ff0-4826-9ece-09a6d05ed3c6 -+ORDER 36489e0f-8ff0-4826-9ece-09a6d05ed3c6 HOUSE_BOT SELL 30 500 1773095462080 -+ORDER_PLACED: 521b3e8b-0b5d-4f33-b5fd-ae50f94675d2 -+ORDER 521b3e8b-0b5d-4f33-b5fd-ae50f94675d2 HOUSE_BOT BUY 70 500 1773095462080 -+ORDER_PLACED: 8ba2d046-b754-46a6-91cc-be1b27995022 -+ORDER 8ba2d046-b754-46a6-91cc-be1b27995022 HOUSE_BOT SELL 74 500 1773095462081 -+ORDER_PLACED: 99b639c1-2333-4f80-ac17-c0b030ffe0e3 -+ORDER 99b639c1-2333-4f80-ac17-c0b030ffe0e3 seller SELL 200 20 1773095477932 -+ORDER_PLACED: cba1d599-a0fa-4c60-88dc-8ae55f6018fc -+ORDER cba1d599-a0fa-4c60-88dc-8ae55f6018fc buyer BUY 210 15 1773095640840 -+TRADE cba1d599-a0fa-4c60-88dc-8ae55f6018fc c2463dd5-c0f6-4e0e-aff6-c9b9ddcd090c 150 5 1773095640842 -+TRADE cba1d599-a0fa-4c60-88dc-8ae55f6018fc 99b639c1-2333-4f80-ac17-c0b030ffe0e3 200 10 1773095640842 -+TRADE_SETTLED: cba1d599-a0fa-4c60-88dc-8ae55f6018fc <-> c2463dd5-c0f6-4e0e-aff6-c9b9ddcd090c -+TRADE_SETTLED: cba1d599-a0fa-4c60-88dc-8ae55f6018fc <-> 99b639c1-2333-4f80-ac17-c0b030ffe0e3 -+ORDER_PLACED: b9f4d514-da1a-47ad-9719-91010ca37d5e -+ORDER b9f4d514-da1a-47ad-9719-91010ca37d5e test2 SELL 100 10 1773096383964 -+ORDER_PLACED: e9fc81d7-43cd-41c8-86b7-48fcac278549 -+ORDER e9fc81d7-43cd-41c8-86b7-48fcac278549 test1 BUY 105 5 1773096394339 -+TRADE e9fc81d7-43cd-41c8-86b7-48fcac278549 b9f4d514-da1a-47ad-9719-91010ca37d5e 100 5 1773096394340 -+TRADE_SETTLED: e9fc81d7-43cd-41c8-86b7-48fcac278549 <-> b9f4d514-da1a-47ad-9719-91010ca37d5e -+ORDER_PLACED: 9ae777b5-b560-48c1-a5df-35fdc56f82f4 -+ORDER 9ae777b5-b560-48c1-a5df-35fdc56f82f4 test4 SELL 150 20 1773096518712 -+ORDER_PLACED: b0a48945-670c-49f2-9c42-0c6bcc619785 -+ORDER b0a48945-670c-49f2-9c42-0c6bcc619785 test3 BUY 155 8 1773096581201 -+TRADE b0a48945-670c-49f2-9c42-0c6bcc619785 9ae777b5-b560-48c1-a5df-35fdc56f82f4 150 8 1773096581207 -+TRADE_SETTLED: b0a48945-670c-49f2-9c42-0c6bcc619785 <-> 9ae777b5-b560-48c1-a5df-35fdc56f82f4 -+ORDER_PLACED: 1e9ffb95-29cd-4ff8-9d81-97bf85706bd7 -+ORDER 1e9ffb95-29cd-4ff8-9d81-97bf85706bd7 HOUSE_BOT BUY 26 500 1773096596080 -+ORDER_PLACED: 586a9c62-b96a-41f6-af52-50a3b1809126 -+ORDER 586a9c62-b96a-41f6-af52-50a3b1809126 HOUSE_BOT SELL 30 500 1773096596081 -+ORDER_PLACED: e21f48cd-4dc2-45ec-bece-87c59edbfd8a -+ORDER e21f48cd-4dc2-45ec-bece-87c59edbfd8a HOUSE_BOT BUY 70 500 1773096596081 -+ORDER_PLACED: 91744cbe-26bf-431b-91e3-524dd13100ab -+ORDER 91744cbe-26bf-431b-91e3-524dd13100ab HOUSE_BOT SELL 74 500 1773096596081 -+ORDER_PLACED: 686cca41-810a-46bd-8412-47daef7082bd -+ORDER 686cca41-810a-46bd-8412-47daef7082bd HOUSE_BOT BUY 26 500 1773097147018 -+ORDER_PLACED: 6d8d4c5e-daac-4a9e-8d23-d89aeed16515 -+ORDER 6d8d4c5e-daac-4a9e-8d23-d89aeed16515 HOUSE_BOT SELL 30 500 1773097147019 -+ORDER_PLACED: 9d2d7b00-90ae-4d16-93a5-20b18999ebd4 -+ORDER 9d2d7b00-90ae-4d16-93a5-20b18999ebd4 HOUSE_BOT BUY 70 500 1773097147019 -+ORDER_PLACED: a58f1770-da0b-4d0b-a50e-4641ef6162db -+ORDER a58f1770-da0b-4d0b-a50e-4641ef6162db HOUSE_BOT SELL 74 500 1773097147020 -+ORDER_PLACED: dae39d25-7eb0-48c6-bc74-3d4fc5be6d43 -+ORDER dae39d25-7eb0-48c6-bc74-3d4fc5be6d43 trader-yes BUY 30 10 1773097198644 -+TRADE dae39d25-7eb0-48c6-bc74-3d4fc5be6d43 6d8d4c5e-daac-4a9e-8d23-d89aeed16515 30 10 1773097198645 -+TRADE_SETTLED: dae39d25-7eb0-48c6-bc74-3d4fc5be6d43 <-> 6d8d4c5e-daac-4a9e-8d23-d89aeed16515 -+ORDER_PLACED: b11f6bdf-085b-4b0a-ab66-2c5208dcbf33 -+ORDER b11f6bdf-085b-4b0a-ab66-2c5208dcbf33 HOUSE_BOT BUY 27 500 1773097198654 -+ORDER_PLACED: 8ca7e156-e844-4ac0-81d4-3b99fd79f864 -+ORDER 8ca7e156-e844-4ac0-81d4-3b99fd79f864 HOUSE_BOT SELL 31 500 1773097198654 -+ORDER_PLACED: fb139d62-e3df-4afe-ba08-f865e4b14706 -+ORDER fb139d62-e3df-4afe-ba08-f865e4b14706 HOUSE_BOT BUY 69 500 1773097198654 -+ORDER_PLACED: e0fd4ab5-d494-4e3b-9981-b544fe726068 -+ORDER e0fd4ab5-d494-4e3b-9981-b544fe726068 HOUSE_BOT SELL 73 500 1773097198655 -+ORDER_PLACED: 3cf79e7e-b1d5-4266-ad0d-fed5d66ab360 -+ORDER 3cf79e7e-b1d5-4266-ad0d-fed5d66ab360 trader-no BUY 74 10 1773097220661 -+TRADE 3cf79e7e-b1d5-4266-ad0d-fed5d66ab360 e0fd4ab5-d494-4e3b-9981-b544fe726068 73 10 1773097220662 -+TRADE_SETTLED: 3cf79e7e-b1d5-4266-ad0d-fed5d66ab360 <-> e0fd4ab5-d494-4e3b-9981-b544fe726068 -+ORDER_PLACED: 45aa1507-2c22-49f9-95ab-c34e0d7ed192 -+ORDER 45aa1507-2c22-49f9-95ab-c34e0d7ed192 HOUSE_BOT BUY 27 500 1773097220663 -+ORDER_PLACED: 5b2d1890-68f3-4d0f-8808-4097057cd90d -+ORDER 5b2d1890-68f3-4d0f-8808-4097057cd90d HOUSE_BOT SELL 31 500 1773097220663 -+ORDER_PLACED: d200a652-4674-4057-9174-16604e0d3d54 -+ORDER d200a652-4674-4057-9174-16604e0d3d54 HOUSE_BOT BUY 69 500 1773097220663 -+ORDER_PLACED: 74aca358-0ff0-4df1-bdb7-9c29d350a0ab -+ORDER 74aca358-0ff0-4df1-bdb7-9c29d350a0ab HOUSE_BOT SELL 73 500 1773097220664 -+ORDER_PLACED: 1896ba12-86ac-4c65-9a35-91312a00d4f2 -+ORDER 1896ba12-86ac-4c65-9a35-91312a00d4f2 trader-yes SELL 27 5 1773097238574 -+TRADE 45aa1507-2c22-49f9-95ab-c34e0d7ed192 1896ba12-86ac-4c65-9a35-91312a00d4f2 27 5 1773097238575 -+TRADE_SETTLED: 45aa1507-2c22-49f9-95ab-c34e0d7ed192 <-> 1896ba12-86ac-4c65-9a35-91312a00d4f2 -+ORDER_PLACED: b4cf102d-c66b-4f31-bb9f-1c5281fb3f48 -+ORDER b4cf102d-c66b-4f31-bb9f-1c5281fb3f48 HOUSE_BOT BUY 27 500 1773097238576 -+ORDER_PLACED: f2aea2bf-178d-4b70-85ec-fe77c9e7c803 -+ORDER f2aea2bf-178d-4b70-85ec-fe77c9e7c803 HOUSE_BOT SELL 31 500 1773097238576 -+ORDER_PLACED: 74c779f6-6698-421a-bbad-b297a7631434 -+ORDER 74c779f6-6698-421a-bbad-b297a7631434 HOUSE_BOT BUY 69 500 1773097238577 -+ORDER_PLACED: 9a5a6c33-1739-492d-a015-10dcab579f98 -+ORDER 9a5a6c33-1739-492d-a015-10dcab579f98 HOUSE_BOT SELL 73 500 1773097238577 -+ORDER_PLACED: d93482b3-d7e2-4ef9-80ab-2e4c864d7bc7 -+ORDER d93482b3-d7e2-4ef9-80ab-2e4c864d7bc7 trader3 BUY 31 5 1773097257718 -+TRADE d93482b3-d7e2-4ef9-80ab-2e4c864d7bc7 f2aea2bf-178d-4b70-85ec-fe77c9e7c803 31 5 1773097257719 -+TRADE_SETTLED: d93482b3-d7e2-4ef9-80ab-2e4c864d7bc7 <-> f2aea2bf-178d-4b70-85ec-fe77c9e7c803 -+ORDER_PLACED: 89daff4d-0302-4121-90ca-af6451639436 -+ORDER 89daff4d-0302-4121-90ca-af6451639436 HOUSE_BOT BUY 27 500 1773097257720 -+ORDER_PLACED: a35298b1-c5f1-49f0-aca3-5c0f929a3741 -+ORDER a35298b1-c5f1-49f0-aca3-5c0f929a3741 HOUSE_BOT SELL 31 500 1773097257720 -+ORDER_PLACED: 5a792477-a3c9-4271-883a-dd34999b15d3 -+ORDER 5a792477-a3c9-4271-883a-dd34999b15d3 HOUSE_BOT BUY 69 500 1773097257721 -+ORDER_PLACED: 15fb0266-4e4c-441e-a52b-9a875cb2e5df -+ORDER 15fb0266-4e4c-441e-a52b-9a875cb2e5df HOUSE_BOT SELL 73 500 1773097257721 -+ORDER_PLACED: 98914de0-d7cb-4282-ba23-8fc6ecb363e6 -+ORDER 98914de0-d7cb-4282-ba23-8fc6ecb363e6 HOUSE_BOT BUY 26 500 1773097277308 -+ORDER_PLACED: a89eaea0-5d24-4fd3-8fb0-5481b196e692 -+ORDER a89eaea0-5d24-4fd3-8fb0-5481b196e692 HOUSE_BOT SELL 30 500 1773097277309 -+ORDER_PLACED: d0931ea4-cdb0-4fea-98e1-dad289b43e3d -+ORDER d0931ea4-cdb0-4fea-98e1-dad289b43e3d HOUSE_BOT BUY 70 500 1773097277309 -+ORDER_PLACED: 80ee5962-8239-48ee-8b62-3a44daadbc65 -+ORDER 80ee5962-8239-48ee-8b62-3a44daadbc65 HOUSE_BOT SELL 74 500 1773097277309 -+ORDER_PLACED: b75546e7-a3cd-4f02-a9e8-bdb713f9d3f4 -+ORDER b75546e7-a3cd-4f02-a9e8-bdb713f9d3f4 fresh-trader BUY 30 10 1773097300339 -+TRADE b75546e7-a3cd-4f02-a9e8-bdb713f9d3f4 a89eaea0-5d24-4fd3-8fb0-5481b196e692 30 10 1773097300340 -+TRADE_SETTLED: b75546e7-a3cd-4f02-a9e8-bdb713f9d3f4 <-> a89eaea0-5d24-4fd3-8fb0-5481b196e692 -+ORDER_PLACED: 9972ac7c-6a34-4bb0-92c2-614c0d39b9b1 -+ORDER 9972ac7c-6a34-4bb0-92c2-614c0d39b9b1 HOUSE_BOT BUY 27 500 1773097300342 -+ORDER_PLACED: 976557fc-f09a-4b6f-aec8-8c916a54ffe7 -+ORDER 976557fc-f09a-4b6f-aec8-8c916a54ffe7 HOUSE_BOT SELL 31 500 1773097300342 -+ORDER_PLACED: ea73dbbf-a9a3-45d7-8a69-165e7d97b0a2 -+ORDER ea73dbbf-a9a3-45d7-8a69-165e7d97b0a2 HOUSE_BOT BUY 69 500 1773097300343 -+ORDER_PLACED: 9f33a6c3-863d-4486-97a5-bf3e7e391ffe -+ORDER 9f33a6c3-863d-4486-97a5-bf3e7e391ffe HOUSE_BOT SELL 73 500 1773097300343 -+ORDER_PLACED: 3059b475-e0a5-471e-bcfc-a6962d673eb3 -+ORDER 3059b475-e0a5-471e-bcfc-a6962d673eb3 HOUSE_BOT BUY 26 500 1773097341383 -+ORDER_PLACED: 30269f8e-f66b-47d3-a8a6-036c04d6f975 -+ORDER 30269f8e-f66b-47d3-a8a6-036c04d6f975 HOUSE_BOT SELL 30 500 1773097341384 -+ORDER_PLACED: 6d2a3d9d-0d7d-4604-8337-09d6f475cd76 -+ORDER 6d2a3d9d-0d7d-4604-8337-09d6f475cd76 HOUSE_BOT BUY 70 500 1773097341384 -+ORDER_PLACED: 63fb6a37-e2bd-4ae6-bc8e-5497acd347f6 -+ORDER 63fb6a37-e2bd-4ae6-bc8e-5497acd347f6 HOUSE_BOT SELL 74 500 1773097341385 -+ORDER_PLACED: 105c8737-bdfb-4cea-9118-11f1258df96e -+ORDER 105c8737-bdfb-4cea-9118-11f1258df96e final-trader BUY 30 5 1773097348388 -+TRADE 105c8737-bdfb-4cea-9118-11f1258df96e 30269f8e-f66b-47d3-a8a6-036c04d6f975 30 5 1773097348389 -+TRADE_SETTLED: 105c8737-bdfb-4cea-9118-11f1258df96e <-> 30269f8e-f66b-47d3-a8a6-036c04d6f975 -+ORDER_PLACED: 773eb336-5d94-4a05-9669-3ffe58262feb -+ORDER 773eb336-5d94-4a05-9669-3ffe58262feb HOUSE_BOT BUY 27 500 1773097348392 -+ORDER_PLACED: 283b2488-c7b9-4700-ab24-5ea9c5d2f2d6 -+ORDER 283b2488-c7b9-4700-ab24-5ea9c5d2f2d6 HOUSE_BOT SELL 31 500 1773097348393 -+ORDER_PLACED: 54bfc35d-578c-4110-bfe2-5624cd00b09f -+ORDER 54bfc35d-578c-4110-bfe2-5624cd00b09f HOUSE_BOT BUY 69 500 1773097348393 -+ORDER_PLACED: b12ad53a-4864-452b-91d5-192272814f26 -+ORDER b12ad53a-4864-452b-91d5-192272814f26 HOUSE_BOT SELL 73 500 1773097348393 -Index: matching-engine/OrderFlow -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>The \"Order of Operations\" [Step by Step flow]\nFor a trade to settle successfully, the order must be \"registered\" in the repository before the matching happens. Here is the correct sequence:\n\nThe Request: Postman sends a \"BUY AAPL\" request.\n\nThe Check: RiskManager checks the Wallet to make sure the user has enough cash.\n\nThe Registration (CRITICAL): You call orderRepository.save(order). Now the system \"knows\" this order ID belongs to \"User1\".\n\nThe Matching Engine: The order goes into the OrderBook algorithm.\n\nThe Match: The algorithm finds a \"SELL AAPL\" order that was already in the repo. It generates a Trade.\n\nThe Settlement: The settleTrade method takes that Trade and asks the Repo: \"Who owns the orders in this trade?\"\n\nThe Hand-off: Because you did Step 3, the Repo says: \"Order A belongs to User1, and Order B belongs to User2.\"\n\nThe Bank: The WalletService finally moves the money and shares.\n\n\n\n---- Trade in it -----\n[OrderBook receives the whole Order object, but inside the Matching Algorithm, it ignores the userId entirely. It only looks at:\n\n Price: To see if the buyer and seller can agree.\n\n Quantity: To see how much can be traded.\n\n Timestamp: To decide who was first in line (Price-Time Priority).]\n\n\n\n When the Matching Engine finishes, it creates a Trade object. That trade object looks like this:\n\n buyOrderId: \"abc-123\"\n\n sellOrderId: \"xyz-789\"\n\n price: 150\n\n quantity: 10\n\n When this trade is sent to Settlement, the first thing the code does is:\n orderRepository.findById(\"abc-123\")\n\n\n\n\n -=================================================================== -diff --git a/matching-engine/OrderFlow b/matching-engine/OrderFlow ---- a/matching-engine/OrderFlow -+++ b/matching-engine/OrderFlow -@@ -46,3 +46,11 @@ - - - -+----- -+Pice ed -+ -+1. What you have vs. What Kalshi -+hasFeature Your Current MarketDataService Kalshi / Prediction Market Logic -+Price OriginUser-to-User trades only. Automated Liquidity (The Bot). -+Logic Calculates VWAP and Spread. Ensures Price(YES) + Price(NO) ≈ 100¢. -+RoleReporting (Show what happened). Price Discovery (Setting the "Fair" price). -\ No newline at end of file -Index: matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching.config;\n\nimport org.example.matching.Wallets.InMemoryWalletService;\nimport org.example.matching.Wallets.RiskManager;\nimport org.example.matching.Wallets.WalletService;\nimport org.example.matching.journal.EventJournal;\nimport org.example.matching.matching.MatchingEngine;\nimport org.example.matching.orderbook.InMemoryOrderRepository;\nimport org.example.matching.orderbook.OrderRepository;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MatchingEngineConfig {\n\n @Bean\n public EventJournal eventJournal() {\n return new EventJournal();\n }\n\n @Bean\n public OrderRepository orderRepository() {\n return new InMemoryOrderRepository();\n }\n\n @Bean\n public WalletService walletService(OrderRepository orderRepository) {\n return new InMemoryWalletService(orderRepository);\n }\n\n @Bean\n public RiskManager riskManager(WalletService walletService, OrderRepository orderRepository) {\n return new RiskManager(walletService, orderRepository);\n }\n\n @Bean\n public MatchingEngine matchingEngine(EventJournal eventJournal) {\n return new MatchingEngine();\n }\n}\n -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java b/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java ---- a/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java -+++ b/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java -@@ -34,7 +34,7 @@ - } - - @Bean -- public MatchingEngine matchingEngine(EventJournal eventJournal) { -- return new MatchingEngine(); -+ public MatchingEngine matchingEngine(WalletService walletService, EventJournal eventJournal) { -+ return new MatchingEngine(walletService, eventJournal); - } - } -Index: matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching.Wallets;\n\nimport lombok.RequiredArgsConstructor;\nimport org.example.matching.model.Order;\nimport org.example.matching.model.Reservation;\nimport org.example.matching.model.Trade;\nimport org.example.matching.model.Wallet;\nimport org.example.matching.orderbook.OrderRepository;\nimport org.springframework.stereotype.Service; // Add this import\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n@Service // Tells Spring this is a managed bean\npublic class InMemoryWalletService implements WalletService {\n\n private final Map wallets = new ConcurrentHashMap<>();\n private final Map reservations = new ConcurrentHashMap<>();\n private final OrderRepository orderRepository;\n private final String INSTRUMENT = \"MARKET\";\n\n // No-arg constructor for Main.java usage\n public InMemoryWalletService() {\n this.orderRepository = null;\n }\n\n // Constructor for dependency injection\n public InMemoryWalletService(OrderRepository orderRepository) {\n this.orderRepository = orderRepository;\n }\n\n private Wallet ensureWallet(String userId) {\n return wallets.computeIfAbsent(userId, k -> new Wallet(userId));\n }\n\n @Override\n public boolean reserveForOrder(Order order) {\n String userId = order.getUserId();\n String instrument = order.getInstrument();\n Wallet w = ensureWallet(userId);\n\n if (order.getSide().name().equals(\"BUY\")) {\n long required = order.getPrice() * (long) order.getQuantity();\n if (!w.tryReserveCash(required)) return false;\n\n Reservation r = new Reservation(order.getId(), userId, order.getPrice(), order.getQuantity(), true, instrument);\n r.setReservedCash(required);\n reservations.put(order.getId(), r);\n } else {\n if (!w.tryReserveShares(instrument, order.getQuantity())) return false;\n\n Reservation r = new Reservation(order.getId(), userId, order.getPrice(), order.getQuantity(), false, instrument);\n r.setReservedShares(order.getQuantity());\n reservations.put(order.getId(), r);\n }\n return true;\n }\n\n\n@Override\npublic void releaseReservation(String orderId){\n Reservation r = reservations.remove(orderId);\n if(r==null)return;\n Wallet w = ensureWallet(r.getUserId());\n if(r.getIsBuy()){\n w.releaseReserveCash(r.getReservedCash());\n\n }else{\n w.releaseReservedShares(r.getInstrument(),r.getReservedShares());\n\n }\n }\n\n @Override\n public void creditUserShares(String userId, long shares) {\n Wallet w = ensureWallet(userId);\n w.addAvailableShares(INSTRUMENT, shares);\n }\n\n @Override\n public void creditUserShares(String userId, String instrument, long shares) {\n Wallet w = ensureWallet(userId);\n w.addAvailableShares(instrument, shares);\n }\n\n @Override\n public void creditUserCash(String userId, long cash) {\n Wallet w = ensureWallet(userId);\n w.addAvailableCash(cash);\n }\n\n @Override\n public Wallet getWallet(String userId) {\n return wallets.get(userId);\n }\n\n @Override\n public void settleTrade(Trade trade) {\n Order buy = orderRepository.findById(trade.getBuyOrderId()).orElse(null);\n Order sell = orderRepository.findById(trade.getSellOrderId()).orElse(null);\n\n if (buy == null || sell == null) return;\n\n Wallet buyerWallet = ensureWallet(buy.getUserId());\n Wallet sellerWallet = ensureWallet(sell.getUserId());\n Reservation buyRes = reservations.get(buy.getId());\n Reservation sellRes = reservations.get(sell.getId());\n\n int qty = (int) trade.getQuantity();\n long tradeValue = trade.getPrice() * (long) qty;\n String instrument = buy.getInstrument();\n // Process Buy Side\n if (buyRes != null) {\n long cashtoDebitFromReserved = buyRes.reduceBy(qty);\n buyerWallet.debitReservedCash(cashtoDebitFromReserved);\n\n long refund = cashtoDebitFromReserved - tradeValue;\n if (refund > 0) buyerWallet.addAvailableCash(refund);\n\n buyerWallet.addAvailableShares(instrument, qty);\n }\n\n // Process Sell Side\n if (sellRes != null) {\n sellRes.reduceBy(qty);\n sellerWallet.debitReservedShares(instrument, (long) qty);\n sellerWallet.addAvailableCash(tradeValue);\n }\n\n // Cleanup completed reservations\n if (buyRes != null && buyRes.getRemainingQty() == 0) {\n reservations.remove(buy.getId());\n }\n if (sellRes != null && sellRes.getRemainingQty() == 0) {\n reservations.remove(sell.getId());\n }\n }\n} -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java b/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java ---- a/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java -+++ b/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java -@@ -8,6 +8,7 @@ - import org.example.matching.orderbook.OrderRepository; - import org.springframework.stereotype.Service; // Add this import - -+import java.util.Collection; - import java.util.Map; - import java.util.concurrent.ConcurrentHashMap; - -@@ -93,6 +94,10 @@ - public Wallet getWallet(String userId) { - return wallets.get(userId); - } -+ @Override -+ public Collection getAllWallets() { -+ return wallets.values(); -+ } - - @Override - public void settleTrade(Trade trade) { -Index: matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/app/MatchingEngineApplication.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/journal/EventJournal.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Trade.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/MatchingEngineApplication.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/WalletResponse.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/OrderOrchestrator.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/OrderBook.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Wallet.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/PriceLevel.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderResponse.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/advice/ApiExceptionHandler.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/validation/OrderValidator.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/OrderRepository.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/Main.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Order.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Reservation.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderBookResponse.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/DepositRequest.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderRequest.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Replay.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/util/IdGenerator.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/OrderSide.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderMapper.java\n/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java\n -=================================================================== -diff --git a/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ---- a/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst -+++ b/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst -@@ -3,17 +3,22 @@ - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Trade.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/MatchingEngineApplication.java -+/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/WalletResponse.java -+/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/OrderOrchestrator.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/OrderBook.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java -+/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Wallet.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/PriceLevel.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderResponse.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/advice/ApiExceptionHandler.java -+/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/EventStatus.java -+/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/validation/OrderValidator.java -@@ -27,6 +32,8 @@ - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderRequest.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java -+/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/EventRequest.java -+/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Replay.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/util/IdGenerator.java - /Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/OrderSide.java -Index: journals/engine.log -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>ORDER 555a6afb-fb3e-41c5-9544-cdbc96ed4fa2 seller1 SELL 100 10 1771557534336\nORDER c5077dcb-c587-4543-9c0b-3da1589504a8 buyer1 BUY 105 4 1771557534365\nTRADE c5077dcb-c587-4543-9c0b-3da1589504a8 555a6afb-fb3e-41c5-9544-cdbc96ed4fa2 100 4 1771557534365\nORDER f4b788e6-632c-48c1-9eff-30880619b16d buyer2 BUY 100 6 1771557534365\nTRADE f4b788e6-632c-48c1-9eff-30880619b16d 555a6afb-fb3e-41c5-9544-cdbc96ed4fa2 100 6 1771557534366\nORDER 729e42d0-bf4a-4dbd-a666-3adb0fd9c136 seller1 SELL 100 10 1772083565558\nORDER c64a2e5d-1e54-4568-bee3-990fe3cde36b buyer1 BUY 105 4 1772083565592\nTRADE c64a2e5d-1e54-4568-bee3-990fe3cde36b 729e42d0-bf4a-4dbd-a666-3adb0fd9c136 100 4 1772083565592\nORDER 92220288-5c9e-4a1a-9a10-88d14f297953 buyer2 BUY 100 6 1772083565593\nTRADE 92220288-5c9e-4a1a-9a10-88d14f297953 729e42d0-bf4a-4dbd-a666-3adb0fd9c136 100 6 1772083565593\nORDER 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc seller1 SELL 100 10\nORDER 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc seller1 SELL 100 10 1772083565594\nORDER 278fdfd7-6893-4d50-923a-0a694dca0884 buyer1 BUY 105 4\nORDER 278fdfd7-6893-4d50-923a-0a694dca0884 buyer1 BUY 105 4 1772083565594\nTRADE 278fdfd7-6893-4d50-923a-0a694dca0884 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc 100 4 1772083565594\nTRADE 278fdfd7-6893-4d50-923a-0a694dca0884 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc 100 4\nORDER 02956e10-2d87-4a95-93e1-5c9de14c0d0b buyer1 BUY 100 6\nORDER 02956e10-2d87-4a95-93e1-5c9de14c0d0b buyer1 BUY 100 6 1772083565594\nTRADE 02956e10-2d87-4a95-93e1-5c9de14c0d0b 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc 100 6 1772083565595\nTRADE 02956e10-2d87-4a95-93e1-5c9de14c0d0b 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc 100 6\nORDER 64cbfaf9-f583-44fc-a836-1187c5003ebe seller1 SELL 100 10 1772670798630\nORDER d69ce365-223d-4776-a5dd-d30cc7a8a912 buyer1 BUY 105 4 1772670798732\nTRADE d69ce365-223d-4776-a5dd-d30cc7a8a912 64cbfaf9-f583-44fc-a836-1187c5003ebe 100 4 1772670798733\nORDER 8891349c-44e5-408c-8ce0-989ec39e24ca buyer2 BUY 100 6 1772670798734\nTRADE 8891349c-44e5-408c-8ce0-989ec39e24ca 64cbfaf9-f583-44fc-a836-1187c5003ebe 100 6 1772670798734\nORDER 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 seller1 SELL 100 10\nORDER 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 seller1 SELL 100 10 1772670871847\nORDER 0b5e5139-84c9-4698-a906-e868092f3308 buyer1 BUY 105 4\nORDER 0b5e5139-84c9-4698-a906-e868092f3308 buyer1 BUY 105 4 1772670872856\nTRADE 0b5e5139-84c9-4698-a906-e868092f3308 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 100 4 1772671176205\nTRADE 0b5e5139-84c9-4698-a906-e868092f3308 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 100 4\nORDER b2557f88-8c0f-446d-8e70-010b17b2c2c8 buyer1 BUY 100 6\nORDER b2557f88-8c0f-446d-8e70-010b17b2c2c8 buyer1 BUY 100 6 1772670876431\nTRADE b2557f88-8c0f-446d-8e70-010b17b2c2c8 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 100 6 1772671176208\nTRADE b2557f88-8c0f-446d-8e70-010b17b2c2c8 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 100 6\nORDER 9ebc90e5-eac2-4ed2-bd23-87dc5f9fad78 seller1 SELL 100 10 1772671494015\nORDER e6791c03-baf8-4f62-8742-ca2046593854 buyer1 BUY 105 4 1772671494047\nTRADE e6791c03-baf8-4f62-8742-ca2046593854 9ebc90e5-eac2-4ed2-bd23-87dc5f9fad78 100 4 1772671494047\nORDER d6177034-7805-4d88-a13c-ccff65a03a40 buyer2 BUY 100 6 1772671494049\nTRADE d6177034-7805-4d88-a13c-ccff65a03a40 9ebc90e5-eac2-4ed2-bd23-87dc5f9fad78 100 6 1772671494049\nORDER 759faf99-a021-4608-bdc7-656bfa915a95 seller1 SELL 100 10\nORDER 759faf99-a021-4608-bdc7-656bfa915a95 seller1 SELL 100 10 1772671494058\nORDER f3bd642f-8fa9-4d48-94e7-04bcbb39e49a buyer1 BUY 105 4\nORDER f3bd642f-8fa9-4d48-94e7-04bcbb39e49a buyer1 BUY 105 4 1772671494058\nTRADE f3bd642f-8fa9-4d48-94e7-04bcbb39e49a 759faf99-a021-4608-bdc7-656bfa915a95 100 4 1772671494060\nTRADE f3bd642f-8fa9-4d48-94e7-04bcbb39e49a 759faf99-a021-4608-bdc7-656bfa915a95 100 4\nORDER 9a5d71f3-1351-403f-a9f6-019f3a5657af buyer1 BUY 100 6\nORDER 9a5d71f3-1351-403f-a9f6-019f3a5657af buyer1 BUY 100 6 1772671494058\nTRADE 9a5d71f3-1351-403f-a9f6-019f3a5657af 759faf99-a021-4608-bdc7-656bfa915a95 100 6 1772671494061\nTRADE 9a5d71f3-1351-403f-a9f6-019f3a5657af 759faf99-a021-4608-bdc7-656bfa915a95 100 6\nORDER cd18202b-085c-44d6-a5b6-3598fe8494e6 seller1 SELL 100 10 1772752285238\nORDER cfe88003-6f8f-4a3a-8236-27b7c139f04d buyer1 BUY 105 4 1772752285262\nTRADE cfe88003-6f8f-4a3a-8236-27b7c139f04d cd18202b-085c-44d6-a5b6-3598fe8494e6 100 4 1772752285262\nORDER 6b68b475-9229-4bc2-aa10-66298c6e9928 buyer2 BUY 100 6 1772752285264\nTRADE 6b68b475-9229-4bc2-aa10-66298c6e9928 cd18202b-085c-44d6-a5b6-3598fe8494e6 100 6 1772752285264\nORDER 28e6dec6-2781-4ad0-8230-7cc1de99f41e seller1 SELL 100 10\nORDER 28e6dec6-2781-4ad0-8230-7cc1de99f41e seller1 SELL 100 10 1772752285268\nORDER 4e0a0c16-518b-42c4-b40b-4504645951d4 buyer1 BUY 105 4\nORDER 4e0a0c16-518b-42c4-b40b-4504645951d4 buyer1 BUY 105 4 1772752285268\nTRADE 4e0a0c16-518b-42c4-b40b-4504645951d4 28e6dec6-2781-4ad0-8230-7cc1de99f41e 100 4 1772752285270\nTRADE 4e0a0c16-518b-42c4-b40b-4504645951d4 28e6dec6-2781-4ad0-8230-7cc1de99f41e 100 4\nORDER ee65a46b-8063-4a87-b6bc-ee9301e48e35 buyer1 BUY 100 6\nORDER ee65a46b-8063-4a87-b6bc-ee9301e48e35 buyer1 BUY 100 6 1772752285268\nTRADE ee65a46b-8063-4a87-b6bc-ee9301e48e35 28e6dec6-2781-4ad0-8230-7cc1de99f41e 100 6 1772752285270\nTRADE ee65a46b-8063-4a87-b6bc-ee9301e48e35 28e6dec6-2781-4ad0-8230-7cc1de99f41e 100 6\nORDER_PLACED: c61d527c-d5a2-46cd-b7bd-1e2e7dd1813b\nORDER c61d527c-d5a2-46cd-b7bd-1e2e7dd1813b user123 BUY 150 10 1772754586115\nORDER_PLACED: 9f12d7ee-5ad2-4702-83fb-4fd4faa682fe\nORDER 9f12d7ee-5ad2-4702-83fb-4fd4faa682fe user123 BUY 150 10 1772755348807\nORDER_PLACED: 4cef0377-341d-4b13-8856-f44b1ad24656\nORDER 4cef0377-341d-4b13-8856-f44b1ad24656 user-seller SELL 150 10 1772756125478\nORDER_PLACED: 44655cad-3469-4cfb-899c-c1dacd78ccda\nORDER 44655cad-3469-4cfb-899c-c1dacd78ccda user1 BUY 100 10 1772756456024\nORDER_PLACED: a16f2f1b-2e4b-48f0-8a6f-7ce51f4bc94b\nORDER a16f2f1b-2e4b-48f0-8a6f-7ce51f4bc94b user-seller SELL 150 10 1772759912084\nORDER_PLACED: a1d913c4-317e-4f88-9c2e-05c2d422d7bb\nORDER a1d913c4-317e-4f88-9c2e-05c2d422d7bb user1 BUY 100 10 1772759987885\nORDER_PLACED: 3db5fb8c-f24b-4674-8a4b-cbadc7fede32\nORDER 3db5fb8c-f24b-4674-8a4b-cbadc7fede32 user-seller SELL 150 10 1772760831791\nORDER_PLACED: 562de0cc-8ba3-4264-8158-98904cbce14d\nORDER 562de0cc-8ba3-4264-8158-98904cbce14d user1 BUY 100 10 1772760849537\nORDER_PLACED: 9320ee7a-9fef-4449-bf80-f2c2ef83cc88\nORDER 9320ee7a-9fef-4449-bf80-f2c2ef83cc88 user-seller SELL 150 10 1772761342572\nORDER_PLACED: b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f\nORDER b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f user1 BUY 151 10 1772761362103\nTRADE b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f 9320ee7a-9fef-4449-bf80-f2c2ef83cc88 150 10 1772761362107\nTRADE_SETTLED: b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f <-> 9320ee7a-9fef-4449-bf80-f2c2ef83cc88\n -=================================================================== -diff --git a/journals/engine.log b/journals/engine.log ---- a/journals/engine.log -+++ b/journals/engine.log -@@ -85,3 +85,45 @@ - ORDER b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f user1 BUY 151 10 1772761362103 - TRADE b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f 9320ee7a-9fef-4449-bf80-f2c2ef83cc88 150 10 1772761362107 - TRADE_SETTLED: b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f <-> 9320ee7a-9fef-4449-bf80-f2c2ef83cc88 -+ORDER_PLACED: a739b58c-1b61-481a-bbda-d32961c6bee7 -+ORDER a739b58c-1b61-481a-bbda-d32961c6bee7 user_1 BUY 70 10 1772929548693 -+ORDER_PLACED: 5bc417f4-bafc-4515-87d8-6ff10f310edb -+ORDER 5bc417f4-bafc-4515-87d8-6ff10f310edb user_1 BUY 70 10 1772930035271 -+ORDER_PLACED: 00740b55-39bc-47c7-b210-049ff138ff88 -+ORDER 00740b55-39bc-47c7-b210-049ff138ff88 USER_A BUY 60 10 1773016937098 -+ORDER_PLACED: 7e7fcd36-dfd2-46d2-84f3-0759ec1722c2 -+ORDER 7e7fcd36-dfd2-46d2-84f3-0759ec1722c2 HOUSE_BOT BUY 26 500 1773098676011 -+ORDER_PLACED: 49bd0b23-73e6-4bb2-a04c-788b0d27a61b -+ORDER 49bd0b23-73e6-4bb2-a04c-788b0d27a61b HOUSE_BOT SELL 30 500 1773098676033 -+ORDER_PLACED: 0b21f612-5b80-468c-a261-5f92cc509c7f -+ORDER 0b21f612-5b80-468c-a261-5f92cc509c7f HOUSE_BOT BUY 70 500 1773098676038 -+ORDER_PLACED: bebf1c67-fd3c-4d66-9ef0-275cdd5b2391 -+ORDER bebf1c67-fd3c-4d66-9ef0-275cdd5b2391 HOUSE_BOT SELL 74 500 1773098676038 -+ORDER_PLACED: 2e1f64a8-c0cd-4999-8a7b-99dbe3485bec -+ORDER 2e1f64a8-c0cd-4999-8a7b-99dbe3485bec HOUSE_BOT BUY 26 500 1773098788582 -+ORDER_PLACED: 337504bc-fae7-422a-8718-9f12a60b9c86 -+ORDER 337504bc-fae7-422a-8718-9f12a60b9c86 HOUSE_BOT SELL 30 500 1773098788584 -+ORDER_PLACED: 2ceb4d50-197f-4740-9ca2-6993a13f601c -+ORDER 2ceb4d50-197f-4740-9ca2-6993a13f601c HOUSE_BOT BUY 70 500 1773098788584 -+ORDER_PLACED: d456036f-200d-422c-9d23-adfb020b6bf3 -+ORDER d456036f-200d-422c-9d23-adfb020b6bf3 HOUSE_BOT SELL 74 500 1773098788584 -+ORDER_PLACED: f88fb1b7-a716-411c-9ac8-9cda420c12dd -+ORDER f88fb1b7-a716-411c-9ac8-9cda420c12dd HOUSE_BOT BUY 26 500 1773099262674 -+ORDER_PLACED: 6a9e9b62-c69b-441d-b48d-d679023a5a05 -+ORDER 6a9e9b62-c69b-441d-b48d-d679023a5a05 HOUSE_BOT SELL 30 500 1773099262677 -+ORDER_PLACED: 6cc5ecdc-6538-433f-8bd8-fb8e69f5f3ba -+ORDER 6cc5ecdc-6538-433f-8bd8-fb8e69f5f3ba HOUSE_BOT BUY 70 500 1773099262677 -+ORDER_PLACED: 09a40c7c-7730-4a7d-af85-1be051cc62ad -+ORDER 09a40c7c-7730-4a7d-af85-1be051cc62ad HOUSE_BOT SELL 74 500 1773099262677 -+ORDER_PLACED: a13e5263-410d-4fdf-bfcc-be134a8e502a -+ORDER a13e5263-410d-4fdf-bfcc-be134a8e502a trader1 BUY 30 10 1773099371840 -+TRADE a13e5263-410d-4fdf-bfcc-be134a8e502a 6a9e9b62-c69b-441d-b48d-d679023a5a05 30 10 1773099371841 -+TRADE_SETTLED: a13e5263-410d-4fdf-bfcc-be134a8e502a <-> 6a9e9b62-c69b-441d-b48d-d679023a5a05 -+ORDER_PLACED: db7dd415-686b-4ede-acc6-6a11e42d22f0 -+ORDER db7dd415-686b-4ede-acc6-6a11e42d22f0 HOUSE_BOT BUY 27 500 1773099371844 -+ORDER_PLACED: 585695d4-f70c-4e10-a01b-0f915f3931a2 -+ORDER 585695d4-f70c-4e10-a01b-0f915f3931a2 HOUSE_BOT SELL 31 500 1773099371844 -+ORDER_PLACED: 2dbeef1a-6788-42a0-ac73-08daa5029908 -+ORDER 2dbeef1a-6788-42a0-ac73-08daa5029908 HOUSE_BOT BUY 69 500 1773099371844 -+ORDER_PLACED: cd48551d-9285-4e3a-8691-17d6dcebb4f0 -+ORDER cd48551d-9285-4e3a-8691-17d6dcebb4f0 HOUSE_BOT SELL 73 500 1773099371844 -Index: matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>package org.example.matching.api.controller;\n\nimport lombok.RequiredArgsConstructor;\nimport org.example.matching.Wallets.WalletService;\nimport org.example.matching.api.dto.DepositRequest;\nimport org.example.matching.api.dto.WalletResponse;\nimport org.example.matching.model.Wallet;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n@RestController\n@RequestMapping(\"/api/wallets\")\n@RequiredArgsConstructor\npublic class WalletController {\n\n private final WalletService walletService;\n\n @GetMapping(\"/{userId}\")\n public ResponseEntity getWallet(@PathVariable String userId){\n Wallet wallet = walletService.getWallet(userId);\n\n // Convert AtomicLong maps to Long maps for JSON response\n Map availableShares = wallet.getAvailableShares().entrySet().stream()\n .collect(Collectors.toMap(\n Map.Entry::getKey,\n entry -> entry.getValue().get()\n ));\n\n Map reservedShares = wallet.getReservedShares().entrySet().stream()\n .collect(Collectors.toMap(\n Map.Entry::getKey,\n entry -> entry.getValue().get()\n ));\n\n WalletResponse response = WalletResponse.builder()\n .userId(userId)\n .availableCash(wallet.getAvailableCash())\n .reservedCash(wallet.getReservedCash())\n .availableShares(availableShares)\n .reservedShares(reservedShares)\n .build();\n return ResponseEntity.ok(response);\n }\n @PostMapping(\"/depositCash\")\n public ResponseEntity depositCash(@RequestBody DepositRequest request) {\n walletService.creditUserCash(request.getUserId(), request.getAmount());\n return ResponseEntity.ok(\"Deposit Successful\");\n }\n \n @PostMapping(\"/depositShares\")\n public ResponseEntity depositShares(@RequestBody DepositRequest request) {\n // Credit shares for the specific instrument\n walletService.creditUserShares(request.getUserId(), request.getInstrument(), request.getAmount());\n return ResponseEntity.ok(\"Shares Deposited Successfully\");\n }\n} -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java b/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java ---- a/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java -+++ b/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java -@@ -21,6 +21,18 @@ - @GetMapping("/{userId}") - public ResponseEntity getWallet(@PathVariable String userId){ - Wallet wallet = walletService.getWallet(userId); -+ -+ // Handle non-existent wallet -+ if (wallet == null) { -+ WalletResponse response = WalletResponse.builder() -+ .userId(userId) -+ .availableCash(0L) -+ .reservedCash(0L) -+ .availableShares(Map.of()) -+ .reservedShares(Map.of()) -+ .build(); -+ return ResponseEntity.ok(response); -+ } - - // Convert AtomicLong maps to Long maps for JSON response - Map availableShares = wallet.getAvailableShares().entrySet().stream() -Index: matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>org/example/matching/api/controller/MarketController.class\norg/example/Main.class\norg/example/matching/app/MatchingEngineApplication.class\norg/example/matching/model/Trade.class\norg/example/matching/model/OrderSide.class\norg/example/matching/api/dto/OrderResponse.class\norg/example/matching/journal/EventJournal.class\norg/example/matching/Wallets/WalletService.class\norg/example/matching/Wallets/InMemoryWalletService.class\norg/example/matching/model/Reservation.class\norg/example/matching/validation/OrderValidator.class\norg/example/matching/api/controller/OrderController.class\norg/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class\norg/example/matching/api/advice/ApiExceptionHandler.class\norg/example/matching/model/OrderBook.class\norg/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class\norg/example/matching/orderbook/OrderOrchestrator.class\norg/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class\norg/example/matching/orderbook/InMemoryOrderRepository.class\norg/example/matching/matching/MatchingEngine.class\norg/example/matching/model/Order.class\norg/example/matching/api/dto/DepositRequest.class\norg/example/matching/api/dto/PriceLevel.class\norg/example/matching/api/dto/WalletResponse.class\norg/example/matching/Replay.class\norg/example/matching/util/IdGenerator.class\norg/example/matching/api/controller/WalletController.class\norg/example/matching/Wallets/RiskManager.class\norg/example/matching/api/dto/OrderMapper.class\norg/example/matching/api/dto/OrderBookResponse.class\norg/example/matching/api/dto/OrderRequest.class\norg/example/matching/config/MatchingEngineConfig.class\norg/example/matching/api/service/MarketDataService.class\norg/example/matching/orderbook/OrderRepository.class\norg/example/matching/MatchingEngineApplication.class\norg/example/matching/model/Wallet.class\norg/example/matching/api/service/OrderService.class\n -=================================================================== -diff --git a/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ---- a/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst -+++ b/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst -@@ -1,5 +1,6 @@ - org/example/matching/api/controller/MarketController.class - org/example/Main.class -+org/example/matching/api/service/MarketManagmentService.class - org/example/matching/app/MatchingEngineApplication.class - org/example/matching/model/Trade.class - org/example/matching/model/OrderSide.class -@@ -10,6 +11,7 @@ - org/example/matching/model/Reservation.class - org/example/matching/validation/OrderValidator.class - org/example/matching/api/controller/OrderController.class -+org/example/matching/api/controller/EventController.class - org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class - org/example/matching/api/advice/ApiExceptionHandler.class - org/example/matching/model/OrderBook.class -@@ -18,6 +20,7 @@ - org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class - org/example/matching/orderbook/InMemoryOrderRepository.class - org/example/matching/matching/MatchingEngine.class -+org/example/matching/api/dto/EventRequest.class - org/example/matching/model/Order.class - org/example/matching/api/dto/DepositRequest.class - org/example/matching/api/dto/PriceLevel.class -@@ -27,11 +30,16 @@ - org/example/matching/api/controller/WalletController.class - org/example/matching/Wallets/RiskManager.class - org/example/matching/api/dto/OrderMapper.class -+org/example/matching/api/service/LiquidBotService.class - org/example/matching/api/dto/OrderBookResponse.class -+org/example/matching/api/dto/MarketEvent.class - org/example/matching/api/dto/OrderRequest.class - org/example/matching/config/MatchingEngineConfig.class -+org/example/matching/api/dto/MarketEvent$MarketEventBuilder.class - org/example/matching/api/service/MarketDataService.class - org/example/matching/orderbook/OrderRepository.class -+org/example/matching/api/service/SettlementService.class -+org/example/matching/api/dto/EventStatus.class - org/example/matching/MatchingEngineApplication.class - org/example/matching/model/Wallet.class - org/example/matching/api/service/OrderService.class diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/InMemoryWalletService.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/InMemoryWalletService.class" deleted file mode 100644 index 9dbce51..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/InMemoryWalletService.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MarketController.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MarketController.class" deleted file mode 100644 index 01508e2..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MarketController.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngine.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngine.class" deleted file mode 100644 index e303a3f..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngine.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngineApplication.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngineApplication.class" deleted file mode 100644 index 9d9c000..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngineApplication.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngineConfig.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngineConfig.class" deleted file mode 100644 index 8566c3b..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/MatchingEngineConfig.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/OrderService.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/OrderService.class" deleted file mode 100644 index db23766..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/OrderService.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/Reservation.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/Reservation.class" deleted file mode 100644 index 79bf7e0..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/Reservation.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/WalletController.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/WalletController.class" deleted file mode 100644 index 5d8e922..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/WalletController.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/WalletService.class" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/WalletService.class" deleted file mode 100644 index e4c2454..0000000 Binary files "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/WalletService.class" and /dev/null differ diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/shelved.patch" "b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/shelved.patch" deleted file mode 100644 index 60eb661..0000000 --- "a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26,_7_40\342\200\257pm_[Changes]1/shelved.patch" +++ /dev/null @@ -1,196 +0,0 @@ -Index: matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java b/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java -new file mode 100644 ---- /dev/null -+++ b/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java -@@ -0,0 +1,65 @@ -+package org.example.matching.api.service; -+ -+import lombok.RequiredArgsConstructor; -+import org.example.matching.Wallets.RiskManager; -+import org.example.matching.Wallets.WalletService; -+import org.example.matching.api.dto.EventStatus; -+import org.example.matching.api.dto.MarketEvent; -+import org.example.matching.matching.MatchingEngine; -+import org.example.matching.model.Order; -+import org.example.matching.model.Wallet; -+import org.springframework.stereotype.Service; -+ -+import java.util.Collection; -+import java.util.List; -+ -+@Service -+@RequiredArgsConstructor -+public class SettlementService { -+ private final WalletService walletService; -+ private final MarketManagmentService marketService; -+ private final RiskManager riskManager; -+ private final MatchingEngine matchingEngine; -+ -+ -+ public void settleEvent(String eventId, String winningOutcome){ -+ MarketEvent event = marketService.getEvent(eventId); -+ if(event==null|| event.getStatus()== EventStatus.SETTLED) return;; -+ -+ // winningTicker will get assigned the outcome , if it is yes , the winningTicker is yes and no so winningTicker is No -+ String winningTicker = winningOutcome.equalsIgnoreCase("YES")? event.getYesTicker():event.getNoTicker(); -+ -+ //losingTicker will get assigned the outcome , if it is yes , the losingTicker is yes and no so losingTicker is NOx -+ String losingTicker = winningOutcome.equalsIgnoreCase("YES")?event.getNoTicker():event.getYesTicker(); -+ -+ Collection allWallets = walletService.getAllWallets(); -+ -+ for (Wallet wallet : allWallets) { -+ // 2. Pay out the Winners ($1.00 per share) -+ -+ //checking how many shares of yes one has -+ long winningShares = wallet.getAvailableShares().containsKey(winningTicker) -+ ? wallet.getAvailableShares().get(winningTicker).get() -+ : 0L; if (winningShares > 0) { -+ long payout = winningShares * 100; // 100 cents = $1.00 -+ wallet.addAvailableCash(payout); -+ wallet.getAvailableShares().remove(winningTicker); -+ } -+ -+ // 3. Remove the Losing Shares (Worth $0) -+ wallet.getAvailableShares().remove(losingTicker); -+ } -+ -+ event.setStatus(EventStatus.SETTLED); -+ event.setOutcome(winningOutcome); -+ } -+ -+ -+ //check if walletService or RiskManager -+ private void refundTicker(String ticker){ -+ List orders = matchingEngine.clearBook(ticker); -+ for (Order o:orders){ -+ walletService.releaseReservation(o.getId()); -+ } -+ } -+} -\ No newline at end of file -Index: matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java b/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java -new file mode 100644 ---- /dev/null -+++ b/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java -@@ -0,0 +1,79 @@ -+package org.example.matching.api.service; -+ -+import org.example.matching.Wallets.WalletService; -+import org.example.matching.api.dto.EventStatus; -+import org.example.matching.api.dto.MarketEvent; -+import org.example.matching.api.service.LiquidBotService; -+import org.springframework.context.annotation.Lazy; -+import org.springframework.stereotype.Service; -+ -+import java.util.Map; -+import java.util.concurrent.ConcurrentHashMap; -+ -+@Service -+public class MarketManagmentService { -+ private final WalletService walletService; -+ private final LiquidBotService liquidBotService; -+ -+ // Stores Event by ID -+ private final Map events = new ConcurrentHashMap<>(); -+ // Stores Ticker Pairs (YES <-> NO) -+ private final Map tickerPairs = new ConcurrentHashMap<>(); -+ // NEW: Maps Tickers to the Event they belong to -+ private final Map tickerToEvent = new ConcurrentHashMap<>(); -+ -+ // Use constructor injection to avoid circular dependency issues -+ public MarketManagmentService(WalletService walletService, @Lazy LiquidBotService liquidBotService) { -+ this.walletService = walletService; -+ this.liquidBotService = liquidBotService; -+ } -+ -+ public void registerEvent(MarketEvent event) { -+ tickerPairs.put(event.getYesTicker(), event.getNoTicker()); -+ tickerPairs.put(event.getNoTicker(), event.getYesTicker()); -+ -+ // Map both tickers to this event object -+ tickerToEvent.put(event.getYesTicker(), event); -+ tickerToEvent.put(event.getNoTicker(), event); -+ } -+ -+ public String getPartnerTicker(String ticker) { -+ return tickerPairs.get(ticker); -+ } -+ -+ public MarketEvent getEvent(String eventId) { -+ return events.get(eventId); -+ } -+ -+ public MarketEvent getEventByTicker(String ticker) { -+ return tickerToEvent.get(ticker); -+ } -+ -+ public MarketEvent createEvent(String id, String question, String yesTicker, String noTicker, int minutesFromNow) { -+ MarketEvent event = MarketEvent.builder() -+ .evendID(id) -+ .questions(question) -+ .yesTicker(yesTicker) -+ .noTicker(noTicker) -+ .expiry(java.time.LocalDateTime.now().plusMinutes(minutesFromNow)) -+ .status(EventStatus.OPEN) -+ .Liquidity(100000) // Default liquidity -+ .build(); -+ -+ events.put(id, event); -+ registerEvent(event); // Populate the lookup maps -+ -+ walletService.creditUserShares("HOUSE_BOT", yesTicker, 100000); -+ walletService.creditUserShares("HOUSE_BOT", noTicker, 100000); -+ -+ // Start the bot for this specific event -+ liquidBotService.updateMarketTrigger(yesTicker); -+ return event; -+ } -+ -+ public java.util.Collection getAllOpenEvents() { -+ return events.values().stream() -+ .filter(event -> event.getStatus() == EventStatus.OPEN) -+ .collect(java.util.stream.Collectors.toList()); -+ } -+} -\ No newline at end of file -Index: matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java b/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java -new file mode 100644 ---- /dev/null -+++ b/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java -@@ -0,0 +1,20 @@ -+package org.example.matching.api.dto; -+ -+import lombok.Builder; -+import lombok.Data; -+ -+import java.time.LocalDateTime; -+ -+@Data -+@Builder -+public class MarketEvent { -+ private String evendID; -+ private String questions; -+ private String yesTicker; -+ private String noTicker; -+ private long Liquidity; -+ private LocalDateTime expiry; //event will expire on this date , either by or oracle -+ private EventStatus status; -+ private String outcome; -+ -+} diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26__7_40pm__Changes_.xml b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26__7_40pm__Changes_.xml deleted file mode 100644 index 612f621..0000000 --- a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26__7_40pm__Changes_.xml +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26__7_40pm__Changes_1.xml b/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26__7_40pm__Changes_1.xml deleted file mode 100644 index cab4021..0000000 --- a/.idea/shelf/Uncommitted_changes_before_Update_at_09_03_26__7_40pm__Changes_1.xml +++ /dev/null @@ -1,49 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 62f171b..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1771215102171 - - - 1771648338680 - - - 1771648512676 - - - 1771716905485 - - - 1771716936127 - - - 1771716951142 - - - 1771716972703 - - - 1771716983118 - - - 1771717007244 - - - 1771820464482 - - - 1771820479411 - - - 1771820497836 - - - 1772671560856 - - - 1772671600544 - - - 1772671662807 - - - 1772671697081 - - - 1772671751384 - - - 1772680635917 - - - 1772684678651 - - - 1772684723158 - - - 1772684747085 - - - 1772684770758 - - - 1772684815676 - - - 1772752466221 - - - 1772752500293 - - - 1772752539039 - - - 1772752591131 - - - 1772752604653 - - - 1772752666287 - - - 1772753404093 - - - 1772765628233 - - - 1772837990490 - - - 1772949584599 - - - 1773099625894 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 12dbdd6..601cd26 100644 --- a/README.md +++ b/README.md @@ -1 +1,145 @@ -Max is Stock Exchange being written in Spring Boot +# Max - Stock Exchange Platform + +Max is a modern stock exchange platform built with a Spring Boot backend and React frontend. The platform provides real-time trading capabilities with a sleek, responsive user interface. + +## 🏗️ Project Structure + +``` +Max/ +├── backend/ # Spring Boot backend services +│ ├── matching-engine/ # Order matching engine +│ └── common/ # Shared utilities and models +└── frontend/ # React + TypeScript frontend + └── src/ + ├── components/ # Reusable UI components + └── pages/ # Application pages +``` + +## 🚀 Features + +- **Real-time Trading**: High-performance order matching engine +- **Responsive Design**: Mobile-first UI with tablet and desktop support +- **Modern UI/UX**: Built with React 19 and Tailwind CSS 4 +- **Category Navigation**: Browse markets by categories (Trending, Politics, Sports, Crypto, etc.) +- **Search Functionality**: Advanced search with smooth animations +- **User Authentication**: Login and signup functionality + +## 🛠️ Tech Stack + +### Backend +- **Framework**: Spring Boot 3.2.2 +- **Language**: Java +- **Build Tool**: Maven +- **Key Dependencies**: + - Spring Web + - Spring Validation + - Lombok + +### Frontend +- **Framework**: React 19.2.0 +- **Language**: TypeScript 5.9.3 +- **Build Tool**: Vite 7.3.1 +- **Styling**: Tailwind CSS 4.2.1 +- **Routing**: React Router DOM 7.13.1 + +## 📋 Prerequisites + +- **Java**: JDK 17 or higher +- **Node.js**: v18 or higher +- **npm**: v9 or higher +- **Maven**: 3.6 or higher + +## 🚀 Quick Start + +### 1. Clone the Repository +```bash +git clone +cd Max +``` + +### 2. Start Backend +```bash +cd backend/matching-engine +mvn clean install +mvn spring-boot:run +``` + +The backend will start on `http://localhost:8080` + +### 3. Start Frontend +```bash +cd frontend +npm install +npm run dev +``` + +The frontend will start on `http://localhost:5173` + +## 📚 Documentation + +For detailed setup and development instructions, see: +- [Backend README](./backend/README.md) +- [Frontend README](./frontend/README.md) + +## 🎨 UI Features + +- **Responsive Header**: Adaptive navigation for mobile, tablet, and desktop +- **Category Bar**: Horizontal scrolling with mouse wheel support +- **Search Modal**: Animated search with blur background +- **Mobile Menu**: Bottom navigation for mobile devices +- **Custom Animations**: Shake effects and smooth transitions + +## 🔧 Development + +### Backend Development +```bash +cd backend/matching-engine +mvn spring-boot:run +``` + +### Frontend Development +```bash +cd frontend +npm run dev +``` + +### Build for Production + +**Backend:** +```bash +cd backend/matching-engine +mvn clean package +``` + +**Frontend:** +```bash +cd frontend +npm run build +``` + +## 📝 Project Status + +This project is currently in active development. Features are being added and refined regularly. + +## 🤝 Contributing + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +## 📄 License + +This project is private and proprietary. + +## 👥 Authors + +- Aum Patel +- Ved Patel + +## 🙏 Acknowledgments + +- Spring Boot team for the excellent framework +- React team for the powerful UI library +- Tailwind CSS for the utility-first CSS framework diff --git a/Strongx.iml b/Strongx.iml deleted file mode 100644 index 1f9993e..0000000 --- a/Strongx.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/backend/.gitignore b/backend/.gitignore index 723ef36..12d32f9 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1 +1,22 @@ -.idea \ No newline at end of file +# Compiled output +target/ +*.class + +# IDE +.idea/workspace.xml +.idea/shelf/ +.idea/libraries/ +.idea/modules.xml +*.iml + +# Logs +*.log +journals/ + +# Claude +.claude/ + +# OS +.DS_Store + +.idea/ \ No newline at end of file diff --git a/backend/common/pom.xml b/backend/common/pom.xml index 4b082cf..e65e5d0 100644 --- a/backend/common/pom.xml +++ b/backend/common/pom.xml @@ -11,6 +11,7 @@ org.example Strongx 1.0-SNAPSHOT + ../src/pom.xml common diff --git a/backend/common/target/classes/org/example/Main.class b/backend/common/target/classes/org/example/Main.class deleted file mode 100644 index c37bb3d..0000000 Binary files a/backend/common/target/classes/org/example/Main.class and /dev/null differ diff --git a/backend/common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/backend/common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst deleted file mode 100644 index 492813d..0000000 --- a/backend/common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ /dev/null @@ -1 +0,0 @@ -org/example/Main.class diff --git a/backend/common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/backend/common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst deleted file mode 100644 index e5787ba..0000000 --- a/backend/common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ /dev/null @@ -1 +0,0 @@ -/Users/aumpatel/Desktop/Spring/Strongx/common/src/main/java/org/example/Main.java diff --git a/backend/journals/engine.log b/backend/journals/engine.log deleted file mode 100644 index a19b55f..0000000 --- a/backend/journals/engine.log +++ /dev/null @@ -1,87 +0,0 @@ -ORDER 555a6afb-fb3e-41c5-9544-cdbc96ed4fa2 seller1 SELL 100 10 1771557534336 -ORDER c5077dcb-c587-4543-9c0b-3da1589504a8 buyer1 BUY 105 4 1771557534365 -TRADE c5077dcb-c587-4543-9c0b-3da1589504a8 555a6afb-fb3e-41c5-9544-cdbc96ed4fa2 100 4 1771557534365 -ORDER f4b788e6-632c-48c1-9eff-30880619b16d buyer2 BUY 100 6 1771557534365 -TRADE f4b788e6-632c-48c1-9eff-30880619b16d 555a6afb-fb3e-41c5-9544-cdbc96ed4fa2 100 6 1771557534366 -ORDER 729e42d0-bf4a-4dbd-a666-3adb0fd9c136 seller1 SELL 100 10 1772083565558 -ORDER c64a2e5d-1e54-4568-bee3-990fe3cde36b buyer1 BUY 105 4 1772083565592 -TRADE c64a2e5d-1e54-4568-bee3-990fe3cde36b 729e42d0-bf4a-4dbd-a666-3adb0fd9c136 100 4 1772083565592 -ORDER 92220288-5c9e-4a1a-9a10-88d14f297953 buyer2 BUY 100 6 1772083565593 -TRADE 92220288-5c9e-4a1a-9a10-88d14f297953 729e42d0-bf4a-4dbd-a666-3adb0fd9c136 100 6 1772083565593 -ORDER 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc seller1 SELL 100 10 -ORDER 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc seller1 SELL 100 10 1772083565594 -ORDER 278fdfd7-6893-4d50-923a-0a694dca0884 buyer1 BUY 105 4 -ORDER 278fdfd7-6893-4d50-923a-0a694dca0884 buyer1 BUY 105 4 1772083565594 -TRADE 278fdfd7-6893-4d50-923a-0a694dca0884 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc 100 4 1772083565594 -TRADE 278fdfd7-6893-4d50-923a-0a694dca0884 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc 100 4 -ORDER 02956e10-2d87-4a95-93e1-5c9de14c0d0b buyer1 BUY 100 6 -ORDER 02956e10-2d87-4a95-93e1-5c9de14c0d0b buyer1 BUY 100 6 1772083565594 -TRADE 02956e10-2d87-4a95-93e1-5c9de14c0d0b 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc 100 6 1772083565595 -TRADE 02956e10-2d87-4a95-93e1-5c9de14c0d0b 9e2a8036-019a-48e2-a22a-f3e2bf1dd1cc 100 6 -ORDER 64cbfaf9-f583-44fc-a836-1187c5003ebe seller1 SELL 100 10 1772670798630 -ORDER d69ce365-223d-4776-a5dd-d30cc7a8a912 buyer1 BUY 105 4 1772670798732 -TRADE d69ce365-223d-4776-a5dd-d30cc7a8a912 64cbfaf9-f583-44fc-a836-1187c5003ebe 100 4 1772670798733 -ORDER 8891349c-44e5-408c-8ce0-989ec39e24ca buyer2 BUY 100 6 1772670798734 -TRADE 8891349c-44e5-408c-8ce0-989ec39e24ca 64cbfaf9-f583-44fc-a836-1187c5003ebe 100 6 1772670798734 -ORDER 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 seller1 SELL 100 10 -ORDER 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 seller1 SELL 100 10 1772670871847 -ORDER 0b5e5139-84c9-4698-a906-e868092f3308 buyer1 BUY 105 4 -ORDER 0b5e5139-84c9-4698-a906-e868092f3308 buyer1 BUY 105 4 1772670872856 -TRADE 0b5e5139-84c9-4698-a906-e868092f3308 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 100 4 1772671176205 -TRADE 0b5e5139-84c9-4698-a906-e868092f3308 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 100 4 -ORDER b2557f88-8c0f-446d-8e70-010b17b2c2c8 buyer1 BUY 100 6 -ORDER b2557f88-8c0f-446d-8e70-010b17b2c2c8 buyer1 BUY 100 6 1772670876431 -TRADE b2557f88-8c0f-446d-8e70-010b17b2c2c8 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 100 6 1772671176208 -TRADE b2557f88-8c0f-446d-8e70-010b17b2c2c8 0b7b68d9-c8d6-4cda-a3c5-f6aa4e9d1330 100 6 -ORDER 9ebc90e5-eac2-4ed2-bd23-87dc5f9fad78 seller1 SELL 100 10 1772671494015 -ORDER e6791c03-baf8-4f62-8742-ca2046593854 buyer1 BUY 105 4 1772671494047 -TRADE e6791c03-baf8-4f62-8742-ca2046593854 9ebc90e5-eac2-4ed2-bd23-87dc5f9fad78 100 4 1772671494047 -ORDER d6177034-7805-4d88-a13c-ccff65a03a40 buyer2 BUY 100 6 1772671494049 -TRADE d6177034-7805-4d88-a13c-ccff65a03a40 9ebc90e5-eac2-4ed2-bd23-87dc5f9fad78 100 6 1772671494049 -ORDER 759faf99-a021-4608-bdc7-656bfa915a95 seller1 SELL 100 10 -ORDER 759faf99-a021-4608-bdc7-656bfa915a95 seller1 SELL 100 10 1772671494058 -ORDER f3bd642f-8fa9-4d48-94e7-04bcbb39e49a buyer1 BUY 105 4 -ORDER f3bd642f-8fa9-4d48-94e7-04bcbb39e49a buyer1 BUY 105 4 1772671494058 -TRADE f3bd642f-8fa9-4d48-94e7-04bcbb39e49a 759faf99-a021-4608-bdc7-656bfa915a95 100 4 1772671494060 -TRADE f3bd642f-8fa9-4d48-94e7-04bcbb39e49a 759faf99-a021-4608-bdc7-656bfa915a95 100 4 -ORDER 9a5d71f3-1351-403f-a9f6-019f3a5657af buyer1 BUY 100 6 -ORDER 9a5d71f3-1351-403f-a9f6-019f3a5657af buyer1 BUY 100 6 1772671494058 -TRADE 9a5d71f3-1351-403f-a9f6-019f3a5657af 759faf99-a021-4608-bdc7-656bfa915a95 100 6 1772671494061 -TRADE 9a5d71f3-1351-403f-a9f6-019f3a5657af 759faf99-a021-4608-bdc7-656bfa915a95 100 6 -ORDER cd18202b-085c-44d6-a5b6-3598fe8494e6 seller1 SELL 100 10 1772752285238 -ORDER cfe88003-6f8f-4a3a-8236-27b7c139f04d buyer1 BUY 105 4 1772752285262 -TRADE cfe88003-6f8f-4a3a-8236-27b7c139f04d cd18202b-085c-44d6-a5b6-3598fe8494e6 100 4 1772752285262 -ORDER 6b68b475-9229-4bc2-aa10-66298c6e9928 buyer2 BUY 100 6 1772752285264 -TRADE 6b68b475-9229-4bc2-aa10-66298c6e9928 cd18202b-085c-44d6-a5b6-3598fe8494e6 100 6 1772752285264 -ORDER 28e6dec6-2781-4ad0-8230-7cc1de99f41e seller1 SELL 100 10 -ORDER 28e6dec6-2781-4ad0-8230-7cc1de99f41e seller1 SELL 100 10 1772752285268 -ORDER 4e0a0c16-518b-42c4-b40b-4504645951d4 buyer1 BUY 105 4 -ORDER 4e0a0c16-518b-42c4-b40b-4504645951d4 buyer1 BUY 105 4 1772752285268 -TRADE 4e0a0c16-518b-42c4-b40b-4504645951d4 28e6dec6-2781-4ad0-8230-7cc1de99f41e 100 4 1772752285270 -TRADE 4e0a0c16-518b-42c4-b40b-4504645951d4 28e6dec6-2781-4ad0-8230-7cc1de99f41e 100 4 -ORDER ee65a46b-8063-4a87-b6bc-ee9301e48e35 buyer1 BUY 100 6 -ORDER ee65a46b-8063-4a87-b6bc-ee9301e48e35 buyer1 BUY 100 6 1772752285268 -TRADE ee65a46b-8063-4a87-b6bc-ee9301e48e35 28e6dec6-2781-4ad0-8230-7cc1de99f41e 100 6 1772752285270 -TRADE ee65a46b-8063-4a87-b6bc-ee9301e48e35 28e6dec6-2781-4ad0-8230-7cc1de99f41e 100 6 -ORDER_PLACED: c61d527c-d5a2-46cd-b7bd-1e2e7dd1813b -ORDER c61d527c-d5a2-46cd-b7bd-1e2e7dd1813b user123 BUY 150 10 1772754586115 -ORDER_PLACED: 9f12d7ee-5ad2-4702-83fb-4fd4faa682fe -ORDER 9f12d7ee-5ad2-4702-83fb-4fd4faa682fe user123 BUY 150 10 1772755348807 -ORDER_PLACED: 4cef0377-341d-4b13-8856-f44b1ad24656 -ORDER 4cef0377-341d-4b13-8856-f44b1ad24656 user-seller SELL 150 10 1772756125478 -ORDER_PLACED: 44655cad-3469-4cfb-899c-c1dacd78ccda -ORDER 44655cad-3469-4cfb-899c-c1dacd78ccda user1 BUY 100 10 1772756456024 -ORDER_PLACED: a16f2f1b-2e4b-48f0-8a6f-7ce51f4bc94b -ORDER a16f2f1b-2e4b-48f0-8a6f-7ce51f4bc94b user-seller SELL 150 10 1772759912084 -ORDER_PLACED: a1d913c4-317e-4f88-9c2e-05c2d422d7bb -ORDER a1d913c4-317e-4f88-9c2e-05c2d422d7bb user1 BUY 100 10 1772759987885 -ORDER_PLACED: 3db5fb8c-f24b-4674-8a4b-cbadc7fede32 -ORDER 3db5fb8c-f24b-4674-8a4b-cbadc7fede32 user-seller SELL 150 10 1772760831791 -ORDER_PLACED: 562de0cc-8ba3-4264-8158-98904cbce14d -ORDER 562de0cc-8ba3-4264-8158-98904cbce14d user1 BUY 100 10 1772760849537 -ORDER_PLACED: 9320ee7a-9fef-4449-bf80-f2c2ef83cc88 -ORDER 9320ee7a-9fef-4449-bf80-f2c2ef83cc88 user-seller SELL 150 10 1772761342572 -ORDER_PLACED: b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f -ORDER b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f user1 BUY 151 10 1772761362103 -TRADE b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f 9320ee7a-9fef-4449-bf80-f2c2ef83cc88 150 10 1772761362107 -TRADE_SETTLED: b7a13e53-93ee-46b0-8ddb-8fa9e26ebe5f <-> 9320ee7a-9fef-4449-bf80-f2c2ef83cc88 diff --git a/backend/matching-engine/journals/engine.log b/backend/matching-engine/journals/engine.log deleted file mode 100644 index 87647fd..0000000 --- a/backend/matching-engine/journals/engine.log +++ /dev/null @@ -1,53 +0,0 @@ -ORDER c78afcd8-aa27-4770-866b-840efa1dcad2 seller1 SELL 100 10 1772752218574 -ORDER 8a44bcdf-4b7a-4bfb-b94c-792f629cd9f1 buyer1 BUY 105 4 1772752218582 -TRADE 8a44bcdf-4b7a-4bfb-b94c-792f629cd9f1 c78afcd8-aa27-4770-866b-840efa1dcad2 100 4 1772752218582 -ORDER 2c267f3a-4da6-4060-bb5b-de3fa97f6029 buyer2 BUY 100 6 1772752218583 -TRADE 2c267f3a-4da6-4060-bb5b-de3fa97f6029 c78afcd8-aa27-4770-866b-840efa1dcad2 100 6 1772752218583 -ORDER a692a971-2eae-4de6-b469-2bff7c12c7ec seller1 SELL 100 10 -ORDER a692a971-2eae-4de6-b469-2bff7c12c7ec seller1 SELL 100 10 1772752218586 -ORDER 9a4428f5-1e36-4bf6-a7e7-2b0900bb62e8 buyer1 BUY 105 4 -ORDER 9a4428f5-1e36-4bf6-a7e7-2b0900bb62e8 buyer1 BUY 105 4 1772752218586 -TRADE 9a4428f5-1e36-4bf6-a7e7-2b0900bb62e8 a692a971-2eae-4de6-b469-2bff7c12c7ec 100 4 1772752218586 -TRADE 9a4428f5-1e36-4bf6-a7e7-2b0900bb62e8 a692a971-2eae-4de6-b469-2bff7c12c7ec 100 4 -ORDER a8eaddb2-4f1f-4df0-a28b-c365b37c6911 buyer1 BUY 100 6 -ORDER a8eaddb2-4f1f-4df0-a28b-c365b37c6911 buyer1 BUY 100 6 1772752218586 -TRADE a8eaddb2-4f1f-4df0-a28b-c365b37c6911 a692a971-2eae-4de6-b469-2bff7c12c7ec 100 6 1772752218587 -TRADE a8eaddb2-4f1f-4df0-a28b-c365b37c6911 a692a971-2eae-4de6-b469-2bff7c12c7ec 100 6 -ORDER 5362fffb-146f-40b0-a9e1-fb84dd970ac7 seller1 SELL 100 10 1772760358808 -ORDER 0211741a-8fa1-4a25-8b1d-1237a52f9211 buyer1 BUY 105 4 1772760358814 -TRADE 0211741a-8fa1-4a25-8b1d-1237a52f9211 5362fffb-146f-40b0-a9e1-fb84dd970ac7 100 4 1772760358815 -ORDER 898a8498-ad4e-434b-8abe-5f5d20ae3173 buyer2 BUY 100 6 1772760358816 -TRADE 898a8498-ad4e-434b-8abe-5f5d20ae3173 5362fffb-146f-40b0-a9e1-fb84dd970ac7 100 6 1772760358816 -REJECT 6b224bfc-c1e8-46d7-b0a3-ac0de2b49358 INSUFFICIENT_FUNDS -ORDER f9712aa9-acba-43aa-adda-cb5a7adf145d buyer1 BUY 105 4 -ORDER f9712aa9-acba-43aa-adda-cb5a7adf145d buyer1 BUY 105 4 1772760358817 -ORDER c2cd976b-335c-4592-a644-d0d07db87af1 buyer1 BUY 100 6 -ORDER c2cd976b-335c-4592-a644-d0d07db87af1 buyer1 BUY 100 6 1772760358817 -ORDER 37c7fb05-1c35-4f6c-9407-3e01713a1ea9 seller1 SELL 100 10 1772760473119 -ORDER 05f435ca-b3b9-4288-a979-062eef0f5e06 buyer1 BUY 105 4 1772760473125 -TRADE 05f435ca-b3b9-4288-a979-062eef0f5e06 37c7fb05-1c35-4f6c-9407-3e01713a1ea9 100 4 1772760473125 -ORDER ce8d19be-00e6-4e0b-8056-2f468e5d540a buyer2 BUY 100 6 1772760473126 -TRADE ce8d19be-00e6-4e0b-8056-2f468e5d540a 37c7fb05-1c35-4f6c-9407-3e01713a1ea9 100 6 1772760473126 -REJECT debaf786-7218-4204-9bed-55e5d79c5e16 INSUFFICIENT_FUNDS -ORDER d5f67c56-67c3-4dac-967d-8b54cc41d1b6 buyer1 BUY 105 4 -ORDER d5f67c56-67c3-4dac-967d-8b54cc41d1b6 buyer1 BUY 105 4 1772760473129 -ORDER a204eca1-a5db-4d75-855f-64ca63a755db buyer1 BUY 100 6 -ORDER a204eca1-a5db-4d75-855f-64ca63a755db buyer1 BUY 100 6 1772760473129 -ORDER_PLACED: e72d00ac-eb7b-42ab-bf3b-f9e568c32ea4 -ORDER e72d00ac-eb7b-42ab-bf3b-f9e568c32ea4 user-seller SELL 100 10 1772760703367 -ORDER_PLACED: cbeb1b27-a8fa-45b2-9bd0-459cc73c3466 -ORDER cbeb1b27-a8fa-45b2-9bd0-459cc73c3466 user1 BUY 100 10 1772760707390 -TRADE cbeb1b27-a8fa-45b2-9bd0-459cc73c3466 e72d00ac-eb7b-42ab-bf3b-f9e568c32ea4 100 10 1772760707390 -TRADE_SETTLED: cbeb1b27-a8fa-45b2-9bd0-459cc73c3466 <-> e72d00ac-eb7b-42ab-bf3b-f9e568c32ea4 -ORDER_PLACED: e5bb5cfc-bb06-4a16-9af5-5e91f72c7a0c -ORDER e5bb5cfc-bb06-4a16-9af5-5e91f72c7a0c user-seller SELL 150 10 1772761274302 -ORDER_PLACED: aa3558d9-7e18-41d7-b6e5-00e3e0c69788 -ORDER aa3558d9-7e18-41d7-b6e5-00e3e0c69788 user1 BUY 151 10 1772761274334 -TRADE aa3558d9-7e18-41d7-b6e5-00e3e0c69788 e5bb5cfc-bb06-4a16-9af5-5e91f72c7a0c 150 10 1772761274335 -TRADE_SETTLED: aa3558d9-7e18-41d7-b6e5-00e3e0c69788 <-> e5bb5cfc-bb06-4a16-9af5-5e91f72c7a0c -ORDER_PLACED: 89a0af09-0b11-467d-ba14-c015ea52007c -ORDER 89a0af09-0b11-467d-ba14-c015ea52007c user-seller SELL 150 10 1772837899360 -ORDER_PLACED: 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 -ORDER 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 user1 BUY 151 10 1772837899385 -TRADE 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 89a0af09-0b11-467d-ba14-c015ea52007c 150 10 1772837899386 -TRADE_SETTLED: 14e62193-7644-460c-bc0e-e6f9f9eb8ff9 <-> 89a0af09-0b11-467d-ba14-c015ea52007c diff --git a/backend/matching-engine/pom.xml b/backend/matching-engine/pom.xml index 8e0bcd4..5d95db0 100644 --- a/backend/matching-engine/pom.xml +++ b/backend/matching-engine/pom.xml @@ -15,36 +15,68 @@ matching-engine 1.0-SNAPSHOT + + 21 + 1.18.34 + + - - org.example - common - 1.0-SNAPSHOT + org.springframework.boot + spring-boot-starter-web org.springframework.boot - spring-boot-starter-web + spring-boot-starter-websocket org.springframework.boot spring-boot-starter-validation + + org.springframework.boot + spring-boot-starter + org.projectlombok lombok true + org.springframework.boot - spring-boot-starter - - - - org.junit.jupiter - junit-jupiter - 5.10.0 + spring-boot-starter-test test - \ No newline at end of file + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + diff --git a/backend/matching-engine/src/main/java/org/example/Main.java b/backend/matching-engine/src/main/java/org/example/Main.java index be87cb5..ab8f332 100644 --- a/backend/matching-engine/src/main/java/org/example/Main.java +++ b/backend/matching-engine/src/main/java/org/example/Main.java @@ -32,7 +32,7 @@ public static void main(String[] args) { private static void engineSmokeTest(){ System.out.println("Testing MatchingEngine directly..."); - MatchingEngine engine = new MatchingEngine(); + MatchingEngine engine = new MatchingEngine(new EventJournal()); Order s1 = new Order(IdGenerator.newId(),"seller1",100L,10,System.currentTimeMillis(),OrderSide.SELL, "AAPL"); engine.placeOrder(s1); @@ -57,7 +57,7 @@ private static void orchestratorE2ETest(){ InMemoryWalletService walletService = new InMemoryWalletService(orderRepo); RiskManager riskManager = new RiskManager(walletService, orderRepo); EventJournal journal = new EventJournal(); - MatchingEngine engine = new MatchingEngine(); + MatchingEngine engine = new MatchingEngine(journal); // Create orchestrator with full constructor OrderOrchestrator orchestrator = new OrderOrchestrator(engine, journal, riskManager, orderRepo, walletService); @@ -100,7 +100,7 @@ private static void orchestratorE2ETest(){ private static void replayTest(){ System.out.println("Testing journal replay functionality..."); - MatchingEngine engine2 = new MatchingEngine(); + MatchingEngine engine2 = new MatchingEngine(new EventJournal()); Replay r = new Replay(engine2); r.replayJournal(); System.out.println("Reconstructed order book:"); diff --git a/backend/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java b/backend/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java index 88335c0..4b54f1e 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java +++ b/backend/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java @@ -6,12 +6,10 @@ import org.example.matching.model.Trade; import org.example.matching.model.Wallet; import org.example.matching.orderbook.OrderRepository; -import org.springframework.stereotype.Service; // Add this import - +import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -@Service // Tells Spring this is a managed bean public class InMemoryWalletService implements WalletService { private final Map wallets = new ConcurrentHashMap<>(); @@ -94,6 +92,11 @@ public Wallet getWallet(String userId) { return wallets.get(userId); } + @Override + public Collection getAllWallets() { + return wallets.values(); + } + @Override public void settleTrade(Trade trade) { Order buy = orderRepository.findById(trade.getBuyOrderId()).orElse(null); diff --git a/backend/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java b/backend/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java index a452b8a..cf86af6 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java +++ b/backend/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java @@ -1,11 +1,8 @@ package org.example.matching.Wallets; -import lombok.AllArgsConstructor; import org.example.matching.model.Order; import org.example.matching.orderbook.OrderRepository; -import org.springframework.stereotype.Service; -@Service public class RiskManager { private final WalletService walletService; diff --git a/backend/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java b/backend/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java index 73c862e..fe32b95 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java +++ b/backend/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java @@ -4,14 +4,17 @@ import org.example.matching.model.Trade; import org.example.matching.model.Wallet; +import java.util.Collection; + public interface WalletService { boolean reserveForOrder(Order order); void releaseReservation(String orderId); void settleTrade(Trade trade); - + // Additional methods needed for testing and API void creditUserShares(String userId, long shares); void creditUserShares(String userId, String instrument, long shares); void creditUserCash(String userId, long cash); Wallet getWallet(String userId); + Collection getAllWallets(); } diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java b/backend/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java index b018cee..e714aa3 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java +++ b/backend/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java @@ -1,6 +1,5 @@ package org.example.matching.api.controller; -import jdk.jfr.Event; import lombok.RequiredArgsConstructor; import org.example.matching.api.dto.EventRequest; import org.example.matching.api.dto.MarketEvent; diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java b/backend/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java index 7e6dacd..00543ba 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java +++ b/backend/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java @@ -10,7 +10,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import javax.swing.text.html.parser.Entity; import java.util.Optional; @RestController diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java b/backend/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java index 2b0b3ba..1c826a3 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java +++ b/backend/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java @@ -21,6 +21,9 @@ public class WalletController { @GetMapping("/{userId}") public ResponseEntity getWallet(@PathVariable String userId){ Wallet wallet = walletService.getWallet(userId); + if (wallet == null) { + return ResponseEntity.notFound().build(); + } // Convert AtomicLong maps to Long maps for JSON response Map availableShares = wallet.getAvailableShares().entrySet().stream() diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/EventRequest.java b/backend/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java similarity index 61% rename from matching-engine/src/main/java/org/example/matching/api/dto/EventRequest.java rename to backend/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java index 0f4d8dc..d092a44 100644 --- a/matching-engine/src/main/java/org/example/matching/api/dto/EventRequest.java +++ b/backend/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java @@ -9,11 +9,11 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class EventRequest { +public class MarketEvent { private String id; - private String questions; + private String question; private String yesTicker; private String noTicker; - private int expiry; // minutes from now - private Long liquidity; // Optional - defaults to 10000 if not provided -} \ No newline at end of file + private int expiry; + private EventStatus status; +} diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/service/EventTickerRegistry.java b/backend/matching-engine/src/main/java/org/example/matching/api/service/EventTickerRegistry.java new file mode 100644 index 0000000..151b31f --- /dev/null +++ b/backend/matching-engine/src/main/java/org/example/matching/api/service/EventTickerRegistry.java @@ -0,0 +1,27 @@ +package org.example.matching.api.service; + +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Tracks YES/NO ticker pairs for prediction market events. + * Allows OrderService to convert "sell without shares" into a buy of the counterpart. + */ +@Component +public class EventTickerRegistry { + + // Each event ticker maps to its counterpart (YES → NO, NO → YES) + private final Map counterpartMap = new ConcurrentHashMap<>(); + + public void registerPair(String yesTicker, String noTicker) { + counterpartMap.put(yesTicker, noTicker); + counterpartMap.put(noTicker, yesTicker); + } + + /** Returns the counterpart ticker, or null if this is not an event ticker. */ + public String getCounterpart(String ticker) { + return counterpartMap.get(ticker); + } +} diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java b/backend/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java index 93bf0e8..4e35b7f 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java +++ b/backend/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java @@ -4,26 +4,31 @@ import org.example.matching.api.dto.OrderRequest; import org.springframework.stereotype.Service; +import java.util.UUID; + @Service @RequiredArgsConstructor public class LiquidBotService { private final OrderService orderService; + public void seedMarket(String yesTicker, String noTicker) { // Providing liquidity for YES - placeBotOrder(yesTicker, "BUY", 45, 1000); // Bot buys from users at 45c - placeBotOrder(yesTicker, "SELL", 55, 1000); // Bot sells to users at 55c + placeBotOrder(yesTicker, "BUY", 45, 1000); // Bot buys from users at 45c + placeBotOrder(yesTicker, "SELL", 55, 1000); // Bot sells to users at 55c // Providing liquidity for NO - placeBotOrder(noTicker, "BUY", 45, 1000); // Bot buys from users at 45c - placeBotOrder(noTicker, "SELL", 55, 1000); // Bot sells to users at 55c + placeBotOrder(noTicker, "BUY", 45, 1000); // Bot buys from users at 45c + placeBotOrder(noTicker, "SELL", 55, 1000); // Bot sells to users at 55c } - private void placeBotOrder(String ticker, String side, long price, long qty){ + + private void placeBotOrder(String ticker, String side, long price, long qty) { OrderRequest req = new OrderRequest(); req.setUserId("HOUSE_BOT"); req.setInstrument(ticker); req.setSide(side); req.setPrice(price); req.setQuantity(qty); + req.setIdempotencyKey(UUID.randomUUID().toString()); orderService.processOrder(req); } -} \ No newline at end of file +} diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java b/backend/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java index 2df098b..3cd5e3f 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java +++ b/backend/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java @@ -1,27 +1,78 @@ package org.example.matching.api.service; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.example.matching.api.dto.OrderBookResponse; +import org.example.matching.api.dto.PriceLevel; +import org.example.matching.model.Trade; +import org.example.matching.ws.MarketWebSocketHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import java.util.List; -import java.util.Map; +import org.springframework.web.socket.WebSocketSession; + +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; +@Slf4j @Service public class MarketDataService { - //stores last price each stock is traded at - private final Map lastPrices = new ConcurrentHashMap<>(); - // Average V(volume)WAP = sum(price*quantity)/sum(quantity) - private final Map sumPV = new ConcurrentHashMap<>(); - private final Map sumV = new ConcurrentHashMap<>(); - // To track the "Top of Book" (Best Bid/Ask) - private final Map bestBids = new ConcurrentHashMap<>(); - private final Map bestAsks = new ConcurrentHashMap<>(); + // ========================================================================= + // EXISTING fields – untouched so REST endpoints keep working + // ========================================================================= + + private final Map lastPrices = new ConcurrentHashMap<>(); + private final Map sumPV = new ConcurrentHashMap<>(); + private final Map sumV = new ConcurrentHashMap<>(); + private final Map bestBids = new ConcurrentHashMap<>(); + private final Map bestAsks = new ConcurrentHashMap<>(); + private final Map> priceHistory = new ConcurrentHashMap<>(); + + // ========================================================================= + // NEW fields for WebSocket streaming + // ========================================================================= + + // Monotonically increasing sequence number per market + private final Map seqNumbers = new ConcurrentHashMap<>(); + + // Ring buffer of the last MAX_BUFFER events per market (for reconnect replay) + private final Map> eventBuffers = new ConcurrentHashMap<>(); + private static final int MAX_BUFFER = 10_000; + private static final int MAX_REPLAY_GAP = 10_000; + + // EMA (20-period) per market + private final Map emaValues = new ConcurrentHashMap<>(); + private static final double EMA_ALPHA = 2.0 / 21.0; // 2 / (period + 1) + + // Previous book levels – needed to compute incremental diffs + private final Map> prevBids = new ConcurrentHashMap<>(); + private final Map> prevAsks = new ConcurrentHashMap<>(); + + // Per-market lock to keep seq + buffer updates atomic across threads + private final Map marketLocks = new ConcurrentHashMap<>(); - public void UpdateTrade(String instrument, long price , long quantity){ - lastPrices.put(instrument,(double)price); - sumPV.merge(instrument,(double)(price*quantity),Double::sum); - sumV.merge(instrument,quantity,Long::sum); + // @Lazy breaks MarketDataService <-> MarketWebSocketHandler circular dep + @Lazy + @Autowired + private MarketWebSocketHandler wsHandler; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + // ========================================================================= + // EXISTING public methods – signatures unchanged + // ========================================================================= + + public void UpdateTrade(String instrument, long price, long quantity) { + lastPrices.put(instrument, (double) price); + sumPV.merge(instrument, (double) (price * quantity), Double::sum); + sumV.merge(instrument, quantity, Long::sum); + + List history = priceHistory.computeIfAbsent(instrument, k -> new CopyOnWriteArrayList<>()); + history.add(0, (double) price); + if (history.size() > 10) history.remove(history.size() - 1); } public void updateBookTops(String instrument, Double bestBid, Double bestAsk) { @@ -29,39 +80,276 @@ public void updateBookTops(String instrument, Double bestBid, Double bestAsk) { if (bestAsk != null) bestAsks.put(instrument, bestAsk); } - - public Map getSnapshots(String instrument){ - double ltp = lastPrices.getOrDefault(instrument,0.0); - double vwap = sumV.getOrDefault(instrument,0L)==0?0: sumPV.get(instrument)/ sumV.get(instrument); - double bid = bestBids.getOrDefault(instrument, 0.0); - double ask = bestAsks.getOrDefault(instrument, 0.0); - double mid = (bid > 0 && ask > 0) ? (bid + ask) / 2.0 : ltp; + public Map getSnapshots(String instrument) { + double ltp = lastPrices.getOrDefault(instrument, 0.0); + double vwap = sumV.getOrDefault(instrument, 0L) == 0 + ? 0 : sumPV.get(instrument) / sumV.get(instrument); + double bid = bestBids.getOrDefault(instrument, 0.0); + double ask = bestAsks.getOrDefault(instrument, 0.0); + double mid = (bid > 0 && ask > 0) ? (bid + ask) / 2.0 : ltp; return Map.of( "instrument", instrument, - "lastPrice", ltp, - "vwap", vwap, - "bid", bid, - "ask", ask, - "mid", mid, - "spread", (ask - bid) + "lastPrice", ltp, + "vwap", vwap, + "bid", bid, + "ask", ask, + "mid", mid, + "spread", (ask - bid) ); } - // A list of the last 10 prices - private final Map> priceHistory = new ConcurrentHashMap<>(); + public List getPriceHistory(String instrument) { + return priceHistory.getOrDefault(instrument, List.of()); + } - public void updateTrade(String instrument, long price, long quantity) { - lastPrices.put(instrument, (double) price); + // ========================================================================= + // NEW public methods – called by OrderService after each trade / book change + // ========================================================================= - // Track History - priceHistory.computeIfAbsent(instrument, k -> new CopyOnWriteArrayList<>()) - .add(0, (double) price); // Add to the front + /** + * Called after every matched trade. + * Updates existing stats AND pushes a trade event to subscribed WS clients. + */ + public void onTrade(String instrument, Trade trade) { + // Keep existing REST snapshot data up to date + UpdateTrade(instrument, trade.getPrice(), trade.getQuantity()); - // Keep only the last 10 - List history = priceHistory.get(instrument); - if (history.size() > 10) history.remove(history.size() - 1); + // Build + broadcast WS trade event (seq-stamped, buffered for replay) +//getMarketLock is just a map + Object lock = getMarketLock(instrument); + String json; + //synchronized(lock){ + //String json; + synchronized (lock) { + // nextSeq is also a map + long seq = nextSeq(instrument); + updateEma(instrument, trade.getPrice()); + json = buildTradeJson(instrument, seq, trade); + bufferEvent(instrument, seq, json); + } + wsHandler.sendToMarket(instrument, json); + } + /** + * long seq = nextSeq(instrument) + */ + + /** + * Called once per order placement (after all trades are settled). + * Diffs the current book top against the previous snapshot and pushes + * a book_update event carrying only the changed / removed levels. + */ + public void onBookChange(String instrument, OrderBookResponse snapshot) { + // Keep existing REST top-of-book data up to date + List bids = limitLevels(snapshot.getBids(), 20); + List asks = limitLevels(snapshot.getAsks(), 20); + + updateBookTops(instrument, + bids.isEmpty() ? 0.0 : bids.get(0).getPrice().doubleValue(), + asks.isEmpty() ? 0.0 : asks.get(0).getPrice().doubleValue()); + + // Compute incremental diff against previous state + List> changes = new ArrayList<>(); + changes.addAll(computeChanges( + prevBids.getOrDefault(instrument, Collections.emptyList()), bids, "bid")); + changes.addAll(computeChanges( + prevAsks.getOrDefault(instrument, Collections.emptyList()), asks, "ask")); + + // Save current levels as "previous" for next diff + prevBids.put(instrument, bids); + prevAsks.put(instrument, asks); + + if (changes.isEmpty()) return; // nothing actually changed → no event + + Object lock = getMarketLock(instrument); + String json; + synchronized (lock) { + long seq = nextSeq(instrument); + json = buildBookUpdateJson(instrument, seq, changes); + bufferEvent(instrument, seq, json); + } + wsHandler.sendToMarket(instrument, json); + } + + /** + * Called from MarketWebSocketHandler when a client subscribes. + * Sends missed events if the gap is small, otherwise a fresh snapshot. + */ + public void subscribe(String sessionId, String marketId, long lastSeq, WebSocketSession session) { + long currentSeq = seqNumbers.getOrDefault(marketId, new AtomicLong(0)).get(); + + if (lastSeq > 0 && currentSeq - lastSeq <= MAX_REPLAY_GAP && currentSeq > lastSeq) { + // Client reconnected with a known seq – replay missed events + sendMissedEvents(sessionId, marketId, lastSeq); + } else { + // Fresh subscribe or gap too large – send full snapshot + // We need the current book from the MatchingEngine; we use prevBids/prevAsks + // which were populated on the last onBookChange call. + sendSnapshot(sessionId, marketId); + } + } + + /** Called from the handler on unsubscribe or disconnect (no-op here; handler owns session maps). */ + public void unsubscribe(String sessionId, String marketId) { + // Session cleanup lives in MarketWebSocketHandler. + // Add per-session state teardown here if needed in the future. + } + + // ========================================================================= + // Private helpers + // ========================================================================= - // ... (rest of your VWAP logic) + private void sendSnapshot(String sessionId, String marketId) { + //get the market + long seq = seqNumbers.getOrDefault(marketId, new AtomicLong(0)).get(); + List bids = prevBids.getOrDefault(marketId, Collections.emptyList()); + List asks = prevAsks.getOrDefault(marketId, Collections.emptyList()); + String json = buildSnapshotJson(marketId, seq, bids, asks); + wsHandler.sendToSession(sessionId, json); } -} \ No newline at end of file + + private void sendMissedEvents(String sessionId, String marketId, long lastSeq) { + ArrayDeque buffer = eventBuffers.getOrDefault(marketId, new ArrayDeque<>()); + List missed = new ArrayList<>(); + synchronized (getMarketLock(marketId)) { + for (BufferedEvent e : buffer) { + if (e.seq() > lastSeq) missed.add(e); + } + } + for (BufferedEvent e : missed) { + wsHandler.sendToSession(sessionId, e.json()); + } + } + + private long nextSeq(String market) { + return seqNumbers.computeIfAbsent(market, k -> new AtomicLong(0)).incrementAndGet(); + } + + private void bufferEvent(String market, long seq, String json) { + ArrayDeque buf = + eventBuffers.computeIfAbsent(market, k -> new ArrayDeque<>()); + buf.addLast(new BufferedEvent(seq, json)); + if (buf.size() > MAX_BUFFER) buf.removeFirst(); + } + + private void updateEma(String instrument, long price) { + double prev = emaValues.getOrDefault(instrument, (double) price); + emaValues.put(instrument, EMA_ALPHA * price + (1 - EMA_ALPHA) * prev); + } + + private Object getMarketLock(String market) { + return marketLocks.computeIfAbsent(market, k -> new Object()); + } + + // ------------------------------------------------------------------------- + // JSON builders (use plain Map → Jackson, keeps it dependency-light) + // ------------------------------------------------------------------------- + + private String buildTradeJson(String market, long seq, Trade trade) { + Map m = new LinkedHashMap<>(); + m.put("type", "trade"); + m.put("market", market); + m.put("seq", seq); + m.put("timestamp", trade.getTimestamp()); + m.put("price", trade.getPrice()); + m.put("quantity", trade.getQuantity()); + m.put("buyOrderId", trade.getBuyOrderId()); + m.put("sellOrderId", trade.getSellOrderId()); + return toJson(m); + } + + private String buildBookUpdateJson(String market, long seq, List> changes) { + Map m = new LinkedHashMap<>(); + m.put("type", "book_update"); + m.put("market", market); + m.put("seq", seq); + m.put("timestamp", System.currentTimeMillis()); + m.put("changes", changes); + return toJson(m); + } + + private String buildSnapshotJson(String market, long seq, + List bids, List asks) { + Map m = new LinkedHashMap<>(); + m.put("type", "snapshot"); + m.put("market", market); + m.put("seq", seq); + m.put("timestamp", System.currentTimeMillis()); + m.put("bestBid", bids.isEmpty() ? 0 : bids.get(0).getPrice()); + m.put("bestAsk", asks.isEmpty() ? 0 : asks.get(0).getPrice()); + m.put("bids", priceLevelsToMaps(bids)); + m.put("asks", priceLevelsToMaps(asks)); + + double vwap = sumV.getOrDefault(market, 0L) == 0 + ? 0 : sumPV.get(market) / sumV.get(market); + m.put("vwap", vwap); + m.put("ema_mark", emaValues.getOrDefault(market, 0.0)); + return toJson(m); + } + + // ------------------------------------------------------------------------- + // Diff computation + // ------------------------------------------------------------------------- + + /** + * Returns only the levels that changed (qty differs) or were removed (qty=0) + * compared to the previous snapshot for that side. + */ + private List> computeChanges(List prev, + List curr, + String side) { + Map prevMap = new LinkedHashMap<>(); + for (PriceLevel pl : prev) prevMap.put(pl.getPrice(), pl.getQuantity()); + + Map currMap = new LinkedHashMap<>(); + for (PriceLevel pl : curr) currMap.put(pl.getPrice(), pl.getQuantity()); + + List> changes = new ArrayList<>(); + + // Added or quantity-changed levels + for (Map.Entry e : currMap.entrySet()) { + if (!e.getValue().equals(prevMap.get(e.getKey()))) { + changes.add(Map.of("side", side, "price", e.getKey(), "qty", e.getValue())); + } + } + // Removed levels (no longer in book → qty = 0) + for (Long price : prevMap.keySet()) { + if (!currMap.containsKey(price)) { + changes.add(Map.of("side", side, "price", price, "qty", 0L)); + } + } + return changes; + } + + // ------------------------------------------------------------------------- + // Utility + // ------------------------------------------------------------------------- + + private List limitLevels(List levels, int max) { + if (levels == null) return Collections.emptyList(); + return levels.size() <= max ? levels : levels.subList(0, max); + } + + private List> priceLevelsToMaps(List levels) { + List> result = new ArrayList<>(levels.size()); + for (PriceLevel pl : levels) { + result.add(Map.of("price", pl.getPrice(), "qty", pl.getQuantity())); + } + return result; + } + + private String toJson(Object obj) { + try { + return objectMapper.writeValueAsString(obj); + } catch (Exception e) { + log.error("JSON serialization failed", e); + return "{}"; + } + } + + // ------------------------------------------------------------------------- + // Inner record – one buffered event for replay + // ------------------------------------------------------------------------- + + private record BufferedEvent(long seq, String json) {} +} diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java b/backend/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java new file mode 100644 index 0000000..47e5ce2 --- /dev/null +++ b/backend/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java @@ -0,0 +1,72 @@ +package org.example.matching.api.service; + +import lombok.RequiredArgsConstructor; +import org.example.matching.Wallets.WalletService; +import org.example.matching.api.dto.EventStatus; +import org.example.matching.api.dto.MarketEvent; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +@Service +@RequiredArgsConstructor +public class MarketManagmentService { + + private final LiquidBotService liquidBotService; + private final WalletService walletService; + private final EventTickerRegistry eventTickerRegistry; + + private final Map events = new ConcurrentHashMap<>(); + + /** + * Creates a new prediction market event. + * Seeds HOUSE_BOT with funds and initial liquidity on both YES/NO books. + */ + public MarketEvent createEvent(String id, String question, String yesTicker, String noTicker, int expiry) { + if (events.containsKey(id)) { + return events.get(id); + } + + // Give HOUSE_BOT enough funds to provide liquidity on both sides: + // BUY at 45 x 1000 = 45,000 cash per ticker (2 tickers = 90,000 total) + // SELL at 55 x 1000 = 1000 shares per ticker + walletService.creditUserCash("HOUSE_BOT", 100_000L); + walletService.creditUserShares("HOUSE_BOT", yesTicker, 2000L); + walletService.creditUserShares("HOUSE_BOT", noTicker, 2000L); + + MarketEvent event = MarketEvent.builder() + .id(id) + .question(question) + .yesTicker(yesTicker) + .noTicker(noTicker) + .expiry(expiry) + .status(EventStatus.OPEN) + .build(); + + events.put(id, event); + + // Register YES/NO pair so OrderService can convert sells-without-shares to buys + eventTickerRegistry.registerPair(yesTicker, noTicker); + + // Seed initial order book liquidity so traders always have a price to trade against + liquidBotService.seedMarket(yesTicker, noTicker); + + return event; + } +// private long getNextSeq(String ticker) { +// return sequences.computeIfAbsent(ticker, k -> new AtomicLong(0)).incrementAndGet(); +// } + + public MarketEvent getEvent(String id) { + return events.get(id); + } + + public void closeEvent(String id) { + MarketEvent event = events.get(id); + if (event != null) { + event.setStatus(EventStatus.CLOSED); + } + } +} diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java b/backend/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java index 2e749f5..c35cb0e 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java +++ b/backend/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java @@ -9,6 +9,7 @@ import org.example.matching.journal.EventJournal; import org.example.matching.matching.MatchingEngine; import org.example.matching.model.Order; +import org.example.matching.model.OrderSide; import org.example.matching.model.Trade; import org.example.matching.orderbook.OrderRepository; import org.springframework.stereotype.Service; @@ -27,19 +28,47 @@ public class OrderService { private final WalletService walletService; private final EventJournal eventJournal; private final OrderRepository orderRepository; + private final EventTickerRegistry eventTickerRegistry; // In-memory idempotency store: Key -> Previous Response private final Map idempotencyStore = new ConcurrentHashMap<>(); public OrderResponse processOrder(OrderRequest request) { - if (idempotencyStore.containsKey(request.getIdempotencyKey())) { - return idempotencyStore.get(request.getIdempotencyKey()); + String idempotencyKey = request.getIdempotencyKey(); + if (idempotencyKey != null && idempotencyStore.containsKey(idempotencyKey)) { + return idempotencyStore.get(idempotencyKey); } - + Order order = OrderMapper.toDomain(request); - - if (!riskManager.checkAndReserve(order)) { - return buildResponse(order, "REJECTED", "Insufficient funds or shares"); + + if (!riskManager.checkAndReserve(order)) { //First tries normal validation (cash for BUY, shares for SELL) + // For event tickers: selling a side you don't hold = buying the opposite side. + // e.g. SELL NO (no shares) → BUY YES at (100 - price), and vice versa. + if (order.getSide() == OrderSide.SELL) { //Only SELL orders get converted to synthetic positions + String counterpart = eventTickerRegistry.getCounterpart(order.getInstrument());//Key Formula: YES price = 100 - NO price + // Example: SELL NO at 30c → BUY YES at 70c + //Validates price > 0 (line 51) + if (counterpart != null) { + long complementaryPrice = 100L - order.getPrice(); + if (complementaryPrice > 0) { + Order converted = new Order( + order.getId(), order.getUserId(), + complementaryPrice, order.getQuantity(), + order.getTimestamp(), OrderSide.BUY, counterpart //// Changed to BUY + ); + if (!riskManager.checkAndReserve(converted)) { + return buildResponse(order, "REJECTED", "Insufficient funds or shares"); + } + order = converted; + } else { + return buildResponse(order, "REJECTED", "Insufficient funds or shares"); + } + } else { + return buildResponse(order, "REJECTED", "Insufficient funds or shares"); + } + } else { + return buildResponse(order, "REJECTED", "Insufficient funds or shares"); + } } orderRepository.save(order); @@ -51,7 +80,7 @@ public OrderResponse processOrder(OrderRequest request) { for (Trade trade : trades) { // so once trade is done manage the cash and shares of the users using their ids and stuff in walletService below walletService.settleTrade(trade); - marketDataService.UpdateTrade(order.getInstrument(),trade.getPrice(),trade.getQuantity()); + marketDataService.onTrade(order.getInstrument(), trade); // updates stats + pushes WS trade event eventJournal.appendRaw("TRADE_SETTLED: " + trade.getBuyOrderId() + " <-> " + trade.getSellOrderId()); } var book = matchingEngine.getOrderBookForMarketData(order.getInstrument()); @@ -60,9 +89,13 @@ public OrderResponse processOrder(OrderRequest request) { book.getBestBid(), book.getBestAsk() ); + // Push book_update diff to WS subscribers after all trades are settled + marketDataService.onBookChange(order.getInstrument(), matchingEngine.getSnapshot(order.getInstrument())); OrderResponse response = buildResponse(order, "ACCEPTED", "Success"); - idempotencyStore.put(request.getIdempotencyKey(), response); + if (idempotencyKey != null) { + idempotencyStore.put(idempotencyKey, response); + } return response; } diff --git a/backend/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java b/backend/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java new file mode 100644 index 0000000..981cdb1 --- /dev/null +++ b/backend/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java @@ -0,0 +1,62 @@ +package org.example.matching.api.service; + +import lombok.RequiredArgsConstructor; +import org.example.matching.Wallets.WalletService; +import org.example.matching.api.dto.EventStatus; +import org.example.matching.api.dto.MarketEvent; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class SettlementService { + + private final MarketManagmentService marketManagmentService; + private final WalletService walletService; + + /** + * Settles a prediction market event. + * outcome must be "YES" or "NO". + * Winners (holders of the winning ticker) receive full payout. + */ + public void settleEvent(String eventId, String outcome) { + MarketEvent event = marketManagmentService.getEvent(eventId); + if (event == null) { + throw new IllegalStateException("Event not found: " + eventId); + } + if (event.getStatus() == EventStatus.SETTLED) { + throw new IllegalStateException("Event already settled: " + eventId); + } + if (!outcome.equalsIgnoreCase("YES") && !outcome.equalsIgnoreCase("NO")) { + throw new IllegalArgumentException("Outcome must be YES or NO, got: " + outcome); + } + + marketManagmentService.closeEvent(eventId); + event.setStatus(EventStatus.SETTLED); + + // Determine winning and losing tickers + String winningTicker = outcome.equalsIgnoreCase("YES") ? event.getYesTicker() : event.getNoTicker(); + String losingTicker = outcome.equalsIgnoreCase("YES") ? event.getNoTicker() : event.getYesTicker(); + + // Credit holders of the winning ticker 100 cash per share (YES+NO = 100 in this system) + for (org.example.matching.model.Wallet wallet : walletService.getAllWallets()) { + long winningAvail = wallet.getAvailableShares(winningTicker); + long winningReserved = wallet.getReservedShares(winningTicker); + long totalWinning = winningAvail + winningReserved; + + if (totalWinning > 0) { + walletService.creditUserCash(wallet.getUserId(), totalWinning * 100L); + } + + // Zero out all share balances for both tickers + zeroClearShares(wallet, winningTicker); + zeroClearShares(wallet, losingTicker); + } + } + + private void zeroClearShares(org.example.matching.model.Wallet wallet, String ticker) { + long avail = wallet.getAvailableShares(ticker); + long reserved = wallet.getReservedShares(ticker); + if (avail > 0) wallet.getAvailableShares().computeIfAbsent(ticker, k -> new java.util.concurrent.atomic.AtomicLong(0)).addAndGet(-avail); + if (reserved > 0) wallet.getReservedShares().computeIfAbsent(ticker, k -> new java.util.concurrent.atomic.AtomicLong(0)).addAndGet(-reserved); + } +} diff --git a/backend/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java b/backend/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java index 918e00c..fa12c3c 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java +++ b/backend/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java @@ -35,6 +35,6 @@ public RiskManager riskManager(WalletService walletService, OrderRepository orde @Bean public MatchingEngine matchingEngine(EventJournal eventJournal) { - return new MatchingEngine(); + return new MatchingEngine(eventJournal); } } diff --git a/backend/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java b/backend/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java index d15634f..7dd33fd 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java +++ b/backend/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java @@ -51,29 +51,26 @@ package org.example.matching.matching; import jakarta.annotation.PostConstruct; + import org.example.matching.api.dto.OrderBookResponse; import org.example.matching.api.dto.PriceLevel; import org.example.matching.journal.EventJournal; import org.example.matching.model.Order; import org.example.matching.model.OrderBook; import org.example.matching.model.Trade; -import org.springframework.stereotype.Service; - import java.util.Deque; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; - -@Service public class MatchingEngine { private final Map orderBooks = new ConcurrentHashMap<>(); private final EventJournal journal; - public MatchingEngine() { - this.journal = new EventJournal(); + public MatchingEngine(EventJournal journal) { + this.journal = journal; } @PostConstruct diff --git a/backend/matching-engine/src/main/java/org/example/matching/model/OrderBook.java b/backend/matching-engine/src/main/java/org/example/matching/model/OrderBook.java index aee9ed6..f253e7e 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/model/OrderBook.java +++ b/backend/matching-engine/src/main/java/org/example/matching/model/OrderBook.java @@ -156,7 +156,7 @@ private void matchSell(Order sell, List trades) { Order buy = queue.peekFirst(); if (buy == null) { bids.remove(bidPrice); - continue; + continue; // this sends back to while looop } int tradedQty = Math.min(sell.getQuantity(), buy.getQuantity()); @@ -184,7 +184,7 @@ private void matchSell(Order sell, List trades) { } // Inside your OrderBook.java model public Double getBestBid() { - return bids.isEmpty() ? 0.0 : bids.lastKey().doubleValue(); + return bids.isEmpty() ? 0.0 : bids.firstKey().doubleValue(); } public Double getBestAsk() { diff --git a/backend/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java b/backend/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java index 6ddee65..6e1f34b 100644 --- a/backend/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java +++ b/backend/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java @@ -1,14 +1,11 @@ package org.example.matching.orderbook; import org.example.matching.model.Order; -import org.springframework.stereotype.Repository; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -@Repository public class InMemoryOrderRepository implements OrderRepository { private final Map map = new ConcurrentHashMap<>(); diff --git a/backend/matching-engine/src/main/java/org/example/matching/ws/MarketWebSocketHandler.java b/backend/matching-engine/src/main/java/org/example/matching/ws/MarketWebSocketHandler.java new file mode 100644 index 0000000..a8dbfc4 --- /dev/null +++ b/backend/matching-engine/src/main/java/org/example/matching/ws/MarketWebSocketHandler.java @@ -0,0 +1,220 @@ +package org.example.matching.ws; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.example.matching.api.service.MarketDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.TextWebSocketHandler; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Handles raw WebSocket connections for market data streaming. + * + * Clients connect to ws://host:8080/ws/market and exchange JSON frames: + * subscribe -> { "type": "subscribe", "market": "AAPL", "lastSeq": 1234 } + * unsubscribe -> { "type": "unsubscribe", "market": "AAPL" } + * heartbeat -> { "type": "heartbeat" } + * + * Server pushes: snapshot, book_update, trade, heartbeat, error + */ +@Slf4j +@Component +public class MarketWebSocketHandler extends TextWebSocketHandler { + + // All open sessions + private final ConcurrentHashMap sessions = new ConcurrentHashMap<>(); + + // session-id -> set of markets the session is subscribed to + private final ConcurrentHashMap> sessionToMarkets = new ConcurrentHashMap<>(); + //client-> to markets they wants to subs // so string [ client 1, clinet 2] - > market theya re subscirbed to are [ a] , [a,b,c] respectively + + // market-id -> set of session-ids subscribed to that market + private final ConcurrentHashMap> marketToSessions = new ConcurrentHashMap<>(); + //market-> to who clients wants it + // reverse , sees which clients set are subscribed to a share e.g [appl]->[client-a,clinetb] + + + // Simple rate limiting: per session [messageCount, windowStartMs] + private final ConcurrentHashMap rateLimits = new ConcurrentHashMap<>(); + + private static final int RATE_LIMIT = 30; // max messages per window + private static final long RATE_WINDOW_MS = 10_000L; // 10-second rolling window + + // @Lazy breaks the circular dependency: MarketDataService <-> MarketWebSocketHandler + @Lazy + @Autowired + private MarketDataService marketDataService; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + + // Connection lifecycle + + + //called automatically by spring after client hits this endpoint - endpoint ws://host:8080/ws/marke + @Override + public void afterConnectionEstablished(WebSocketSession session) { + String sid = session.getId(); + sessions.put(sid, session); + sessionToMarkets.put(sid, ConcurrentHashMap.newKeySet()); + rateLimits.put(sid, new long[]{0L, System.currentTimeMillis()}); + log.debug("WS connected: {}", sid); + } + + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { + String sid = session.getId(); + Set markets = sessionToMarkets.remove(sid); + if (markets != null) { + for (String market : markets) { + Set subs = marketToSessions.get(market); + if (subs != null) subs.remove(sid); + marketDataService.unsubscribe(sid, market); + } + } + sessions.remove(sid); + rateLimits.remove(sid); + log.debug("WS disconnected: {} status={}", sid, status); + } + + @Override + public void handleTransportError(WebSocketSession session, Throwable ex) { + log.warn("WS transport error session={}: {}", session.getId(), ex.getMessage()); + } + + // ------------------------------------------------------------------------- + // Inbound message dispatch + // ------------------------------------------------------------------------- + + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { + String sid = session.getId(); + + if (!checkRateLimit(sid)) { + sendToSession(sid, errorJson("rate_limit_exceeded", "Too many requests")); + session.close(CloseStatus.POLICY_VIOLATION); + return; + } + + JsonNode root; + try { + root = objectMapper.readTree(message.getPayload()); + } catch (Exception e) { + sendToSession(sid, errorJson("bad_request", "Invalid JSON")); + return; + } + + String type = root.path("type").asText(""); + + switch (type) { + case "subscribe" -> { + String market = root.path("market").asText(null); + if (market == null || market.isBlank()) { + sendToSession(sid, errorJson("bad_request", "missing 'market' field")); + return; + } + long lastSeq = root.path("lastSeq").asLong(0L); + // Register the subscription mapping first so snapshot/events reach this session + addSubscription(sid, market); + // Delegate snapshot-or-replay logic to MarketDataService + marketDataService.subscribe(sid, market, lastSeq, session); + } + case "unsubscribe" -> { + String market = root.path("market").asText(null); + if (market != null && !market.isBlank()) { + removeSubscription(sid, market); + marketDataService.unsubscribe(sid, market); + } + } + case "heartbeat" -> { + // Echo heartbeat back + String hb = objectMapper.writeValueAsString( + Map.of("type", "heartbeat", "timestamp", System.currentTimeMillis())); + sendToSession(sid, hb); + } + default -> sendToSession(sid, errorJson("bad_request", "unknown type: " + type)); + } + } + + // ------------------------------------------------------------------------- + // Outbound helpers (called by MarketDataService) + // ------------------------------------------------------------------------- + + /** Send a JSON string to a single session (thread-safe). */ + public void sendToSession(String sessionId, String json) { + WebSocketSession session = sessions.get(sessionId); + if (session != null && session.isOpen()) { + try { + synchronized (session) { // WebSocketSession is not thread-safe + session.sendMessage(new TextMessage(json)); + } + } catch (IOException e) { + log.warn("Failed to send to {}: {}", sessionId, e.getMessage()); + } + } + } + + /** Broadcast a JSON string to all sessions subscribed to a market. */ + public void sendToMarket(String marketId, String json) { + Set sids = marketToSessions.getOrDefault(marketId, Collections.emptySet()); + for (String sid : sids) { + sendToSession(sid, json); + } + } + + // ------------------------------------------------------------------------- + // Subscription bookkeeping + // ------------------------------------------------------------------------- + + void addSubscription(String sessionId, String marketId) { + sessionToMarkets.computeIfAbsent(sessionId, k -> ConcurrentHashMap.newKeySet()).add(marketId); + marketToSessions.computeIfAbsent(marketId, k -> ConcurrentHashMap.newKeySet()).add(sessionId); + } + + void removeSubscription(String sessionId, String marketId) { + Set markets = sessionToMarkets.get(sessionId); + if (markets != null) markets.remove(marketId); + Set sids = marketToSessions.get(marketId); + if (sids != null) sids.remove(sessionId); + } + + // ------------------------------------------------------------------------- + // Rate limiting + // ------------------------------------------------------------------------- + + private boolean checkRateLimit(String sessionId) { + long[] rl = rateLimits.get(sessionId); + if (rl == null) return true; + long now = System.currentTimeMillis(); + if (now - rl[1] > RATE_WINDOW_MS) { + rl[0] = 1L; + rl[1] = now; + return true; + } + return ++rl[0] <= RATE_LIMIT; + } + + // ------------------------------------------------------------------------- + // Helper + // ------------------------------------------------------------------------- + + private String errorJson(String code, String message) { + try { + return objectMapper.writeValueAsString( + Map.of("type", "error", "code", code, "message", message)); + } catch (Exception e) { + return "{\"type\":\"error\"}"; + } + } +} diff --git a/backend/matching-engine/src/main/java/org/example/matching/ws/WebSocketConfig.java b/backend/matching-engine/src/main/java/org/example/matching/ws/WebSocketConfig.java new file mode 100644 index 0000000..de0628d --- /dev/null +++ b/backend/matching-engine/src/main/java/org/example/matching/ws/WebSocketConfig.java @@ -0,0 +1,25 @@ +package org.example.matching.ws; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; + +@Configuration +@EnableWebSocket // turns on ws in spring +public class WebSocketConfig implements WebSocketConfigurer { + + private final MarketWebSocketHandler handler; + + public WebSocketConfig(MarketWebSocketHandler handler) { + this.handler = handler; + } + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + // Connect via ws://host:8080/ws/market + // if /ws/market is reached , use this handler to have the buisness logic + registry.addHandler(handler, "/ws/market") + .setAllowedOrigins("*"); + } +} diff --git a/backend/matching-engine/target/classes/application.properties b/backend/matching-engine/target/classes/application.properties deleted file mode 100644 index b2860ce..0000000 --- a/backend/matching-engine/target/classes/application.properties +++ /dev/null @@ -1,12 +0,0 @@ -# Server Configuration -server.port=8080 - -# Application Configuration -spring.application.name=matching-engine - -# Logging Configuration -logging.level.org.example.matching=DEBUG -logging.level.org.springframework.web=INFO - -# Jackson Configuration (for JSON serialization) -spring.jackson.serialization.indent-output=true diff --git a/backend/matching-engine/target/classes/org/example/Main.class b/backend/matching-engine/target/classes/org/example/Main.class deleted file mode 100644 index 0f8def7..0000000 Binary files a/backend/matching-engine/target/classes/org/example/Main.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/MatchingEngineApplication.class b/backend/matching-engine/target/classes/org/example/matching/MatchingEngineApplication.class deleted file mode 100644 index 51acbb5..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/MatchingEngineApplication.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/Replay.class b/backend/matching-engine/target/classes/org/example/matching/Replay.class deleted file mode 100644 index ca9e67d..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/Replay.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/Wallets/InMemoryWalletService.class b/backend/matching-engine/target/classes/org/example/matching/Wallets/InMemoryWalletService.class deleted file mode 100644 index c429079..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/Wallets/InMemoryWalletService.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/Wallets/RiskManager.class b/backend/matching-engine/target/classes/org/example/matching/Wallets/RiskManager.class deleted file mode 100644 index 54bd4dd..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/Wallets/RiskManager.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/Wallets/WalletService.class b/backend/matching-engine/target/classes/org/example/matching/Wallets/WalletService.class deleted file mode 100644 index 3a90051..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/Wallets/WalletService.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/advice/ApiExceptionHandler.class b/backend/matching-engine/target/classes/org/example/matching/api/advice/ApiExceptionHandler.class deleted file mode 100644 index a5456f4..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/advice/ApiExceptionHandler.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/controller/MarketController.class b/backend/matching-engine/target/classes/org/example/matching/api/controller/MarketController.class deleted file mode 100644 index ee75bf8..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/controller/MarketController.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/controller/OrderController.class b/backend/matching-engine/target/classes/org/example/matching/api/controller/OrderController.class deleted file mode 100644 index 49b3483..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/controller/OrderController.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/controller/WalletController.class b/backend/matching-engine/target/classes/org/example/matching/api/controller/WalletController.class deleted file mode 100644 index 92d4fff..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/controller/WalletController.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/DepositRequest.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/DepositRequest.class deleted file mode 100644 index 050c968..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/DepositRequest.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class deleted file mode 100644 index b7f8866..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse.class deleted file mode 100644 index ca71dbc..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderMapper.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderMapper.class deleted file mode 100644 index 1b97ccc..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderMapper.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderRequest.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderRequest.class deleted file mode 100644 index 1407b0a..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderRequest.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class deleted file mode 100644 index 9e8af78..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse.class deleted file mode 100644 index 434c332..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/PriceLevel.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/PriceLevel.class deleted file mode 100644 index 03e7eb7..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/PriceLevel.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class deleted file mode 100644 index 0138625..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse.class b/backend/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse.class deleted file mode 100644 index be44d0c..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/api/service/OrderService.class b/backend/matching-engine/target/classes/org/example/matching/api/service/OrderService.class deleted file mode 100644 index 39b84ef..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/api/service/OrderService.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/app/MatchingEngineApplication.class b/backend/matching-engine/target/classes/org/example/matching/app/MatchingEngineApplication.class deleted file mode 100644 index 366dc50..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/app/MatchingEngineApplication.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/config/MatchingEngineConfig.class b/backend/matching-engine/target/classes/org/example/matching/config/MatchingEngineConfig.class deleted file mode 100644 index 6dbc2de..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/config/MatchingEngineConfig.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/journal/EventJournal.class b/backend/matching-engine/target/classes/org/example/matching/journal/EventJournal.class deleted file mode 100644 index 0844424..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/journal/EventJournal.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/matching/MatchingEngine.class b/backend/matching-engine/target/classes/org/example/matching/matching/MatchingEngine.class deleted file mode 100644 index f84c455..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/matching/MatchingEngine.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/model/Order.class b/backend/matching-engine/target/classes/org/example/matching/model/Order.class deleted file mode 100644 index f95401f..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/model/Order.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/model/OrderBook.class b/backend/matching-engine/target/classes/org/example/matching/model/OrderBook.class deleted file mode 100644 index bf855d2..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/model/OrderBook.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/model/OrderSide.class b/backend/matching-engine/target/classes/org/example/matching/model/OrderSide.class deleted file mode 100644 index a9a98a8..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/model/OrderSide.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/model/Reservation.class b/backend/matching-engine/target/classes/org/example/matching/model/Reservation.class deleted file mode 100644 index 71300c0..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/model/Reservation.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/model/Trade.class b/backend/matching-engine/target/classes/org/example/matching/model/Trade.class deleted file mode 100644 index ce71b93..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/model/Trade.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/model/Wallet.class b/backend/matching-engine/target/classes/org/example/matching/model/Wallet.class deleted file mode 100644 index 41fecd1..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/model/Wallet.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/orderbook/InMemoryOrderRepository.class b/backend/matching-engine/target/classes/org/example/matching/orderbook/InMemoryOrderRepository.class deleted file mode 100644 index 8d82348..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/orderbook/InMemoryOrderRepository.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/orderbook/OrderOrchestrator.class b/backend/matching-engine/target/classes/org/example/matching/orderbook/OrderOrchestrator.class deleted file mode 100644 index 31340e1..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/orderbook/OrderOrchestrator.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/orderbook/OrderRepository.class b/backend/matching-engine/target/classes/org/example/matching/orderbook/OrderRepository.class deleted file mode 100644 index 8a4a058..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/orderbook/OrderRepository.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/util/IdGenerator.class b/backend/matching-engine/target/classes/org/example/matching/util/IdGenerator.class deleted file mode 100644 index 884a082..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/util/IdGenerator.class and /dev/null differ diff --git a/backend/matching-engine/target/classes/org/example/matching/validation/OrderValidator.class b/backend/matching-engine/target/classes/org/example/matching/validation/OrderValidator.class deleted file mode 100644 index 3f28dce..0000000 Binary files a/backend/matching-engine/target/classes/org/example/matching/validation/OrderValidator.class and /dev/null differ diff --git a/backend/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/backend/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst deleted file mode 100644 index 200dd91..0000000 --- a/backend/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ /dev/null @@ -1,37 +0,0 @@ -org/example/matching/api/controller/MarketController.class -org/example/Main.class -org/example/matching/app/MatchingEngineApplication.class -org/example/matching/model/Trade.class -org/example/matching/model/OrderSide.class -org/example/matching/api/dto/OrderResponse.class -org/example/matching/journal/EventJournal.class -org/example/matching/Wallets/WalletService.class -org/example/matching/Wallets/InMemoryWalletService.class -org/example/matching/model/Reservation.class -org/example/matching/validation/OrderValidator.class -org/example/matching/api/controller/OrderController.class -org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class -org/example/matching/api/advice/ApiExceptionHandler.class -org/example/matching/model/OrderBook.class -org/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class -org/example/matching/orderbook/OrderOrchestrator.class -org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class -org/example/matching/orderbook/InMemoryOrderRepository.class -org/example/matching/matching/MatchingEngine.class -org/example/matching/model/Order.class -org/example/matching/api/dto/DepositRequest.class -org/example/matching/api/dto/PriceLevel.class -org/example/matching/api/dto/WalletResponse.class -org/example/matching/Replay.class -org/example/matching/util/IdGenerator.class -org/example/matching/api/controller/WalletController.class -org/example/matching/Wallets/RiskManager.class -org/example/matching/api/dto/OrderMapper.class -org/example/matching/api/dto/OrderBookResponse.class -org/example/matching/api/dto/OrderRequest.class -org/example/matching/config/MatchingEngineConfig.class -org/example/matching/api/service/MarketDataService.class -org/example/matching/orderbook/OrderRepository.class -org/example/matching/MatchingEngineApplication.class -org/example/matching/model/Wallet.class -org/example/matching/api/service/OrderService.class diff --git a/backend/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/backend/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst deleted file mode 100644 index d31a6fd..0000000 --- a/backend/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ /dev/null @@ -1,34 +0,0 @@ -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/app/MatchingEngineApplication.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/journal/EventJournal.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Trade.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/MatchingEngineApplication.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/WalletResponse.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/OrderOrchestrator.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/OrderBook.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Wallet.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/PriceLevel.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderResponse.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/advice/ApiExceptionHandler.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/validation/OrderValidator.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/OrderRepository.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/Main.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Order.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Reservation.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderBookResponse.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/DepositRequest.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderRequest.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Replay.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/util/IdGenerator.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/OrderSide.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderMapper.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java diff --git a/backend/matching-engine/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/backend/matching-engine/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst deleted file mode 100644 index e69de29..0000000 diff --git a/backend/src/pom.xml b/backend/src/pom.xml index 39b72ca..b4b5683 100644 --- a/backend/src/pom.xml +++ b/backend/src/pom.xml @@ -9,8 +9,8 @@ 1.0-SNAPSHOT pom - common - matching-engine + ../common + ../matching-engine diff --git a/common.iml b/common.iml deleted file mode 100644 index 99f38c6..0000000 --- a/common.iml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index a547bf3..7ceb59f 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -22,3 +22,4 @@ dist-ssr *.njsproj *.sln *.sw? +.env diff --git a/frontend/README.md b/frontend/README.md index d2e7761..5dbf533 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,73 +1,387 @@ -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## React Compiler - -The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: - -```js -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - - // Remove tseslint.configs.recommended and replace with this - tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - tseslint.configs.stylisticTypeChecked, - - // Other configs... - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` - -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: - -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - // Enable lint rules for React - reactX.configs['recommended-typescript'], - // Enable lint rules for React DOM - reactDom.configs.recommended, - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) +# Max Frontend - Stock Exchange UI + +Modern, responsive frontend for the Max stock exchange platform built with React, TypeScript, and Tailwind CSS. + +## 🏗️ Project Structure + +``` +frontend/ +├── public/ # Static assets +│ └── vite.svg +├── src/ +│ ├── components/ # Reusable UI components +│ │ ├── Category/ +│ │ │ └── CategoryBar.tsx +│ │ ├── Header/ +│ │ │ ├── Header.tsx +│ │ │ ├── MaxLogo.tsx +│ │ │ └── UserAuth.tsx +│ │ └── Search/ +│ │ ├── MobileSearch.tsx +│ │ └── SearchBar.tsx +│ ├── pages/ # Application pages +│ │ └── Home.tsx +│ ├── App.tsx # Main app component +│ ├── main.tsx # Application entry point +│ ├── index.css # Global styles +│ └── App.css # App-specific styles +├── index.html # HTML template +├── package.json # Dependencies and scripts +├── vite.config.ts # Vite configuration +└── tsconfig.json # TypeScript configuration +``` + +## 🛠️ Technology Stack + +- **Framework**: React 19.2.0 +- **Language**: TypeScript 5.9.3 +- **Build Tool**: Vite 7.3.1 +- **Styling**: Tailwind CSS 4.2.1 +- **Routing**: React Router DOM 7.13.1 +- **Linting**: ESLint 9.39.1 + +## 📋 Prerequisites + +- Node.js v18 or higher +- npm v9 or higher (or yarn/pnpm) + +## 🚀 Getting Started + +### 1. Install Dependencies + +```bash +cd frontend +npm install +``` + +### 2. Start Development Server + +```bash +npm run dev ``` + +The application will start on `http://localhost:5173` + +### 3. Build for Production + +```bash +npm run build +``` + +Build output will be in the `dist/` directory. + +### 4. Preview Production Build + +```bash +npm run preview +``` + +## 📜 Available Scripts + +| Script | Description | +|--------|-------------| +| `npm run dev` | Start development server with hot reload | +| `npm run build` | Build for production | +| `npm run preview` | Preview production build locally | +| `npm run lint` | Run ESLint to check code quality | + +## 🎨 UI Components + +### Header Components + +#### MaxLogo +- Responsive logo with different sizes for mobile, tablet, and desktop +- Sizes: `text-3xl` (mobile), `text-4xl` (tablet), `text-5xl` (desktop) + +#### UserAuth +- Login and Sign up buttons +- Responsive padding and text sizing +- Fixed height for consistency across devices +- Search button for mobile/tablet views + +#### SearchBar +- Desktop search input with animated icon +- Shake animation on focus +- Color changes from gray to `#48CAE4` when focused + +### Search Components + +#### MobileSearch +- Full-screen search modal for mobile/tablet +- Blur background overlay +- Animated search icon +- Auto-focus input field +- Close button with X icon + +### Category Components + +#### CategoryBar +- Horizontal scrolling category navigation +- Mouse wheel horizontal scrolling support +- Smooth scroll behavior +- Categories: Trending, Politics, Sports, Culture, Crypto, Climate, Economics, Mentions, Companies, Financials, Tech & Science + +## 🎨 Styling + +### Tailwind CSS Configuration + +The project uses Tailwind CSS 4 with custom utilities: + +```css +/* Custom Scrollbar Hide */ +.scrollbar-hide::-webkit-scrollbar { + display: none; +} +.scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; +} + +/* Custom Shake Animation */ +@keyframes shake { + 0%, 100% { transform: rotate(0deg); } + 25% { transform: rotate(15deg); } + 75% { transform: rotate(-15deg); } +} +.animate-shake { + animation: shake 0.5s ease-in-out; +} +``` + +### Color Palette + +- **Primary**: `#48CAE4` (Cyan) +- **Background**: `#000000` (Black) +- **Secondary Background**: `#242423` (Dark Gray) +- **Text**: `#ffffff` (White) +- **Text Secondary**: `rgba(255, 255, 255, 0.6)` (White 60%) + +### Responsive Breakpoints + +- **Mobile**: < 640px (default) +- **Tablet**: ≥ 640px (`sm:`) +- **Desktop**: ≥ 768px (`md:`) + +## 🔧 Development + +### Component Development + +Create new components in `src/components/`: + +```tsx +// Example component +export default function MyComponent() { + return ( +
+ {/* Component content */} +
+ ); +} +``` + +### Adding New Pages + +1. Create page in `src/pages/` +2. Add route in `App.tsx`: + +```tsx +} /> +``` + +### Custom Hooks + +Create custom hooks in `src/hooks/` (create directory if needed): + +```tsx +export function useCustomHook() { + // Hook logic +} +``` + +## 🎯 Features + +### Responsive Design +- Mobile-first approach +- Adaptive layouts for all screen sizes +- Touch-friendly interactions + +### Animations +- Smooth transitions +- Shake effects on focus +- Scale animations on button clicks +- Blur backgrounds for modals + +### User Experience +- Auto-focus on search inputs +- Keyboard navigation support +- Mouse wheel horizontal scrolling +- Visual feedback on interactions + +## 🐛 Debugging + +### Enable React DevTools +Install React Developer Tools browser extension + +### TypeScript Errors +```bash +# Check TypeScript errors +npx tsc --noEmit +``` + +### Vite Issues +```bash +# Clear Vite cache +rm -rf node_modules/.vite +npm run dev +``` + +## 📦 Building + +### Development Build +```bash +npm run dev +``` + +### Production Build +```bash +npm run build +``` + +### Analyze Bundle Size +```bash +npm run build -- --mode analyze +``` + +## 🚀 Deployment + +### Deploy to Vercel +```bash +npm install -g vercel +vercel +``` + +### Deploy to Netlify +```bash +npm run build +# Upload dist/ folder to Netlify +``` + +### Environment Variables +Create `.env` file: +```env +VITE_API_URL=http://localhost:8080 +VITE_APP_NAME=Max +``` + +Access in code: +```tsx +const apiUrl = import.meta.env.VITE_API_URL; +``` + +## 🧪 Testing (Future) + +### Setup Testing +```bash +npm install -D vitest @testing-library/react @testing-library/jest-dom +``` + +### Run Tests +```bash +npm run test +``` + +## 📱 Mobile Optimization + +- Bottom navigation for mobile devices +- Touch-optimized button sizes +- Responsive font sizes +- Optimized images and assets + +## ♿ Accessibility + +- Semantic HTML elements +- ARIA labels where needed +- Keyboard navigation support +- Focus indicators +- Color contrast compliance + +## 🔒 Security + +- No sensitive data in client-side code +- Environment variables for API endpoints +- Input sanitization +- XSS protection + +## 📈 Performance + +- Code splitting with React.lazy +- Optimized images +- Minimal bundle size +- Fast initial load time + +## 🎨 Design System + +### Typography +- Font: System fonts for optimal performance +- Sizes: Responsive with Tailwind utilities + +### Spacing +- Consistent padding and margins +- Gap utilities for flex/grid layouts + +### Components +- Reusable and composable +- Props for customization +- TypeScript for type safety + +## 🤝 Contributing + +1. Follow the existing code style +2. Use TypeScript for type safety +3. Write responsive components +4. Test on multiple devices +5. Use meaningful commit messages + +## 📚 Resources + +- [React Documentation](https://react.dev/) +- [TypeScript Documentation](https://www.typescriptlang.org/) +- [Tailwind CSS Documentation](https://tailwindcss.com/) +- [Vite Documentation](https://vitejs.dev/) +- [React Router Documentation](https://reactrouter.com/) + +## 🐞 Common Issues + +### Port Already in Use +```bash +# Change port +npm run dev -- --port 3000 +``` + +### Module Not Found +```bash +# Reinstall dependencies +rm -rf node_modules package-lock.json +npm install +``` + +### TypeScript Errors +```bash +# Update TypeScript +npm install -D typescript@latest +``` + +## 📄 License + +This project is private and proprietary. + +## 👥 Authors + +- Aum Patel +- Ved Patel + +## 🙏 Acknowledgments + +- React team for the amazing framework +- Tailwind CSS for the utility-first approach +- Vite for the blazing fast build tool diff --git a/frontend/package-lock.json b/frontend/package-lock.json index def3310..d50fda0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,9 +8,11 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.2.1", "react": "^19.2.0", "react-dom": "^19.2.0", + "react-redux": "^9.2.0", "react-router-dom": "^7.13.1", "tailwindcss": "^4.2.1" }, @@ -981,6 +983,32 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", @@ -1313,6 +1341,18 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@tailwindcss/node": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", @@ -1642,7 +1682,7 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -1658,6 +1698,12 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", @@ -2216,7 +2262,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/debug": { @@ -2703,6 +2749,16 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -3373,6 +3429,29 @@ "react": "^19.2.4" } }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", @@ -3421,6 +3500,27 @@ "react-dom": ">=18" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3702,6 +3802,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 776c2f8..2eaa31b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,9 +10,11 @@ "preview": "vite preview" }, "dependencies": { + "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.2.1", "react": "^19.2.0", "react-dom": "^19.2.0", + "react-redux": "^9.2.0", "react-router-dom": "^7.13.1", "tailwindcss": "^4.2.1" }, diff --git a/frontend/src/App.css b/frontend/src/App.css index 8e9aca3..c446417 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,6 +1,6 @@ #root { - max-width: 1315px; + margin: 0 auto; - padding: 0.5rem; + } diff --git a/frontend/src/components/Category/CategoryBar.tsx b/frontend/src/components/Category/CategoryBar.tsx new file mode 100644 index 0000000..54b38bb --- /dev/null +++ b/frontend/src/components/Category/CategoryBar.tsx @@ -0,0 +1,33 @@ +import { useRef } from "react"; +import { useSelector } from "react-redux"; +import type { RootState } from "../../store/store"; + +export default function CategoryBar() { + const categories = useSelector((state: RootState) => state.category.categories); + const scrollRef = useRef(null); + + const handleWheel = (e: React.WheelEvent) => { + if (scrollRef.current) { + e.preventDefault(); + scrollRef.current.scrollLeft += e.deltaY; + } + }; + + return ( +
+ {categories.map((category) => ( + + {category} + + ))} +
+ ); +} diff --git a/frontend/src/components/Footer/Footer.tsx b/frontend/src/components/Footer/Footer.tsx new file mode 100644 index 0000000..64d51cc --- /dev/null +++ b/frontend/src/components/Footer/Footer.tsx @@ -0,0 +1,41 @@ +import { useSelector } from "react-redux"; +import type { RootState } from "../../store/store"; + +export default function Footer() { + const { sections, copyright, disclaimer } = useSelector( + (state: RootState) => state.footer + ); + + return ( +
+
+
+ {sections.map((section) => ( +
+

+ {section.title} +

+ +
+ ))} +
+ +
+

{copyright}

+

{disclaimer}

+
+
+
+ ); +} diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx deleted file mode 100644 index a186012..0000000 --- a/frontend/src/components/Header.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import MaxLogo from "./MaxLogo"; -import SearchBar from "./SearchBar"; -import UserAuth from "./UserAuth"; - -export default function Header() { - return ( -
- - - - - -
- ); -} diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx new file mode 100644 index 0000000..5a0e71f --- /dev/null +++ b/frontend/src/components/Header/Header.tsx @@ -0,0 +1,21 @@ +import CategoryBar from "../Category/CategoryBar"; +import MaxLogo from "./MaxLogo"; +import SearchBar from "../Search/SearchBar"; +import UserAuth from "./UserAuth"; + +export default function Header() { + return ( +
+
+ + + + + + + +
+ +
+ ); +} diff --git a/frontend/src/components/MaxLogo.tsx b/frontend/src/components/Header/MaxLogo.tsx similarity index 65% rename from frontend/src/components/MaxLogo.tsx rename to frontend/src/components/Header/MaxLogo.tsx index 6368239..d95b6b5 100644 --- a/frontend/src/components/MaxLogo.tsx +++ b/frontend/src/components/Header/MaxLogo.tsx @@ -2,7 +2,7 @@ export default function MaxLogo() { return ( ) diff --git a/frontend/src/components/Header/UserAuth.tsx b/frontend/src/components/Header/UserAuth.tsx new file mode 100644 index 0000000..1756e8f --- /dev/null +++ b/frontend/src/components/Header/UserAuth.tsx @@ -0,0 +1,14 @@ +import MobileSearch from "../Search/MobileSearch"; +export default function MaxLogo() { + return ( +
+ + + +
+ ); +} diff --git a/frontend/src/components/InfoCard/InfoCard.tsx b/frontend/src/components/InfoCard/InfoCard.tsx new file mode 100644 index 0000000..b62af59 --- /dev/null +++ b/frontend/src/components/InfoCard/InfoCard.tsx @@ -0,0 +1,29 @@ +interface InfoCardProps { + icon: React.ReactNode; + title: string; + description: string; +} + +export default function InfoCard({ icon, title, description }: InfoCardProps) { + return ( +
+
+
+
{icon}
+
+

{title}

+

{description}

+
+
+ + + +
+
+ ); +} diff --git a/frontend/src/components/MarketCard/MarketCard.tsx b/frontend/src/components/MarketCard/MarketCard.tsx new file mode 100644 index 0000000..936ecd8 --- /dev/null +++ b/frontend/src/components/MarketCard/MarketCard.tsx @@ -0,0 +1,223 @@ +interface TrendingOption { + label: string; + image: string; + payout: string; + odds: string; + color: string; +} + +interface MarketCardProps { + title: string; + options: TrendingOption[]; + volume: string; + marketCount: string; + newsText: string; + currentIndex?: number; + totalCards?: number; + onNext?: () => void; + onPrev?: () => void; +} + +export default function MarketCard({ + title, + options: candidates, + volume, + marketCount, + newsText, + currentIndex = 1, + totalCards = 7, + onNext, + onPrev, +}: MarketCardProps) { + return ( +
+ {/* Header with navigation */} +
+

{title}

+
+ + + {currentIndex} of {totalCards} + + +
+
+ + {/* Mobile/Tablet Layout (< 768px) */} +
+
+ Market +
+ Pays out + Odds +
+
+ + {/* Candidates */} + {candidates.map((candidate, index) => ( +
+
+ {candidate.label} +
+

{candidate.label}

+
+
+
+
+ {candidate.payout} + + {candidate.odds} + +
+
+ ))} + + {/* Volume and markets */} +
+ {volume} vol + {marketCount} markets +
+ + {/* News section */} +
+

+ News {newsText} +

+
+
+ + {/* Desktop Layout (>= 768px) */} +
+ {/* Left side - Market info */} +
+
+ Market + Pays out + Odds +
+ + {/* Candidates */} + {candidates.map((candidate, index) => ( +
+
+ {candidate.label} +
+

{candidate.label}

+
+
+
+ {candidate.payout} + + {candidate.odds} + +
+ ))} + + {/* Volume and markets */} +
+ {volume} vol + {marketCount} markets +
+ + {/* News section */} +
+

+ News {newsText} +

+
+
+ + {/* Right side - Chart and legend */} +
+ {/* Legend */} +
+
+ {candidates.map((candidate, index) => ( +
+
+ {candidate.label} + {candidate.odds} +
+ ))} +
+ Max +
+ + {/* Chart placeholder */} +
+ + {/* Grid lines */} + {[0, 20, 40, 60, 80, 100].map((y) => ( + + ))} + + {/* Sample chart lines */} + + + {candidates[2] && ( + + )} + + + {/* Y-axis labels */} +
+ 40% + 30% + 20% + 10% + 0% +
+ + {/* X-axis labels */} +
+ May + Jul + Oct + Dec + Mar +
+
+
+
+
+ ); +} diff --git a/frontend/src/components/Search/MobileSearch.tsx b/frontend/src/components/Search/MobileSearch.tsx new file mode 100644 index 0000000..742009b --- /dev/null +++ b/frontend/src/components/Search/MobileSearch.tsx @@ -0,0 +1,84 @@ +import { useState, useEffect, useRef } from "react"; +import MaxLogo from "../Header/MaxLogo"; +export default function MobileSearch() { + const [isSearchOpen, setIsSearchOpen] = useState(false); + const [shouldShake, setShouldShake] = useState(false); + + const handleFocus = () => { + setShouldShake(true); + setTimeout(() => setShouldShake(false), 500); + }; + + useEffect(() => { + if (isSearchOpen) { + setShouldShake(true); + const timer = setTimeout(() => setShouldShake(false), 500); + return () => clearTimeout(timer); + } + }, [isSearchOpen]); + return ( + <> + + {isSearchOpen && ( +
+
+ + +
+
+
+ + + + +
+
+
+ )} + + ); +} diff --git a/frontend/src/components/Search/SearchBar.tsx b/frontend/src/components/Search/SearchBar.tsx new file mode 100644 index 0000000..2ad884a --- /dev/null +++ b/frontend/src/components/Search/SearchBar.tsx @@ -0,0 +1,35 @@ +import { useDispatch, useSelector } from "react-redux"; +import type { RootState, AppDispatch } from "../../store/store"; +import { triggerShake, resetShake } from "../../store/searchSlice"; + +export default function SearchBar() { + const dispatch = useDispatch(); + const shouldShake = useSelector((state: RootState) => state.search.shouldShake); + + const handleFocus = () => { + dispatch(triggerShake()); + setTimeout(() => dispatch(resetShake()), 500); + }; + + return ( +
+ + + + +
+ ); +} diff --git a/frontend/src/components/SearchBar.tsx b/frontend/src/components/SearchBar.tsx deleted file mode 100644 index 041344f..0000000 --- a/frontend/src/components/SearchBar.tsx +++ /dev/null @@ -1,26 +0,0 @@ -export default function MaxLogo() { - return ( -
-
- - - -
- - -
- ) -} \ No newline at end of file diff --git a/frontend/src/components/TopicCard/TopicCard.tsx b/frontend/src/components/TopicCard/TopicCard.tsx new file mode 100644 index 0000000..ca58a63 --- /dev/null +++ b/frontend/src/components/TopicCard/TopicCard.tsx @@ -0,0 +1,80 @@ +interface Option { + label: string; + payout: string; + odds: string; + color?: string; +} + +interface TopicCardProps { + category: string; + categoryIcon?: React.ReactNode; + title: string; + date: string; + options: Option[]; + volume: string; + marketCount: string; +} + +export default function TopicCard({ + category, + categoryIcon, + title, + date, + options, + volume, + marketCount, +}: TopicCardProps) { + return ( +
+ {/* Category Header */} +
+
+ {categoryIcon} +
+ + {category} + +
+ + {/* Title */} +

+ {title} +

+ + {/* Date */} +

{date}

+ + {/* Options */} +
+ {options.map((option, index) => ( +
+
+
+ {option.label} +
+
+
+
+ {option.payout} + + {option.odds} + +
+
+ ))} +
+ + {/* Footer */} +
+ {volume} vol + {marketCount} markets +
+
+ ); +} diff --git a/frontend/src/components/UserAuth.tsx b/frontend/src/components/UserAuth.tsx deleted file mode 100644 index 74bc37e..0000000 --- a/frontend/src/components/UserAuth.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export default function MaxLogo() { - return ( -
- - -
- ); -} diff --git a/frontend/src/hooks/useHomeData.ts b/frontend/src/hooks/useHomeData.ts new file mode 100644 index 0000000..f921d11 --- /dev/null +++ b/frontend/src/hooks/useHomeData.ts @@ -0,0 +1,28 @@ +// hooks/useHomeData.ts +import { useState, useEffect } from "react"; +import { marketService } from "../services/marketService"; +import type { HomePageData } from "../store/market"; + +export function useHomeData() { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const homeData = await marketService.getHomeData(); + setData(homeData); + } catch (err) { + setError(err instanceof Error ? err.message : "Something went wrong"); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, []); // single fetch on mount, no frequent requests + + return { data, loading, error }; +} \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css index 47b87ef..b605005 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -4,6 +4,31 @@ background-color: #000000; } +@layer utilities { + .scrollbar-hide::-webkit-scrollbar { + display: none; + } + .scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; + } + + @keyframes shake { + 0%, 100% { + transform: rotate(0deg); + } + 25% { + transform: rotate(15deg); + } + 75% { + transform: rotate(-15deg); + } + } + + .animate-shake { + animation: shake 0.5s ease-in-out; + } +} body { min-width: 320px; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 7687293..dd6ceae 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -2,9 +2,13 @@ import ReactDOM from 'react-dom/client' import './index.css' import App from './App.tsx' import { BrowserRouter } from 'react-router-dom' +import { Provider } from 'react-redux' +import { store } from './store/store' ReactDOM.createRoot(document.getElementById('root')!).render( - - - , + + + + + , ) diff --git a/frontend/src/mocks/homeData.mock.ts b/frontend/src/mocks/homeData.mock.ts new file mode 100644 index 0000000..ef2a8da --- /dev/null +++ b/frontend/src/mocks/homeData.mock.ts @@ -0,0 +1,146 @@ +// mocks/homeData.mock.ts +import type { HomePageData } from "../store/market"; + +export const mockHomeData: HomePageData = { + trending: [ + { + id: "t1", + title: "2028 U.S. Presidential Election winner?", + options: [ + { label: "Gavin Newsom", image: "https://i.pravatar.cc/40?img=1", payout: "5.25x", odds: "18%", color: "#4a9eff" }, + { label: "J.D. Vance", image: "https://i.pravatar.cc/40?img=2", payout: "5.25x", odds: "17%", color: "#00d4aa" }, + { label: "Marco Rubio", image: "https://i.pravatar.cc/40?img=3", payout: "5.25x", odds: "18%", color: "#ffa500" }, + ], + volume: "$17,834,984", + marketCount: "22", + newsText: "Secretary of State Marco Rubio's chances in the race for the 2028 Republican presidential nomination have risen...", + }, + { + id: "t2", + title: "Will the Fed cut rates in Q2 2025?", + options: [ + { label: "Yes", image: "https://i.pravatar.cc/40?img=4", payout: "2.65x", odds: "38%", color: "#00d4aa" }, + { label: "No", image: "https://i.pravatar.cc/40?img=5", payout: "1.62x", odds: "62%", color: "#ff6b6b" }, + ], + volume: "$6,750,000", + marketCount: "22", + newsText: "Federal Reserve officials remain cautious about cutting rates amid persistent inflation...", + }, + ], + categories: [ + { + categoryId: "politics", + categoryName: "Politics", + categoryIcon: "shield", + markets: [ + { + id: "m1", + title: "How long will the government shutdown last?", + date: "Feb 14 @ 10:00AM", + options: [ + { label: "At least 60 days", payout: "2.05x", odds: "46%", color: "#48CAE4" }, + { label: "At least 55 days", payout: "1.65x", odds: "59%", color: "#48CAE4" }, + ], + volume: "$4,199,600", + marketCount: "21", + }, + { + id: "m2", + title: "Will members of Congress be banned from trading stocks?", + date: "Before Jan 21, 2029", + options: [ + { label: "Before Jan 21, 2029", payout: "1.59x", odds: "56%", color: "#00d4aa" }, + { label: "Before 2027", payout: "7.14x", odds: "12%", color: "#00d4aa" }, + ], + volume: "$222,525", + marketCount: "3", + }, + { + id: "m3", + title: "Will Americans receive tariff stimulus checks?", + date: "Before 2027", + options: [ + { label: "Before 2027", payout: "5.25x", odds: "18%", color: "#48CAE4" }, + { label: "Before August", payout: "10.4x", odds: "9%", color: "#4a9eff" }, + ], + volume: "$1,334,816", + marketCount: "9", + }, + { + id: "m4", + title: "What will the US tariff rate on China be on July 1?", + date: "Jul 1, 2025", + options: [ + { label: "Between 10% and 19.99%", payout: "1.47x", odds: "69%", color: "#00d4aa" }, + { label: "Between 20% and 29.99%", payout: "4.35x", odds: "27%", color: "#00d4aa" }, + ], + volume: "$36,656", + marketCount: "7", + }, + // This 5th one won't show — slice(0,4) will cut it + { + id: "m5", + title: "Will Biden endorse a 2028 candidate before 2026?", + date: "Dec 31, 2025", + options: [ + { label: "Yes", payout: "3.10x", odds: "30%", color: "#4a9eff" }, + { label: "No", payout: "1.42x", odds: "70%", color: "#ff6b6b" }, + ], + volume: "$980,000", + marketCount: "5", + }, + ], + }, + { + categoryId: "comedy", + categoryName: "Comedy", + categoryIcon: "laugh", + markets: [ + { + id: "c1", + title: "Will SNL get cancelled in 2025?", + date: "Dec 31, 2025", + options: [ + { label: "Yes", payout: "8.00x", odds: "12%", color: "#ffa500" }, + { label: "No", payout: "1.14x", odds: "88%", color: "#00d4aa" }, + ], + volume: "$450,000", + marketCount: "4", + }, + { + id: "c2", + title: "Will a stand-up special win an Emmy in 2025?", + date: "Sep 30, 2025", + options: [ + { label: "Yes", payout: "2.50x", odds: "40%", color: "#00d4aa" }, + { label: "No", payout: "1.65x", odds: "60%", color: "#ff6b6b" }, + ], + volume: "$120,000", + marketCount: "2", + }, + { + id: "c3", + title: "Will Netflix release 10+ comedy specials in 2025?", + date: "Dec 31, 2025", + options: [ + { label: "Yes", payout: "1.35x", odds: "74%", color: "#00d4aa" }, + { label: "No", payout: "3.85x", odds: "26%", color: "#ff6b6b" }, + ], + volume: "$89,000", + marketCount: "3", + }, + { + id: "c4", + title: "Will a comedian host the Oscars in 2026?", + date: "Mar 2026", + options: [ + { label: "Yes", payout: "1.75x", odds: "57%", color: "#4a9eff" }, + { label: "No", payout: "2.25x", odds: "43%", color: "#ff6b6b" }, + ], + volume: "$200,000", + marketCount: "6", + }, + ], + }, + ], +}; \ No newline at end of file diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 19b55df..543f165 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,8 +1,103 @@ -import Header from "../components/Header"; +// pages/Home.tsx +import { useState } from "react"; +import { useHomeData } from "../hooks/useHomeData"; +import Header from "../components/Header/Header"; +import Footer from "../components/Footer/Footer"; +import MarketCard from "../components/MarketCard/MarketCard"; +import TopicCard from "../components/TopicCard/TopicCard"; + +function LoadingSkeleton() { + return ( +
+
Loading markets...
+
+ ); +} + +function ErrorState({ message }: { message: string }) { + return ( +
+
{message}
+
+ ); +} + +const TOPICS_TO_SHOW = 4; // controls how many TopicCards show per category + export default function Home() { - return ( - <> -
- - ) + const { data, loading, error } = useHomeData(); + const [currentTrendingIndex, setCurrentTrendingIndex] = useState(0); + + if (loading) return ; + if (error) return ; + if (!data) return null; + + const currentTrending = data.trending[currentTrendingIndex]; + + return ( +
+
+ +
+ + {/* ✅ MarketCard - driven by trending array from backend */} + {currentTrending && ( + + setCurrentTrendingIndex((i) => + i < data.trending.length - 1 ? i + 1 : 0 + ) + } + onPrev={() => + setCurrentTrendingIndex((i) => + i > 0 ? i - 1 : data.trending.length - 1 + ) + } + /> + )} + + {/* ✅ Categories - backend sends all markets, frontend shows top 4 */} + {data.categories.map((category) => ( +
+ + {/* Section Header */} +
+

+ {category.categoryName} +

+ + + +
+ + {/* Top 4 only — slice happens here in frontend */} +
+ {category.markets.slice(0, TOPICS_TO_SHOW).map((market) => ( + + ))} +
+ +
+ ))} + +
+ +
+
+ ); } \ No newline at end of file diff --git a/frontend/src/services/marketService.ts b/frontend/src/services/marketService.ts new file mode 100644 index 0000000..9451406 --- /dev/null +++ b/frontend/src/services/marketService.ts @@ -0,0 +1,21 @@ +import type { HomePageData } from "../store/market"; +import { mockHomeData } from "../mocks/homeData.mock"; + + +const BASE_URL = import.meta.env.VITE_API_URL; +const USE_MOCK = import.meta.env.VITE_USE_MOCK === "true"; // 👈 flag + + +export const marketService = { + getHomeData: async (): Promise => { + if (USE_MOCK) { + // Simulates real network delay so loading state is visible + await new Promise((res) => setTimeout(res, 800)); + return mockHomeData; + } + + const res = await fetch(`${BASE_URL}/home`); + if (!res.ok) throw new Error("Failed to fetch home data"); + return res.json(); + }, +}; \ No newline at end of file diff --git a/frontend/src/store/categorySlice.ts b/frontend/src/store/categorySlice.ts new file mode 100644 index 0000000..6c015b6 --- /dev/null +++ b/frontend/src/store/categorySlice.ts @@ -0,0 +1,29 @@ +import { createSlice } from "@reduxjs/toolkit"; + +interface CategoryState { + categories: string[]; +} + +const initialState: CategoryState = { + categories: [ + "Trending", + "Politics", + "Sports", + "Culture", + "Crypto", + "Climate", + "Economics", + "Mentions", + "Companies", + "Financials", + "Tech & Science", + ], +}; + +const categorySlice = createSlice({ + name: "category", + initialState, + reducers: {}, +}); + +export default categorySlice.reducer; diff --git a/frontend/src/store/footerSlice.ts b/frontend/src/store/footerSlice.ts new file mode 100644 index 0000000..d7ecda8 --- /dev/null +++ b/frontend/src/store/footerSlice.ts @@ -0,0 +1,74 @@ +import { createSlice } from "@reduxjs/toolkit"; + +interface FooterLink { + label: string; + href: string; +} + +interface FooterSection { + title: string; + links: FooterLink[]; +} + +interface FooterState { + sections: FooterSection[]; + disclaimer: string; + copyright: string; +} + +const initialState: FooterState = { + sections: [ + { + title: "Company", + links: [ + { label: "Blog", href: "#" }, + { label: "Careers", href: "#" }, + { label: "Privacy Policy", href: "#" }, + { label: "Contest Rules", href: "#" }, + { label: "Data Terms of Service", href: "#" }, + { label: "Company", href: "#" }, + { label: "Brand Kit", href: "#" }, + ], + }, + { + title: "Social", + links: [ + { label: "X (Twitter)", href: "#" }, + { label: "LinkedIn", href: "#" }, + { label: "Discord", href: "#" }, + { label: "Instagram", href: "#" }, + { label: "Reddit", href: "#" }, + { label: "TikTok", href: "#" }, + ], + }, + { + title: "Product", + links: [ + { label: "Help Center", href: "#" }, + { label: "API", href: "#" }, + { label: "FAQ", href: "#" }, + { label: "FAQ for Finance Professionals", href: "#" }, + { label: "Regulatory", href: "#" }, + { label: "Trading Hours", href: "#" }, + { label: "Fee Schedule", href: "#" }, + { label: "Trading Prohibitions", href: "#" }, + { label: "Incentive Program", href: "#" }, + { label: "Research", href: "#" }, + { label: "Institutional Trading", href: "#" }, + { label: "Responsible Trading", href: "#" }, + { label: "Market Integrity", href: "#" }, + ], + }, + ], + copyright: "© 2026 Max Inc.", + disclaimer: + "Trading on Max involves risk and may not be appropriate for all. Members risk losing their cost to enter any transaction, including fees. You should carefully consider whether trading on Max is appropriate for you in light of your investment experience and financial resources. Any trading decisions you make are solely your responsibility and at your own risk. Information is provided for convenience only on an \"AS IS\" basis. Past performance is not necessarily indicative of future results. Max is subject to Canadian regulatory oversight by the CSA.", +}; + +const footerSlice = createSlice({ + name: "footer", + initialState, + reducers: {}, +}); + +export default footerSlice.reducer; diff --git a/frontend/src/store/market.ts b/frontend/src/store/market.ts new file mode 100644 index 0000000..d03067c --- /dev/null +++ b/frontend/src/store/market.ts @@ -0,0 +1,50 @@ +// For MarketCard trending options (WITH images) +export interface TrendingOption { + label: string; + image: string; // image shown in MarketCard + payout: string; + odds: string; + color: string; +} + +// For TopicCard options (WITHOUT images) +export interface TopicOption { + label: string; + payout: string; + odds: string; + color: string; +} + +// Each trending topic shown in MarketCard +export interface TrendingMarket { + id: string; + title: string; + options: TrendingOption[]; // has images + volume: string; + marketCount: string; + newsText: string; +} + +// Each title/market inside a category +export interface MarketTitle { + id: string; + title: string; + date: string; + options: TopicOption[]; // no images + volume: string; + marketCount: string; +} + +// A full category like Politics, Comedy, etc. +export interface MarketCategory { + categoryId: string; + categoryName: string; // "Politics", "Comedy", etc. + categoryIcon: string; // icon identifier or url + markets: MarketTitle[]; // backend sends ALL, frontend shows top 4 +} + +// Full API response shape for home page +export interface HomePageData { + trending: TrendingMarket[]; // variable count, for MarketCard + categories: MarketCategory[]; // all categories with all their markets +} \ No newline at end of file diff --git a/frontend/src/store/searchSlice.ts b/frontend/src/store/searchSlice.ts new file mode 100644 index 0000000..f2eb493 --- /dev/null +++ b/frontend/src/store/searchSlice.ts @@ -0,0 +1,35 @@ +import { createSlice } from "@reduxjs/toolkit"; + +interface SearchState { + isSearchOpen: boolean; + shouldShake: boolean; +} + +const initialState: SearchState = { + isSearchOpen: false, + shouldShake: false, +}; + +const searchSlice = createSlice({ + name: "search", + initialState, + reducers: { + toggleSearch(state) { + state.isSearchOpen = !state.isSearchOpen; + state.shouldShake = true; + }, + closeSearch(state) { + state.isSearchOpen = false; + }, + triggerShake(state) { + state.shouldShake = true; + }, + resetShake(state) { + state.shouldShake = false; + }, + }, +}); + +export const { toggleSearch, closeSearch, triggerShake, resetShake } = + searchSlice.actions; +export default searchSlice.reducer; diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts new file mode 100644 index 0000000..6024900 --- /dev/null +++ b/frontend/src/store/store.ts @@ -0,0 +1,15 @@ +import { configureStore } from "@reduxjs/toolkit"; +import footerReducer from "./footerSlice"; +import categoryReducer from "./categorySlice"; +import searchReducer from "./searchSlice"; + +export const store = configureStore({ + reducer: { + footer: footerReducer, + category: categoryReducer, + search: searchReducer + }, +}); + +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; diff --git a/journals/engine.log b/journals/engine.log deleted file mode 100644 index 8f3b0ad..0000000 --- a/journals/engine.log +++ /dev/null @@ -1,38 +0,0 @@ -ORDER_PLACED: HOUSE_BOT-LAKERS_Y-BUY-1773184182557 -ORDER HOUSE_BOT-LAKERS_Y-BUY-1773184182557 HOUSE_BOT BUY 48 500 1773184182557 -ORDER_PLACED: HOUSE_BOT-LAKERS_Y-SELL-1773184182561 -ORDER HOUSE_BOT-LAKERS_Y-SELL-1773184182561 HOUSE_BOT SELL 52 500 1773184182561 -ORDER_PLACED: HOUSE_BOT-LAKERS_N-BUY-1773184182562 -ORDER HOUSE_BOT-LAKERS_N-BUY-1773184182562 HOUSE_BOT BUY 48 500 1773184182562 -ORDER_PLACED: HOUSE_BOT-LAKERS_N-SELL-1773184182562 -ORDER HOUSE_BOT-LAKERS_N-SELL-1773184182562 HOUSE_BOT SELL 52 500 1773184182562 -ORDER_PLACED: HOUSE_BOT-POSTMAN_Y-BUY-1773186601361 -ORDER HOUSE_BOT-POSTMAN_Y-BUY-1773186601361 HOUSE_BOT BUY 48 500 1773186601361 -ORDER_PLACED: HOUSE_BOT-POSTMAN_Y-SELL-1773186601365 -ORDER HOUSE_BOT-POSTMAN_Y-SELL-1773186601365 HOUSE_BOT SELL 52 500 1773186601365 -ORDER_PLACED: HOUSE_BOT-POSTMAN_N-BUY-1773186601365 -ORDER HOUSE_BOT-POSTMAN_N-BUY-1773186601365 HOUSE_BOT BUY 48 500 1773186601365 -ORDER_PLACED: HOUSE_BOT-POSTMAN_N-SELL-1773186601365 -ORDER HOUSE_BOT-POSTMAN_N-SELL-1773186601365 HOUSE_BOT SELL 52 500 1773186601365 -ORDER_PLACED: 5b2de25a-ae2f-4870-a818-b8f03dd8bc1f -ORDER 5b2de25a-ae2f-4870-a818-b8f03dd8bc1f TRADER_ALICE BUY 52 200 1773186725079 -TRADE 5b2de25a-ae2f-4870-a818-b8f03dd8bc1f HOUSE_BOT-POSTMAN_Y-SELL-1773186601365 52 200 1773186725080 -TRADE_SETTLED: 5b2de25a-ae2f-4870-a818-b8f03dd8bc1f <-> HOUSE_BOT-POSTMAN_Y-SELL-1773186601365 -ORDER_PLACED: HOUSE_BOT-POSTMAN_Y-BUY-1773186725084 -ORDER HOUSE_BOT-POSTMAN_Y-BUY-1773186725084 HOUSE_BOT BUY 48 500 1773186725084 -ORDER_PLACED: HOUSE_BOT-POSTMAN_Y-SELL-1773186725084 -ORDER HOUSE_BOT-POSTMAN_Y-SELL-1773186725084 HOUSE_BOT SELL 52 500 1773186725084 -ORDER_PLACED: HOUSE_BOT-POSTMAN_N-BUY-1773186725084 -ORDER HOUSE_BOT-POSTMAN_N-BUY-1773186725084 HOUSE_BOT BUY 48 500 1773186725084 -ORDER_PLACED: HOUSE_BOT-POSTMAN_N-SELL-1773186725084 -ORDER HOUSE_BOT-POSTMAN_N-SELL-1773186725084 HOUSE_BOT SELL 52 500 1773186725084 -ORDER_PLACED: 5da566f9-37f3-4900-a119-8fdb4eb4125b -ORDER 5da566f9-37f3-4900-a119-8fdb4eb4125b TRADER_BOB BUY 48 150 1773186910682 -ORDER_PLACED: HOUSE_BOT-POSTMAN_Y-BUY-1773187204510 -ORDER HOUSE_BOT-POSTMAN_Y-BUY-1773187204510 HOUSE_BOT BUY 48 500 1773187204510 -ORDER_PLACED: HOUSE_BOT-POSTMAN_Y-SELL-1773187204512 -ORDER HOUSE_BOT-POSTMAN_Y-SELL-1773187204512 HOUSE_BOT SELL 52 500 1773187204512 -ORDER_PLACED: HOUSE_BOT-POSTMAN_N-BUY-1773187204513 -ORDER HOUSE_BOT-POSTMAN_N-BUY-1773187204513 HOUSE_BOT BUY 48 500 1773187204513 -ORDER_PLACED: HOUSE_BOT-POSTMAN_N-SELL-1773187204513 -ORDER HOUSE_BOT-POSTMAN_N-SELL-1773187204513 HOUSE_BOT SELL 52 500 1773187204513 diff --git a/matching-engine/journals/engine.log b/matching-engine/journals/engine.log deleted file mode 100644 index 1dbd384..0000000 --- a/matching-engine/journals/engine.log +++ /dev/null @@ -1,460 +0,0 @@ -ORDER_PLACED: 9fcbe96b-69c9-4ec2-93b1-9d3b308e1125 -ORDER 9fcbe96b-69c9-4ec2-93b1-9d3b308e1125 HOUSE_BOT SELL 39 500 1773111257466 -ORDER_PLACED: 334193e5-4548-4665-8c2b-0707bad3b561 -ORDER 334193e5-4548-4665-8c2b-0707bad3b561 HOUSE_BOT SELL 65 500 1773111257469 -ORDER_PLACED: f857168d-a67f-42c3-914f-ec95793dc06b -ORDER f857168d-a67f-42c3-914f-ec95793dc06b USER123 SELL 45 100 1773111323887 -ORDER_PLACED: e40b87e3-1222-4495-aa7b-766b5f20818c -ORDER e40b87e3-1222-4495-aa7b-766b5f20818c HOUSE_BOT SELL 39 500 1773111478206 -ORDER_PLACED: a5ae4f1c-744a-4a40-8aea-eff9553702bb -ORDER a5ae4f1c-744a-4a40-8aea-eff9553702bb HOUSE_BOT SELL 65 500 1773111478209 -ORDER_PLACED: 330319eb-0019-4a8b-bfc5-69a088962e9c -ORDER 330319eb-0019-4a8b-bfc5-69a088962e9c HOUSE_BOT BUY 35 500 1773111529515 -ORDER_PLACED: 3c08e5d7-4dd8-443e-82de-48b4697ae940 -ORDER 3c08e5d7-4dd8-443e-82de-48b4697ae940 HOUSE_BOT SELL 39 500 1773111529517 -ORDER_PLACED: 96335740-3c15-42cf-b14b-3eb7ccc1aa61 -ORDER 96335740-3c15-42cf-b14b-3eb7ccc1aa61 HOUSE_BOT BUY 61 500 1773111529517 -ORDER_PLACED: 9f035132-ca79-4084-b9c8-bf0e7e9ee066 -ORDER 9f035132-ca79-4084-b9c8-bf0e7e9ee066 HOUSE_BOT SELL 65 500 1773111529518 -ORDER_PLACED: fe49922a-2f1d-4191-99a7-935923ca05ff -ORDER fe49922a-2f1d-4191-99a7-935923ca05ff HOUSE_BOT BUY 35 500 1773111619461 -ORDER_PLACED: 37d97bf0-c039-48df-b5b6-0a33862fb02d -ORDER 37d97bf0-c039-48df-b5b6-0a33862fb02d HOUSE_BOT SELL 39 500 1773111619462 -ORDER_PLACED: 7b25c777-68c7-4981-926a-f87994af2220 -ORDER 7b25c777-68c7-4981-926a-f87994af2220 HOUSE_BOT BUY 61 500 1773111619462 -ORDER_PLACED: dda4ba2d-034c-45b1-9e5b-bd21e0c17254 -ORDER dda4ba2d-034c-45b1-9e5b-bd21e0c17254 HOUSE_BOT SELL 65 500 1773111619462 -ORDER_PLACED: f83ceb7a-6484-4c14-a811-41dccc23c45a -ORDER f83ceb7a-6484-4c14-a811-41dccc23c45a HOUSE_BOT SELL 52 500 1773115355538 -ORDER_PLACED: beed7c8f-5422-4fa5-a2c0-379edc41924d -ORDER beed7c8f-5422-4fa5-a2c0-379edc41924d HOUSE_BOT SELL 52 500 1773115355540 -ORDER_PLACED: 4088cdcc-5d72-4a05-bd7b-aef37940f405 -ORDER 4088cdcc-5d72-4a05-bd7b-aef37940f405 HOUSE_BOT SELL 52 500 1773115422800 -ORDER_PLACED: 052f3f2f-1a59-4bd8-998e-815b1c457976 -ORDER 052f3f2f-1a59-4bd8-998e-815b1c457976 HOUSE_BOT SELL 52 500 1773115422803 -ORDER_PLACED: 19e3ed26-b804-42a3-bcbc-b7d9c1d84912 -ORDER 19e3ed26-b804-42a3-bcbc-b7d9c1d84912 HOUSE_BOT SELL 52 500 1773115432535 -ORDER_PLACED: a1fa2e30-dba1-4ddf-8d8d-28bb3c8785af -ORDER a1fa2e30-dba1-4ddf-8d8d-28bb3c8785af HOUSE_BOT SELL 52 500 1773115432536 -ORDER_PLACED: b7ded81f-58ed-4723-a693-9fb313fb7224 -ORDER b7ded81f-58ed-4723-a693-9fb313fb7224 HOUSE_BOT SELL 52 500 1773115517554 -ORDER_PLACED: 070db175-751a-4a57-953e-862ea50f16d7 -ORDER 070db175-751a-4a57-953e-862ea50f16d7 HOUSE_BOT SELL 52 500 1773115517557 -ORDER_PLACED: f5a62e4e-9b94-4ce5-bc2d-2df3fee64a5d -ORDER f5a62e4e-9b94-4ce5-bc2d-2df3fee64a5d TEST_USER SELL 48 100 1773115543826 -ORDER_PLACED: 50babf85-2f52-44d7-b02b-78653bcfa0c0 -ORDER 50babf85-2f52-44d7-b02b-78653bcfa0c0 HOUSE_BOT SELL 52 500 1773180225213 -ORDER_PLACED: 1c800282-d925-4b30-ab5b-6456dd49f780 -ORDER 1c800282-d925-4b30-ab5b-6456dd49f780 HOUSE_BOT SELL 52 500 1773180225216 -ORDER_PLACED: b49d3f7e-e844-494b-8859-d9217aaf69da -ORDER b49d3f7e-e844-494b-8859-d9217aaf69da TRADER1 SELL 52 100 1773180255539 -ORDER_PLACED: a94818b6-1ad8-4976-b862-664729216038 -ORDER a94818b6-1ad8-4976-b862-664729216038 TRADER1 SELL 48 50 1773180283856 -ORDER_PLACED: 13876ec9-beed-4de5-b979-f7d5f0c2a03b -ORDER 13876ec9-beed-4de5-b979-f7d5f0c2a03b TRADER2 BUY 52 100 1773180298567 -TRADE 13876ec9-beed-4de5-b979-f7d5f0c2a03b a94818b6-1ad8-4976-b862-664729216038 48 50 1773180298567 -TRADE 13876ec9-beed-4de5-b979-f7d5f0c2a03b 50babf85-2f52-44d7-b02b-78653bcfa0c0 52 50 1773180298567 -TRADE_SETTLED: 13876ec9-beed-4de5-b979-f7d5f0c2a03b <-> a94818b6-1ad8-4976-b862-664729216038 -TRADE_SETTLED: 13876ec9-beed-4de5-b979-f7d5f0c2a03b <-> 50babf85-2f52-44d7-b02b-78653bcfa0c0 -ORDER_PLACED: 5e3586db-0b48-4c10-bdcd-daca7d0ce198 -ORDER 5e3586db-0b48-4c10-bdcd-daca7d0ce198 TRADER2 BUY 52 50 1773180328151 -TRADE 5e3586db-0b48-4c10-bdcd-daca7d0ce198 50babf85-2f52-44d7-b02b-78653bcfa0c0 52 50 1773180328152 -TRADE_SETTLED: 5e3586db-0b48-4c10-bdcd-daca7d0ce198 <-> 50babf85-2f52-44d7-b02b-78653bcfa0c0 -ORDER_PLACED: 857255b7-251a-41c4-8c1c-813aa64cce41 -ORDER 857255b7-251a-41c4-8c1c-813aa64cce41 HOUSE_BOT SELL 52 500 1773180526243 -ORDER_PLACED: 93c5368c-c8cb-4b48-9376-c7025e61548e -ORDER 93c5368c-c8cb-4b48-9376-c7025e61548e HOUSE_BOT SELL 52 500 1773180526351 -ORDER_PLACED: 27baed26-7b91-4e97-9a81-cfa552da5385 -ORDER 27baed26-7b91-4e97-9a81-cfa552da5385 TRADER BUY 52 100 1773180554676 -TRADE 27baed26-7b91-4e97-9a81-cfa552da5385 857255b7-251a-41c4-8c1c-813aa64cce41 52 100 1773180554677 -TRADE_SETTLED: 27baed26-7b91-4e97-9a81-cfa552da5385 <-> 857255b7-251a-41c4-8c1c-813aa64cce41 -ORDER_PLACED: HOUSE_BOT-FINAL_Y-SELL-1773180610141 -ORDER HOUSE_BOT-FINAL_Y-SELL-1773180610141 HOUSE_BOT SELL 52 500 1773180610141 -ORDER_PLACED: HOUSE_BOT-FINAL_N-SELL-1773180610249 -ORDER HOUSE_BOT-FINAL_N-SELL-1773180610249 HOUSE_BOT SELL 52 500 1773180610249 -ORDER_PLACED: ffec44af-35e2-42dc-8c26-1b907a3c7c08 -ORDER ffec44af-35e2-42dc-8c26-1b907a3c7c08 TEST_USER BUY 52 50 1773180612508 -TRADE ffec44af-35e2-42dc-8c26-1b907a3c7c08 HOUSE_BOT-FINAL_Y-SELL-1773180610141 52 50 1773180612510 -TRADE_SETTLED: ffec44af-35e2-42dc-8c26-1b907a3c7c08 <-> HOUSE_BOT-FINAL_Y-SELL-1773180610141 -ORDER_PLACED: HOUSE_BOT-FINAL_Y-SELL-1773180612515 -ORDER HOUSE_BOT-FINAL_Y-SELL-1773180612515 HOUSE_BOT SELL 52 500 1773180612515 -ORDER_PLACED: HOUSE_BOT-FINAL_N-SELL-1773180612616 -ORDER HOUSE_BOT-FINAL_N-SELL-1773180612616 HOUSE_BOT SELL 52 500 1773180612616 -ORDER_PLACED: 61b5c74b-0a60-417b-ac6e-949bda9047b6 -ORDER 61b5c74b-0a60-417b-ac6e-949bda9047b6 TEST_USER2 BUY 52 50 1773180644089 -TRADE 61b5c74b-0a60-417b-ac6e-949bda9047b6 HOUSE_BOT-FINAL_Y-SELL-1773180612515 52 50 1773180644091 -TRADE_SETTLED: 61b5c74b-0a60-417b-ac6e-949bda9047b6 <-> HOUSE_BOT-FINAL_Y-SELL-1773180612515 -ORDER_PLACED: HOUSE_BOT-FINAL_Y-SELL-1773180644093 -ORDER HOUSE_BOT-FINAL_Y-SELL-1773180644093 HOUSE_BOT SELL 52 500 1773180644093 -ORDER_PLACED: HOUSE_BOT-FINAL_N-SELL-1773180644199 -ORDER HOUSE_BOT-FINAL_N-SELL-1773180644199 HOUSE_BOT SELL 52 500 1773180644199 -ORDER_PLACED: HOUSE_BOT-LAKERS_Y-SELL-1773180714856 -ORDER HOUSE_BOT-LAKERS_Y-SELL-1773180714856 HOUSE_BOT SELL 52 500 1773180714856 -ORDER_PLACED: HOUSE_BOT-LAKERS_N-SELL-1773180714963 -ORDER HOUSE_BOT-LAKERS_N-SELL-1773180714963 HOUSE_BOT SELL 52 500 1773180714963 -ORDER_PLACED: df2f3319-dd7e-4494-9060-6dd02491f654 -ORDER df2f3319-dd7e-4494-9060-6dd02491f654 TRADER1 BUY 52 100 1773182089056 -ORDER_PLACED: HOUSE_BOT-FIX_Y-SELL-1773182231959 -ORDER HOUSE_BOT-FIX_Y-SELL-1773182231959 HOUSE_BOT SELL 52 500 1773182231959 -ORDER_PLACED: HOUSE_BOT-FIX_N-SELL-1773182232068 -ORDER HOUSE_BOT-FIX_N-SELL-1773182232068 HOUSE_BOT SELL 52 500 1773182232068 -ORDER_PLACED: HOUSE_BOT-SETTLE_Y-SELL-1773182489558 -ORDER HOUSE_BOT-SETTLE_Y-SELL-1773182489558 HOUSE_BOT SELL 52 500 1773182489558 -ORDER_PLACED: HOUSE_BOT-SETTLE_N-SELL-1773182489666 -ORDER HOUSE_BOT-SETTLE_N-SELL-1773182489666 HOUSE_BOT SELL 52 500 1773182489666 -ORDER_PLACED: ae9ef9c4-1eed-4a3a-a0b2-9423d5f349d2 -ORDER ae9ef9c4-1eed-4a3a-a0b2-9423d5f349d2 BLOCK_TEST BUY 52 100 1773182491902 -ORDER_PLACED: HOUSE_BOT-FRESH_Y-SELL-1773182557013 -ORDER HOUSE_BOT-FRESH_Y-SELL-1773182557013 HOUSE_BOT SELL 52 500 1773182557013 -ORDER_PLACED: HOUSE_BOT-FRESH_N-SELL-1773182557125 -ORDER HOUSE_BOT-FRESH_N-SELL-1773182557125 HOUSE_BOT SELL 52 500 1773182557125 -ORDER_PLACED: d0c1f18e-4498-4330-b0c6-07ea30fa05a6 -ORDER d0c1f18e-4498-4330-b0c6-07ea30fa05a6 FRESH_USER BUY 52 100 1773182567228 -ORDER_PLACED: HOUSE_BOT-FINAL_Y-SELL-1773182608916 -ORDER HOUSE_BOT-FINAL_Y-SELL-1773182608916 HOUSE_BOT SELL 52 500 1773182608916 -ORDER_PLACED: HOUSE_BOT-FINAL_N-SELL-1773182609027 -ORDER HOUSE_BOT-FINAL_N-SELL-1773182609027 HOUSE_BOT SELL 52 500 1773182609027 -ORDER_PLACED: 07818452-8d0f-42c5-8c96-bde654e88df9 -ORDER 07818452-8d0f-42c5-8c96-bde654e88df9 BLOCK_USER BUY 52 100 1773182611196 -ORDER_PLACED: HOUSE_BOT-ENUM_Y-SELL-1773182929676 -ORDER HOUSE_BOT-ENUM_Y-SELL-1773182929676 HOUSE_BOT SELL 52 500 1773182929676 -ORDER_PLACED: HOUSE_BOT-ENUM_N-SELL-1773182929781 -ORDER HOUSE_BOT-ENUM_N-SELL-1773182929781 HOUSE_BOT SELL 52 500 1773182929781 -ORDER_PLACED: HOUSE_BOT-E2E_Y-SELL-1773183005330 -ORDER HOUSE_BOT-E2E_Y-SELL-1773183005330 HOUSE_BOT SELL 52 500 1773183005330 -ORDER_PLACED: HOUSE_BOT-E2E_N-SELL-1773183005332 -ORDER HOUSE_BOT-E2E_N-SELL-1773183005332 HOUSE_BOT SELL 52 500 1773183005332 -ORDER_PLACED: HOUSE_BOT-E2E_Y-SELL-1773183131060 -ORDER HOUSE_BOT-E2E_Y-SELL-1773183131060 HOUSE_BOT SELL 52 500 1773183131060 -ORDER_PLACED: HOUSE_BOT-E2E_N-SELL-1773183131061 -ORDER HOUSE_BOT-E2E_N-SELL-1773183131061 HOUSE_BOT SELL 52 500 1773183131061 -ORDER_PLACED: 15edb532-ebe5-4587-82b1-22a2927369f2 -ORDER 15edb532-ebe5-4587-82b1-22a2927369f2 E2E_TRADER BUY 52 100 1773183166506 -TRADE 15edb532-ebe5-4587-82b1-22a2927369f2 HOUSE_BOT-E2E_Y-SELL-1773183131060 52 100 1773183166507 -TRADE_SETTLED: 15edb532-ebe5-4587-82b1-22a2927369f2 <-> HOUSE_BOT-E2E_Y-SELL-1773183131060 -ORDER_PLACED: HOUSE_BOT-E2E_Y-SELL-1773183166514 -ORDER HOUSE_BOT-E2E_Y-SELL-1773183166514 HOUSE_BOT SELL 52 500 1773183166514 -ORDER_PLACED: HOUSE_BOT-E2E_N-SELL-1773183166514 -ORDER HOUSE_BOT-E2E_N-SELL-1773183166514 HOUSE_BOT SELL 52 500 1773183166514 -ORDER_PLACED: HOUSE_BOT-DEBUG_Y-SELL-1773183287280 -ORDER HOUSE_BOT-DEBUG_Y-SELL-1773183287280 HOUSE_BOT SELL 52 500 1773183287280 -ORDER_PLACED: HOUSE_BOT-DEBUG_N-SELL-1773183287284 -ORDER HOUSE_BOT-DEBUG_N-SELL-1773183287284 HOUSE_BOT SELL 52 500 1773183287284 -ORDER_PLACED: HOUSE_BOT-TRACE_Y-SELL-1773183356717 -ORDER HOUSE_BOT-TRACE_Y-SELL-1773183356717 HOUSE_BOT SELL 52 500 1773183356717 -ORDER_PLACED: HOUSE_BOT-TRACE_N-SELL-1773183356722 -ORDER HOUSE_BOT-TRACE_N-SELL-1773183356722 HOUSE_BOT SELL 52 500 1773183356722 -ORDER_PLACED: HOUSE_BOT-CASH_Y-BUY-1773183416028 -ORDER HOUSE_BOT-CASH_Y-BUY-1773183416028 HOUSE_BOT BUY 48 500 1773183416028 -ORDER_PLACED: HOUSE_BOT-CASH_Y-SELL-1773183416033 -ORDER HOUSE_BOT-CASH_Y-SELL-1773183416033 HOUSE_BOT SELL 52 500 1773183416033 -ORDER_PLACED: HOUSE_BOT-CASH_N-BUY-1773183416034 -ORDER HOUSE_BOT-CASH_N-BUY-1773183416034 HOUSE_BOT BUY 48 500 1773183416034 -ORDER_PLACED: HOUSE_BOT-CASH_N-SELL-1773183416034 -ORDER HOUSE_BOT-CASH_N-SELL-1773183416034 HOUSE_BOT SELL 52 500 1773183416034 -ORDER_PLACED: HOUSE_BOT-CASH_Y-BUY-1773183490652 -ORDER HOUSE_BOT-CASH_Y-BUY-1773183490652 HOUSE_BOT BUY 48 500 1773183490652 -ORDER_PLACED: HOUSE_BOT-CASH_Y-SELL-1773183490653 -ORDER HOUSE_BOT-CASH_Y-SELL-1773183490653 HOUSE_BOT SELL 52 500 1773183490653 -ORDER_PLACED: HOUSE_BOT-CASH_N-BUY-1773183490653 -ORDER HOUSE_BOT-CASH_N-BUY-1773183490653 HOUSE_BOT BUY 48 500 1773183490653 -ORDER_PLACED: HOUSE_BOT-CASH_N-SELL-1773183490653 -ORDER HOUSE_BOT-CASH_N-SELL-1773183490653 HOUSE_BOT SELL 52 500 1773183490653 -ORDER_PLACED: a08fd557-f51b-47c3-9dce-72cd6878db5d -ORDER a08fd557-f51b-47c3-9dce-72cd6878db5d TRADER_TEST BUY 52 100 1773183525895 -TRADE a08fd557-f51b-47c3-9dce-72cd6878db5d HOUSE_BOT-CASH_Y-SELL-1773183490653 52 100 1773183525897 -TRADE_SETTLED: a08fd557-f51b-47c3-9dce-72cd6878db5d <-> HOUSE_BOT-CASH_Y-SELL-1773183490653 -ORDER_PLACED: HOUSE_BOT-CASH_Y-BUY-1773183525900 -ORDER HOUSE_BOT-CASH_Y-BUY-1773183525900 HOUSE_BOT BUY 48 500 1773183525900 -ORDER_PLACED: HOUSE_BOT-CASH_Y-SELL-1773183525901 -ORDER HOUSE_BOT-CASH_Y-SELL-1773183525901 HOUSE_BOT SELL 52 500 1773183525901 -ORDER_PLACED: HOUSE_BOT-CASH_N-BUY-1773183525901 -ORDER HOUSE_BOT-CASH_N-BUY-1773183525901 HOUSE_BOT BUY 48 500 1773183525901 -ORDER_PLACED: HOUSE_BOT-CASH_N-SELL-1773183525902 -ORDER HOUSE_BOT-CASH_N-SELL-1773183525902 HOUSE_BOT SELL 52 500 1773183525902 -ORDER_PLACED: HOUSE_BOT-PROD_Y-BUY-1773183593439 -ORDER HOUSE_BOT-PROD_Y-BUY-1773183593439 HOUSE_BOT BUY 48 500 1773183593440 -ORDER_PLACED: HOUSE_BOT-PROD_Y-SELL-1773183593442 -ORDER HOUSE_BOT-PROD_Y-SELL-1773183593442 HOUSE_BOT SELL 52 500 1773183593442 -ORDER_PLACED: HOUSE_BOT-PROD_N-BUY-1773183593443 -ORDER HOUSE_BOT-PROD_N-BUY-1773183593443 HOUSE_BOT BUY 48 500 1773183593443 -ORDER_PLACED: HOUSE_BOT-PROD_N-SELL-1773183593443 -ORDER HOUSE_BOT-PROD_N-SELL-1773183593443 HOUSE_BOT SELL 52 500 1773183593443 -ORDER_PLACED: 6d3c662b-89c8-484d-98be-bd5d5e39b76a -ORDER 6d3c662b-89c8-484d-98be-bd5d5e39b76a PROD_TRADER BUY 52 50 1773183601183 -TRADE 6d3c662b-89c8-484d-98be-bd5d5e39b76a HOUSE_BOT-PROD_Y-SELL-1773183593442 52 50 1773183601184 -TRADE_SETTLED: 6d3c662b-89c8-484d-98be-bd5d5e39b76a <-> HOUSE_BOT-PROD_Y-SELL-1773183593442 -ORDER_PLACED: HOUSE_BOT-PROD_Y-BUY-1773183601186 -ORDER HOUSE_BOT-PROD_Y-BUY-1773183601186 HOUSE_BOT BUY 48 500 1773183601186 -ORDER_PLACED: HOUSE_BOT-PROD_Y-SELL-1773183601186 -ORDER HOUSE_BOT-PROD_Y-SELL-1773183601186 HOUSE_BOT SELL 52 500 1773183601186 -ORDER_PLACED: HOUSE_BOT-PROD_N-BUY-1773183601186 -ORDER HOUSE_BOT-PROD_N-BUY-1773183601186 HOUSE_BOT BUY 48 500 1773183601186 -ORDER_PLACED: HOUSE_BOT-PROD_N-SELL-1773183601186 -ORDER HOUSE_BOT-PROD_N-SELL-1773183601186 HOUSE_BOT SELL 52 500 1773183601186 -ORDER_PLACED: HOUSE_BOT-KALSHI_Y-BUY-1773186148086 -ORDER HOUSE_BOT-KALSHI_Y-BUY-1773186148086 HOUSE_BOT BUY 48 500 1773186148086 -ORDER_PLACED: HOUSE_BOT-KALSHI_Y-SELL-1773186148094 -ORDER HOUSE_BOT-KALSHI_Y-SELL-1773186148094 HOUSE_BOT SELL 52 500 1773186148094 -ORDER_PLACED: HOUSE_BOT-KALSHI_N-BUY-1773186148095 -ORDER HOUSE_BOT-KALSHI_N-BUY-1773186148095 HOUSE_BOT BUY 48 500 1773186148095 -ORDER_PLACED: HOUSE_BOT-KALSHI_N-SELL-1773186148095 -ORDER HOUSE_BOT-KALSHI_N-SELL-1773186148095 HOUSE_BOT SELL 52 500 1773186148095 -ORDER_PLACED: 244dc1b4-7f6c-4c2f-b0d7-34e313624c6e -ORDER 244dc1b4-7f6c-4c2f-b0d7-34e313624c6e KALSHI_TRADER BUY 52 100 1773186172195 -TRADE 244dc1b4-7f6c-4c2f-b0d7-34e313624c6e HOUSE_BOT-KALSHI_Y-SELL-1773186148094 52 100 1773186172195 -TRADE_SETTLED: 244dc1b4-7f6c-4c2f-b0d7-34e313624c6e <-> HOUSE_BOT-KALSHI_Y-SELL-1773186148094 -ORDER_PLACED: HOUSE_BOT-KALSHI_Y-BUY-1773186172198 -ORDER HOUSE_BOT-KALSHI_Y-BUY-1773186172198 HOUSE_BOT BUY 48 500 1773186172198 -ORDER_PLACED: HOUSE_BOT-KALSHI_Y-SELL-1773186172198 -ORDER HOUSE_BOT-KALSHI_Y-SELL-1773186172198 HOUSE_BOT SELL 52 500 1773186172198 -ORDER_PLACED: HOUSE_BOT-KALSHI_N-BUY-1773186172198 -ORDER HOUSE_BOT-KALSHI_N-BUY-1773186172198 HOUSE_BOT BUY 48 500 1773186172198 -ORDER_PLACED: HOUSE_BOT-KALSHI_N-SELL-1773186172198 -ORDER HOUSE_BOT-KALSHI_N-SELL-1773186172198 HOUSE_BOT SELL 52 500 1773186172198 -ORDER_PLACED: HOUSE_BOT-SETTLE_Y-BUY-1773186369960 -ORDER HOUSE_BOT-SETTLE_Y-BUY-1773186369960 HOUSE_BOT BUY 48 500 1773186369960 -ORDER_PLACED: HOUSE_BOT-SETTLE_Y-SELL-1773186369964 -ORDER HOUSE_BOT-SETTLE_Y-SELL-1773186369964 HOUSE_BOT SELL 52 500 1773186369964 -ORDER_PLACED: HOUSE_BOT-SETTLE_N-BUY-1773186369964 -ORDER HOUSE_BOT-SETTLE_N-BUY-1773186369964 HOUSE_BOT BUY 48 500 1773186369964 -ORDER_PLACED: HOUSE_BOT-SETTLE_N-SELL-1773186369964 -ORDER HOUSE_BOT-SETTLE_N-SELL-1773186369964 HOUSE_BOT SELL 52 500 1773186369964 -ORDER_PLACED: HOUSE_BOT-KALSHI_YES-BUY-1773186383923 -ORDER HOUSE_BOT-KALSHI_YES-BUY-1773186383923 HOUSE_BOT BUY 48 500 1773186383923 -ORDER_PLACED: HOUSE_BOT-KALSHI_YES-SELL-1773186383924 -ORDER HOUSE_BOT-KALSHI_YES-SELL-1773186383924 HOUSE_BOT SELL 52 500 1773186383924 -ORDER_PLACED: HOUSE_BOT-KALSHI_NO-BUY-1773186383925 -ORDER HOUSE_BOT-KALSHI_NO-BUY-1773186383925 HOUSE_BOT BUY 48 500 1773186383925 -ORDER_PLACED: HOUSE_BOT-KALSHI_NO-SELL-1773186383925 -ORDER HOUSE_BOT-KALSHI_NO-SELL-1773186383925 HOUSE_BOT SELL 52 500 1773186383925 -ORDER_PLACED: 68649056-e19a-40dc-b643-e339a6bd60ec -ORDER 68649056-e19a-40dc-b643-e339a6bd60ec KALSHI_USER BUY 52 100 1773186389481 -TRADE 68649056-e19a-40dc-b643-e339a6bd60ec HOUSE_BOT-KALSHI_YES-SELL-1773186383924 52 100 1773186389482 -TRADE_SETTLED: 68649056-e19a-40dc-b643-e339a6bd60ec <-> HOUSE_BOT-KALSHI_YES-SELL-1773186383924 -ORDER_PLACED: HOUSE_BOT-KALSHI_YES-BUY-1773186389484 -ORDER HOUSE_BOT-KALSHI_YES-BUY-1773186389484 HOUSE_BOT BUY 48 500 1773186389484 -ORDER_PLACED: HOUSE_BOT-KALSHI_YES-SELL-1773186389484 -ORDER HOUSE_BOT-KALSHI_YES-SELL-1773186389484 HOUSE_BOT SELL 52 500 1773186389484 -ORDER_PLACED: HOUSE_BOT-KALSHI_NO-BUY-1773186389484 -ORDER HOUSE_BOT-KALSHI_NO-BUY-1773186389484 HOUSE_BOT BUY 48 500 1773186389484 -ORDER_PLACED: HOUSE_BOT-KALSHI_NO-SELL-1773186389484 -ORDER HOUSE_BOT-KALSHI_NO-SELL-1773186389484 HOUSE_BOT SELL 52 500 1773186389484 -ORDER_PLACED: HOUSE_BOT-DEBUG_Y-BUY-1773196703281 -ORDER HOUSE_BOT-DEBUG_Y-BUY-1773196703281 HOUSE_BOT BUY 48 500 1773196703281 -ORDER_PLACED: HOUSE_BOT-DEBUG_Y-SELL-1773196703287 -ORDER HOUSE_BOT-DEBUG_Y-SELL-1773196703287 HOUSE_BOT SELL 52 500 1773196703287 -ORDER_PLACED: HOUSE_BOT-DEBUG_N-BUY-1773196703287 -ORDER HOUSE_BOT-DEBUG_N-BUY-1773196703287 HOUSE_BOT BUY 48 500 1773196703287 -ORDER_PLACED: HOUSE_BOT-DEBUG_N-SELL-1773196703288 -ORDER HOUSE_BOT-DEBUG_N-SELL-1773196703288 HOUSE_BOT SELL 52 500 1773196703288 -ORDER_PLACED: 0797e709-d3d0-4b79-ac88-a5f6bfa736d9 -ORDER 0797e709-d3d0-4b79-ac88-a5f6bfa736d9 DEBUG_USER BUY 52 100 1773196727276 -TRADE 0797e709-d3d0-4b79-ac88-a5f6bfa736d9 HOUSE_BOT-DEBUG_Y-SELL-1773196703287 52 100 1773196727277 -TRADE_SETTLED: 0797e709-d3d0-4b79-ac88-a5f6bfa736d9 <-> HOUSE_BOT-DEBUG_Y-SELL-1773196703287 -ORDER_PLACED: HOUSE_BOT-DEBUG_Y-BUY-1773196727280 -ORDER HOUSE_BOT-DEBUG_Y-BUY-1773196727280 HOUSE_BOT BUY 48 500 1773196727280 -ORDER_PLACED: HOUSE_BOT-DEBUG_Y-SELL-1773196727280 -ORDER HOUSE_BOT-DEBUG_Y-SELL-1773196727280 HOUSE_BOT SELL 52 500 1773196727280 -ORDER_PLACED: HOUSE_BOT-DEBUG_N-BUY-1773196727281 -ORDER HOUSE_BOT-DEBUG_N-BUY-1773196727281 HOUSE_BOT BUY 48 500 1773196727281 -ORDER_PLACED: HOUSE_BOT-DEBUG_N-SELL-1773196727281 -ORDER HOUSE_BOT-DEBUG_N-SELL-1773196727281 HOUSE_BOT SELL 52 500 1773196727281 -ORDER_PLACED: HOUSE_BOT-FINAL_Y-BUY-1773196818389 -ORDER HOUSE_BOT-FINAL_Y-BUY-1773196818389 HOUSE_BOT BUY 48 500 1773196818389 -ORDER_PLACED: HOUSE_BOT-FINAL_Y-SELL-1773196818396 -ORDER HOUSE_BOT-FINAL_Y-SELL-1773196818396 HOUSE_BOT SELL 52 500 1773196818396 -ORDER_PLACED: HOUSE_BOT-FINAL_N-BUY-1773196818396 -ORDER HOUSE_BOT-FINAL_N-BUY-1773196818396 HOUSE_BOT BUY 48 500 1773196818396 -ORDER_PLACED: HOUSE_BOT-FINAL_N-SELL-1773196818396 -ORDER HOUSE_BOT-FINAL_N-SELL-1773196818396 HOUSE_BOT SELL 52 500 1773196818396 -ORDER_PLACED: cb03ae4c-a815-4ee1-8f03-058686afc966 -ORDER cb03ae4c-a815-4ee1-8f03-058686afc966 FINAL_USER BUY 52 100 1773196824595 -TRADE cb03ae4c-a815-4ee1-8f03-058686afc966 HOUSE_BOT-FINAL_Y-SELL-1773196818396 52 100 1773196824596 -TRADE_SETTLED: cb03ae4c-a815-4ee1-8f03-058686afc966 <-> HOUSE_BOT-FINAL_Y-SELL-1773196818396 -ORDER_PLACED: HOUSE_BOT-FINAL_Y-BUY-1773196824598 -ORDER HOUSE_BOT-FINAL_Y-BUY-1773196824598 HOUSE_BOT BUY 48 500 1773196824598 -ORDER_PLACED: HOUSE_BOT-FINAL_Y-SELL-1773196824600 -ORDER HOUSE_BOT-FINAL_Y-SELL-1773196824600 HOUSE_BOT SELL 52 500 1773196824600 -ORDER_PLACED: HOUSE_BOT-FINAL_N-BUY-1773196824600 -ORDER HOUSE_BOT-FINAL_N-BUY-1773196824600 HOUSE_BOT BUY 48 500 1773196824600 -ORDER_PLACED: HOUSE_BOT-FINAL_N-SELL-1773196824600 -ORDER HOUSE_BOT-FINAL_N-SELL-1773196824600 HOUSE_BOT SELL 52 500 1773196824600 -ORDER_PLACED: HOUSE_BOT-SEQ_Y-BUY-1773197042881 -ORDER HOUSE_BOT-SEQ_Y-BUY-1773197042881 HOUSE_BOT BUY 48 500 1773197042881 -ORDER_PLACED: HOUSE_BOT-SEQ_Y-SELL-1773197042883 -ORDER HOUSE_BOT-SEQ_Y-SELL-1773197042883 HOUSE_BOT SELL 52 500 1773197042883 -ORDER_PLACED: HOUSE_BOT-SEQ_N-BUY-1773197042883 -ORDER HOUSE_BOT-SEQ_N-BUY-1773197042883 HOUSE_BOT BUY 48 500 1773197042883 -ORDER_PLACED: HOUSE_BOT-SEQ_N-SELL-1773197042883 -ORDER HOUSE_BOT-SEQ_N-SELL-1773197042883 HOUSE_BOT SELL 52 500 1773197042883 -ORDER_PLACED: f6bf9d4a-34ea-4ffe-94a7-0fc764f4772e -ORDER f6bf9d4a-34ea-4ffe-94a7-0fc764f4772e TRADER_1 BUY 52 200 1773197152241 -TRADE f6bf9d4a-34ea-4ffe-94a7-0fc764f4772e HOUSE_BOT-SEQ_Y-SELL-1773197042883 52 200 1773197152246 -TRADE_SETTLED: f6bf9d4a-34ea-4ffe-94a7-0fc764f4772e <-> HOUSE_BOT-SEQ_Y-SELL-1773197042883 -ORDER_PLACED: HOUSE_BOT-SEQ_Y-BUY-1773197152250 -ORDER HOUSE_BOT-SEQ_Y-BUY-1773197152250 HOUSE_BOT BUY 48 500 1773197152250 -ORDER_PLACED: HOUSE_BOT-SEQ_Y-SELL-1773197152250 -ORDER HOUSE_BOT-SEQ_Y-SELL-1773197152250 HOUSE_BOT SELL 52 500 1773197152250 -ORDER_PLACED: HOUSE_BOT-SEQ_N-BUY-1773197152250 -ORDER HOUSE_BOT-SEQ_N-BUY-1773197152250 HOUSE_BOT BUY 48 500 1773197152250 -ORDER_PLACED: HOUSE_BOT-SEQ_N-SELL-1773197152251 -ORDER HOUSE_BOT-SEQ_N-SELL-1773197152251 HOUSE_BOT SELL 52 500 1773197152251 -ORDER_PLACED: dc79abc1-6558-4ab2-9203-a639256b8b6f -ORDER dc79abc1-6558-4ab2-9203-a639256b8b6f TRADER_2 BUY 48 150 1773197259219 -ORDER_PLACED: e30e3d7f-3670-4045-8eb2-52e90780735b -ORDER e30e3d7f-3670-4045-8eb2-52e90780735b TRADER_1 BUY 52 100 1773197377539 -TRADE e30e3d7f-3670-4045-8eb2-52e90780735b HOUSE_BOT-SEQ_Y-SELL-1773197152250 52 100 1773197377540 -TRADE_SETTLED: e30e3d7f-3670-4045-8eb2-52e90780735b <-> HOUSE_BOT-SEQ_Y-SELL-1773197152250 -ORDER_PLACED: HOUSE_BOT-ANA_Y-BUY-1773197833978 -ORDER HOUSE_BOT-ANA_Y-BUY-1773197833978 HOUSE_BOT BUY 48 500 1773197833978 -ORDER_PLACED: HOUSE_BOT-ANA_Y-SELL-1773197833981 -ORDER HOUSE_BOT-ANA_Y-SELL-1773197833981 HOUSE_BOT SELL 52 500 1773197833981 -ORDER_PLACED: HOUSE_BOT-ANA_N-BUY-1773197833981 -ORDER HOUSE_BOT-ANA_N-BUY-1773197833981 HOUSE_BOT BUY 48 500 1773197833981 -ORDER_PLACED: HOUSE_BOT-ANA_N-SELL-1773197833981 -ORDER HOUSE_BOT-ANA_N-SELL-1773197833981 HOUSE_BOT SELL 52 500 1773197833981 -ORDER_PLACED: f29e1458-f71b-4f0e-b6bc-9759e1e1e8c9 -ORDER f29e1458-f71b-4f0e-b6bc-9759e1e1e8c9 TRADER_ANALYSIS_1 BUY 52 200 1773197852165 -TRADE f29e1458-f71b-4f0e-b6bc-9759e1e1e8c9 HOUSE_BOT-ANA_Y-SELL-1773197833981 52 200 1773197852167 -TRADE_SETTLED: f29e1458-f71b-4f0e-b6bc-9759e1e1e8c9 <-> HOUSE_BOT-ANA_Y-SELL-1773197833981 -ORDER_PLACED: HOUSE_BOT-ANA_Y-BUY-1773197852169 -ORDER HOUSE_BOT-ANA_Y-BUY-1773197852169 HOUSE_BOT BUY 48 500 1773197852169 -ORDER_PLACED: HOUSE_BOT-ANA_Y-SELL-1773197852169 -ORDER HOUSE_BOT-ANA_Y-SELL-1773197852169 HOUSE_BOT SELL 52 500 1773197852169 -ORDER_PLACED: HOUSE_BOT-ANA_N-BUY-1773197852169 -ORDER HOUSE_BOT-ANA_N-BUY-1773197852169 HOUSE_BOT BUY 48 500 1773197852169 -ORDER_PLACED: HOUSE_BOT-ANA_N-SELL-1773197852169 -ORDER HOUSE_BOT-ANA_N-SELL-1773197852169 HOUSE_BOT SELL 52 500 1773197852169 -ORDER_PLACED: 4fbee2ad-8f7b-4ca9-8ce5-ab1da70386c7 -ORDER 4fbee2ad-8f7b-4ca9-8ce5-ab1da70386c7 TRADER_ANALYSIS_2 BUY 48 150 1773197867077 -ORDER_PLACED: f3026e2a-e187-4167-983c-ae86fd5d08b8 -ORDER f3026e2a-e187-4167-983c-ae86fd5d08b8 TRADER_ANALYSIS_2 BUY 52 150 1773197914709 -TRADE f3026e2a-e187-4167-983c-ae86fd5d08b8 HOUSE_BOT-ANA_N-SELL-1773197852169 52 150 1773197914711 -TRADE_SETTLED: f3026e2a-e187-4167-983c-ae86fd5d08b8 <-> HOUSE_BOT-ANA_N-SELL-1773197852169 -ORDER_PLACED: HOUSE_BOT-FIX_Y-BUY-1773198020183 -ORDER HOUSE_BOT-FIX_Y-BUY-1773198020183 HOUSE_BOT BUY 48 500 1773198020183 -ORDER_PLACED: HOUSE_BOT-FIX_Y-SELL-1773198020186 -ORDER HOUSE_BOT-FIX_Y-SELL-1773198020186 HOUSE_BOT SELL 52 500 1773198020186 -ORDER_PLACED: HOUSE_BOT-FIX_N-BUY-1773198020186 -ORDER HOUSE_BOT-FIX_N-BUY-1773198020186 HOUSE_BOT BUY 48 500 1773198020186 -ORDER_PLACED: HOUSE_BOT-FIX_N-SELL-1773198020186 -ORDER HOUSE_BOT-FIX_N-SELL-1773198020186 HOUSE_BOT SELL 52 500 1773198020186 -ORDER_PLACED: f8677342-1636-4461-adb3-dc706eeec950 -ORDER f8677342-1636-4461-adb3-dc706eeec950 TRADER_FIX_1 BUY 52 200 1773198028364 -TRADE f8677342-1636-4461-adb3-dc706eeec950 HOUSE_BOT-FIX_Y-SELL-1773198020186 52 200 1773198028365 -TRADE_SETTLED: f8677342-1636-4461-adb3-dc706eeec950 <-> HOUSE_BOT-FIX_Y-SELL-1773198020186 -ORDER_PLACED: HOUSE_BOT-FIX_Y-BUY-1773198028367 -ORDER HOUSE_BOT-FIX_Y-BUY-1773198028367 HOUSE_BOT BUY 48 500 1773198028367 -ORDER_PLACED: HOUSE_BOT-FIX_Y-SELL-1773198028367 -ORDER HOUSE_BOT-FIX_Y-SELL-1773198028367 HOUSE_BOT SELL 52 500 1773198028367 -ORDER_PLACED: HOUSE_BOT-FIX_N-BUY-1773198028367 -ORDER HOUSE_BOT-FIX_N-BUY-1773198028367 HOUSE_BOT BUY 48 500 1773198028367 -ORDER_PLACED: HOUSE_BOT-FIX_N-SELL-1773198028367 -ORDER HOUSE_BOT-FIX_N-SELL-1773198028367 HOUSE_BOT SELL 52 500 1773198028367 -ORDER_PLACED: 707308a4-f067-4714-9362-c8d710a9bdd0 -ORDER 707308a4-f067-4714-9362-c8d710a9bdd0 TRADER_FIX_2 BUY 52 150 1773198028381 -TRADE 707308a4-f067-4714-9362-c8d710a9bdd0 HOUSE_BOT-FIX_N-SELL-1773198028367 52 150 1773198028382 -TRADE_SETTLED: 707308a4-f067-4714-9362-c8d710a9bdd0 <-> HOUSE_BOT-FIX_N-SELL-1773198028367 -ORDER_PLACED: HOUSE_BOT-FIX_Y-BUY-1773198028382 -ORDER HOUSE_BOT-FIX_Y-BUY-1773198028382 HOUSE_BOT BUY 48 500 1773198028382 -ORDER_PLACED: HOUSE_BOT-FIX_Y-SELL-1773198028383 -ORDER HOUSE_BOT-FIX_Y-SELL-1773198028383 HOUSE_BOT SELL 52 500 1773198028383 -ORDER_PLACED: HOUSE_BOT-FIX_N-BUY-1773198028383 -ORDER HOUSE_BOT-FIX_N-BUY-1773198028383 HOUSE_BOT BUY 48 500 1773198028383 -ORDER_PLACED: HOUSE_BOT-FIX_N-SELL-1773198028383 -ORDER HOUSE_BOT-FIX_N-SELL-1773198028383 HOUSE_BOT SELL 52 500 1773198028383 -ORDER_PLACED: HOUSE_BOT-PROD_Y-BUY-1773198230862 -ORDER HOUSE_BOT-PROD_Y-BUY-1773198230862 HOUSE_BOT BUY 48 500 1773198230862 -ORDER_PLACED: HOUSE_BOT-PROD_Y-SELL-1773198230867 -ORDER HOUSE_BOT-PROD_Y-SELL-1773198230867 HOUSE_BOT SELL 52 500 1773198230867 -ORDER_PLACED: HOUSE_BOT-PROD_N-BUY-1773198230867 -ORDER HOUSE_BOT-PROD_N-BUY-1773198230867 HOUSE_BOT BUY 48 500 1773198230867 -ORDER_PLACED: HOUSE_BOT-PROD_N-SELL-1773198230868 -ORDER HOUSE_BOT-PROD_N-SELL-1773198230868 HOUSE_BOT SELL 52 500 1773198230868 -ORDER_PLACED: e64d3874-e13d-4baa-863d-a6a0a16d24c1 -ORDER e64d3874-e13d-4baa-863d-a6a0a16d24c1 PROD_TRADER_1 BUY 52 300 1773198322127 -TRADE e64d3874-e13d-4baa-863d-a6a0a16d24c1 HOUSE_BOT-PROD_Y-SELL-1773198230867 52 300 1773198322128 -TRADE_SETTLED: e64d3874-e13d-4baa-863d-a6a0a16d24c1 <-> HOUSE_BOT-PROD_Y-SELL-1773198230867 -ORDER_PLACED: HOUSE_BOT-PROD_Y-BUY-1773198322130 -ORDER HOUSE_BOT-PROD_Y-BUY-1773198322130 HOUSE_BOT BUY 48 500 1773198322130 -ORDER_PLACED: HOUSE_BOT-PROD_Y-SELL-1773198322130 -ORDER HOUSE_BOT-PROD_Y-SELL-1773198322130 HOUSE_BOT SELL 52 500 1773198322130 -ORDER_PLACED: HOUSE_BOT-PROD_N-BUY-1773198322130 -ORDER HOUSE_BOT-PROD_N-BUY-1773198322130 HOUSE_BOT BUY 48 500 1773198322130 -ORDER_PLACED: HOUSE_BOT-PROD_N-SELL-1773198322130 -ORDER HOUSE_BOT-PROD_N-SELL-1773198322130 HOUSE_BOT SELL 52 500 1773198322130 -ORDER_PLACED: 18f747fb-7173-4f44-956b-92a9140b7581 -ORDER 18f747fb-7173-4f44-956b-92a9140b7581 PROD_TRADER_2 BUY 52 200 1773198417372 -TRADE 18f747fb-7173-4f44-956b-92a9140b7581 HOUSE_BOT-PROD_N-SELL-1773198322130 52 200 1773198417373 -TRADE_SETTLED: 18f747fb-7173-4f44-956b-92a9140b7581 <-> HOUSE_BOT-PROD_N-SELL-1773198322130 -ORDER_PLACED: HOUSE_BOT-PROD_Y-BUY-1773198417376 -ORDER HOUSE_BOT-PROD_Y-BUY-1773198417376 HOUSE_BOT BUY 48 500 1773198417376 -ORDER_PLACED: HOUSE_BOT-PROD_Y-SELL-1773198417376 -ORDER HOUSE_BOT-PROD_Y-SELL-1773198417376 HOUSE_BOT SELL 52 500 1773198417376 -ORDER_PLACED: HOUSE_BOT-PROD_N-BUY-1773198417376 -ORDER HOUSE_BOT-PROD_N-BUY-1773198417376 HOUSE_BOT BUY 48 500 1773198417376 -ORDER_PLACED: HOUSE_BOT-PROD_N-SELL-1773198417376 -ORDER HOUSE_BOT-PROD_N-SELL-1773198417376 HOUSE_BOT SELL 52 500 1773198417376 -ORDER_PLACED: 51ab4eef-3879-4bad-a89d-49b32f218615 -ORDER 51ab4eef-3879-4bad-a89d-49b32f218615 PROD_TRADER_1 BUY 52 150 1773198496269 -TRADE 51ab4eef-3879-4bad-a89d-49b32f218615 HOUSE_BOT-PROD_Y-SELL-1773198417376 52 150 1773198496271 -TRADE_SETTLED: 51ab4eef-3879-4bad-a89d-49b32f218615 <-> HOUSE_BOT-PROD_Y-SELL-1773198417376 -ORDER_PLACED: HOUSE_BOT-PROD_Y-BUY-1773198496275 -ORDER HOUSE_BOT-PROD_Y-BUY-1773198496275 HOUSE_BOT BUY 48 500 1773198496275 -ORDER_PLACED: HOUSE_BOT-PROD_Y-SELL-1773198496275 -ORDER HOUSE_BOT-PROD_Y-SELL-1773198496275 HOUSE_BOT SELL 52 500 1773198496275 -ORDER_PLACED: HOUSE_BOT-PROD_N-BUY-1773198496275 -ORDER HOUSE_BOT-PROD_N-BUY-1773198496275 HOUSE_BOT BUY 48 500 1773198496275 -ORDER_PLACED: HOUSE_BOT-PROD_N-SELL-1773198496275 -ORDER HOUSE_BOT-PROD_N-SELL-1773198496275 HOUSE_BOT SELL 52 500 1773198496275 -ORDER_PLACED: HOUSE_BOT-HIGH_Y-BUY-1773271518730 -ORDER HOUSE_BOT-HIGH_Y-BUY-1773271518730 HOUSE_BOT BUY 49 500 1773271518730 -ORDER_PLACED: HOUSE_BOT-HIGH_Y-SELL-1773271518733 -ORDER HOUSE_BOT-HIGH_Y-SELL-1773271518733 HOUSE_BOT SELL 51 500 1773271518733 -ORDER_PLACED: HOUSE_BOT-HIGH_N-BUY-1773271518734 -ORDER HOUSE_BOT-HIGH_N-BUY-1773271518734 HOUSE_BOT BUY 49 500 1773271518734 -ORDER_PLACED: HOUSE_BOT-HIGH_N-SELL-1773271518734 -ORDER HOUSE_BOT-HIGH_N-SELL-1773271518734 HOUSE_BOT SELL 51 500 1773271518734 -ORDER_PLACED: HOUSE_BOT-LOW_Y-BUY-1773271518765 -ORDER HOUSE_BOT-LOW_Y-BUY-1773271518765 HOUSE_BOT BUY 49 500 1773271518765 -ORDER_PLACED: HOUSE_BOT-LOW_Y-SELL-1773271518766 -ORDER HOUSE_BOT-LOW_Y-SELL-1773271518766 HOUSE_BOT SELL 51 500 1773271518766 -ORDER_PLACED: HOUSE_BOT-LOW_N-BUY-1773271518766 -ORDER HOUSE_BOT-LOW_N-BUY-1773271518766 HOUSE_BOT BUY 49 500 1773271518766 -ORDER_PLACED: HOUSE_BOT-LOW_N-SELL-1773271518766 -ORDER HOUSE_BOT-LOW_N-SELL-1773271518766 HOUSE_BOT SELL 51 500 1773271518766 -ORDER_PLACED: 679b5ac5-a1f1-4a2a-8a7f-4678f47ca89d -ORDER 679b5ac5-a1f1-4a2a-8a7f-4678f47ca89d LIQUIDITY_TRADER BUY 52 100 1773271525752 -TRADE 679b5ac5-a1f1-4a2a-8a7f-4678f47ca89d HOUSE_BOT-LOW_Y-SELL-1773271518766 51 100 1773271525752 -TRADE_SETTLED: 679b5ac5-a1f1-4a2a-8a7f-4678f47ca89d <-> HOUSE_BOT-LOW_Y-SELL-1773271518766 -ORDER_PLACED: HOUSE_BOT-LOW_Y-BUY-1773271525754 -ORDER HOUSE_BOT-LOW_Y-BUY-1773271525754 HOUSE_BOT BUY 51 500 1773271525754 -ORDER_PLACED: HOUSE_BOT-LOW_Y-SELL-1773271525754 -ORDER HOUSE_BOT-LOW_Y-SELL-1773271525754 HOUSE_BOT SELL 53 500 1773271525754 -ORDER_PLACED: HOUSE_BOT-LOW_N-BUY-1773271525754 -ORDER HOUSE_BOT-LOW_N-BUY-1773271525754 HOUSE_BOT BUY 47 500 1773271525754 -ORDER_PLACED: HOUSE_BOT-LOW_N-SELL-1773271525754 -ORDER HOUSE_BOT-LOW_N-SELL-1773271525754 HOUSE_BOT SELL 49 500 1773271525754 -ORDER_PLACED: HOUSE_BOT-EVENT_Y-BUY-1773272988202 -ORDER HOUSE_BOT-EVENT_Y-BUY-1773272988202 HOUSE_BOT BUY 49 500 1773272988202 -ORDER_PLACED: HOUSE_BOT-EVENT_Y-SELL-1773272988206 -ORDER HOUSE_BOT-EVENT_Y-SELL-1773272988206 HOUSE_BOT SELL 51 500 1773272988206 -ORDER_PLACED: HOUSE_BOT-EVENT_N-BUY-1773272988206 -ORDER HOUSE_BOT-EVENT_N-BUY-1773272988206 HOUSE_BOT BUY 49 500 1773272988206 -ORDER_PLACED: HOUSE_BOT-EVENT_N-SELL-1773272988206 -ORDER HOUSE_BOT-EVENT_N-SELL-1773272988206 HOUSE_BOT SELL 51 500 1773272988206 -ORDER_PLACED: cfcf2488-6beb-4bee-a72b-dd76e143af4d -ORDER cfcf2488-6beb-4bee-a72b-dd76e143af4d STOCK_TRADER BUY 52 100 1773272994015 -TRADE cfcf2488-6beb-4bee-a72b-dd76e143af4d HOUSE_BOT-EVENT_Y-SELL-1773272988206 51 100 1773272994015 -TRADE_SETTLED: cfcf2488-6beb-4bee-a72b-dd76e143af4d <-> HOUSE_BOT-EVENT_Y-SELL-1773272988206 -ORDER_PLACED: HOUSE_BOT-EVENT_Y-BUY-1773272994020 -ORDER HOUSE_BOT-EVENT_Y-BUY-1773272994020 HOUSE_BOT BUY 49 500 1773272994020 -ORDER_PLACED: HOUSE_BOT-EVENT_Y-SELL-1773272994021 -ORDER HOUSE_BOT-EVENT_Y-SELL-1773272994021 HOUSE_BOT SELL 51 500 1773272994021 -ORDER_PLACED: HOUSE_BOT-EVENT_N-BUY-1773272994021 -ORDER HOUSE_BOT-EVENT_N-BUY-1773272994021 HOUSE_BOT BUY 49 500 1773272994021 -ORDER_PLACED: HOUSE_BOT-EVENT_N-SELL-1773272994021 -ORDER HOUSE_BOT-EVENT_N-SELL-1773272994021 HOUSE_BOT SELL 51 500 1773272994021 -ORDER_PLACED: 528ff941-d66a-495e-9f51-687ce8ec7d84 -ORDER 528ff941-d66a-495e-9f51-687ce8ec7d84 STOCK_TRADER BUY 150 10 1773272994036 diff --git a/matching-engine/matching-engine.iml b/matching-engine/matching-engine.iml deleted file mode 100644 index 867655b..0000000 --- a/matching-engine/matching-engine.iml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/matching-engine/pom.xml b/matching-engine/pom.xml deleted file mode 100644 index 8e0bcd4..0000000 --- a/matching-engine/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-starter-parent - 3.2.2 - - - - org.example - matching-engine - 1.0-SNAPSHOT - - - - - org.example - common - 1.0-SNAPSHOT - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-validation - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter - - - - org.junit.jupiter - junit-jupiter - 5.10.0 - test - - - \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java b/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java deleted file mode 100644 index 3c692ba..0000000 --- a/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java +++ /dev/null @@ -1,147 +0,0 @@ -package org.example.matching.Wallets; - -import lombok.RequiredArgsConstructor; -import org.example.matching.model.Order; -import org.example.matching.model.Reservation; -import org.example.matching.model.Trade; -import org.example.matching.model.Wallet; -import org.example.matching.orderbook.OrderRepository; -import org.springframework.stereotype.Service; // Add this import - -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Service // Tells Spring this is a managed bean -@RequiredArgsConstructor -public class InMemoryWalletService implements WalletService { - - private final Map wallets = new ConcurrentHashMap<>(); - private final Map reservations = new ConcurrentHashMap<>(); - private final OrderRepository orderRepository; - private final String INSTRUMENT = "MARKET"; - - private Wallet ensureWallet(String userId) { - return wallets.computeIfAbsent(userId, k -> new Wallet(userId)); - } - - @Override - public boolean reserveForOrder(Order order) { - String userId = order.getUserId(); - String instrument = order.getInstrument(); - Wallet w = ensureWallet(userId); - - if (order.getSide().name().equals("BUY")) { - long required = order.getPrice() * (long) order.getQuantity(); - if (!w.tryReserveCash(required)) return false; - - Reservation r = new Reservation(order.getId(), userId, order.getPrice(), order.getQuantity(), true, instrument); - r.setReservedCash(required); - reservations.put(order.getId(), r); - } else { - if (!w.tryReserveShares(instrument, order.getQuantity())) return false; - - Reservation r = new Reservation(order.getId(), userId, order.getPrice(), order.getQuantity(), false, instrument); - r.setReservedShares(order.getQuantity()); - reservations.put(order.getId(), r); - } - return true; - } - - -@Override -public void releaseReservation(String orderId){ - Reservation r = reservations.remove(orderId); - if(r==null)return; - Wallet w = ensureWallet(r.getUserId()); - if(r.getIsBuy()){ - w.releaseReserveCash(r.getReservedCash()); - - }else{ - w.releaseReservedShares(r.getInstrument(),r.getReservedShares()); - - } - } - - @Override - public void creditUserShares(String userId, long shares) { - Wallet w = ensureWallet(userId); - w.addAvailableShares(INSTRUMENT, shares); - } - - @Override - public void creditUserShares(String userId, String instrument, long shares) { - Wallet w = ensureWallet(userId); - w.addAvailableShares(instrument, shares); - } - - @Override - public void creditUserCash(String userId, long cash) { - Wallet w = ensureWallet(userId); - w.addAvailableCash(cash); - } - - @Override - public Wallet getWallet(String userId) { - return wallets.get(userId); - } - - @Override - public void settleTrade(Trade trade) { - Order buy = orderRepository.findById(trade.getBuyOrderId()).orElse(null); - Order sell = orderRepository.findById(trade.getSellOrderId()).orElse(null); - - if (buy == null || sell == null) return; - - Wallet buyerWallet = ensureWallet(buy.getUserId()); - Wallet sellerWallet = ensureWallet(sell.getUserId()); - Reservation buyRes = reservations.get(buy.getId()); - Reservation sellRes = reservations.get(sell.getId()); - - int qty = (int) trade.getQuantity(); - long tradeValue = trade.getPrice() * (long) qty; - String instrument = buy.getInstrument(); - // Process Buy Side - if (buyRes != null) { - long cashtoDebitFromReserved = buyRes.reduceBy(qty); - buyerWallet.debitReservedCash(cashtoDebitFromReserved); - - long refund = cashtoDebitFromReserved - tradeValue; - if (refund > 0) { - buyerWallet.addAvailableCash(refund); - } - // Add back the remaining available cash that was tied up in reservation - // Available cash before reservation = current available + reserved - long totalCost = tradeValue; - long availableBeforeReservation = buyerWallet.getAvailableCash() + cashtoDebitFromReserved; - long shouldHaveAvailable = availableBeforeReservation - totalCost + refund; - long currentAvailable = buyerWallet.getAvailableCash(); - long cashToAdd = shouldHaveAvailable - currentAvailable; - if (cashToAdd > 0) { - buyerWallet.addAvailableCash(cashToAdd); - } - - buyerWallet.addAvailableShares(instrument, qty); - } - - // Process Sell Side - if (sellRes != null) { - sellRes.reduceBy(qty); - sellerWallet.debitReservedShares(instrument, (long) qty); - sellerWallet.addAvailableCash(tradeValue); - } - - // Cleanup completed reservations - if (buyRes != null && buyRes.getRemainingQty() == 0) { - reservations.remove(buy.getId()); - } - if (sellRes != null && sellRes.getRemainingQty() == 0) { - reservations.remove(sell.getId()); - } - } - - @Override - public Collection getAllWallets() { - return wallets.values(); - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java b/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java deleted file mode 100644 index a452b8a..0000000 --- a/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.example.matching.Wallets; - -import lombok.AllArgsConstructor; -import org.example.matching.model.Order; -import org.example.matching.orderbook.OrderRepository; -import org.springframework.stereotype.Service; - -@Service -public class RiskManager { - - private final WalletService walletService; - private final OrderRepository orderRepository; // 1. Add the field - - // No-arg constructor for Main.java usage - public RiskManager() { - this.walletService = null; - this.orderRepository = null; - } - - // Constructor for dependency injection - public RiskManager(WalletService walletService, OrderRepository orderRepository) { - this.walletService = walletService; - this.orderRepository = orderRepository; - } - - - public boolean checkAndReserve(Order order) { - // currently just delegates to walletService which performs the reserve attempt - //orderRepository.save(order); - - // 2. NOW ATTEMPT TO RESERVE - return walletService.reserveForOrder(order); - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java b/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java deleted file mode 100644 index c6f6f87..0000000 --- a/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.example.matching.Wallets; - -import org.example.matching.model.Order; -import org.example.matching.model.Trade; -import org.example.matching.model.Wallet; - -public interface WalletService { - boolean reserveForOrder(Order order); - void releaseReservation(String orderId); - void settleTrade(Trade trade); - - // Additional methods needed for testing and API - void creditUserShares(String userId, long shares); - void creditUserShares(String userId, String instrument, long shares); - void creditUserCash(String userId, long cash); - Wallet getWallet(String userId); - java.util.Collection getAllWallets(); -} diff --git a/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java b/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java deleted file mode 100644 index 4bea993..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.example.matching.api.controller; - -import jdk.jfr.Event; -import lombok.RequiredArgsConstructor; -import org.example.matching.api.dto.EventRequest; -import org.example.matching.api.dto.MarketEvent; -import org.example.matching.api.service.MarketManagmentService; -import org.example.matching.api.service.SettlementService; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/api/events") -@RequiredArgsConstructor -public class EventController { - private final MarketManagmentService managementService; - private final SettlementService settlementService; - - @PostMapping("/create") - public ResponseEntity create(@RequestBody EventRequest req) { - MarketEvent event = managementService.createEvent( - req.getId(), - req.getQuestions(), - req.getYesTicker(), - req.getNoTicker(), - req.getExpiry(), - req.getLiquidity() - ); - return ResponseEntity.ok(event); - } - - @GetMapping("/{id}") - public ResponseEntity getEvent(@PathVariable String id) { - MarketEvent event = managementService.getEvent(id); - if (event == null) { - return ResponseEntity.notFound().build(); - } - return ResponseEntity.ok(event); - } - - @PostMapping("/{id}/settle/{outcome}") - public ResponseEntity settle(@PathVariable String id, @PathVariable String outcome) { - settlementService.settleEvent(id, outcome); - return ResponseEntity.ok("Event " + id + " settled with outcome: " + outcome); - - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java b/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java deleted file mode 100644 index 3efdcc3..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.example.matching.api.controller; - -import lombok.RequiredArgsConstructor; -import org.example.matching.api.dto.MarketEvent; -import org.example.matching.api.dto.OrderBookResponse; -import org.example.matching.api.service.MarketDataService; -import org.example.matching.api.service.MarketManagmentService; -import org.example.matching.matching.MatchingEngine; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; -import java.util.Map; - -@RequestMapping("/api/market") -@RestController -@RequiredArgsConstructor -public class MarketController { - private final MatchingEngine matchingEngine; - private final MarketDataService marketDataService; - private final MarketManagmentService marketManagmentService; - -// public MarketController(MatchingEngine matchingEngine) { -// this.matchingEngine = matchingEngine; -// } - - @GetMapping("/orderbook/{instrument}") - public ResponseEntity getOrderBook(@PathVariable String instrument) { - OrderBookResponse snapshot = matchingEngine.getSnapshot(instrument); - return ResponseEntity.ok(snapshot); - } - - @GetMapping("/ticker/{instrument}") - public ResponseEntity> getTicker(@PathVariable String instrument){ - return ResponseEntity.ok(marketDataService.getSnapshots(instrument.toUpperCase())); - } - - @GetMapping("/active") - public ResponseEntity> getActiveEvents() { - List activeEvents = marketManagmentService.getAllOpenEvents().stream().toList(); - return ResponseEntity.ok(activeEvents); - } -} diff --git a/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java b/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java deleted file mode 100644 index 7e6dacd..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.example.matching.api.controller; - -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import org.example.matching.api.dto.OrderRequest; -import org.example.matching.api.dto.OrderResponse; -import org.example.matching.api.service.OrderService; -import org.example.matching.model.Order; -import org.example.matching.orderbook.OrderRepository; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import javax.swing.text.html.parser.Entity; -import java.util.Optional; - -@RestController -@RequestMapping("/api/orders") -@RequiredArgsConstructor -public class OrderController { -private final OrderService orderService; -private final OrderRepository orderRepository; - - @PostMapping - public ResponseEntity placeOrder(@Valid @RequestBody OrderRequest request) { - OrderResponse response = orderService.processOrder(request); - - if (response.getStatus().equals("REJECTED")) { - return ResponseEntity.badRequest().body(response); - } - return ResponseEntity.ok(response); - } - - @GetMapping("/{id}") - public ResponseEntity getOrder(@PathVariable String id){ - return orderRepository.findById(id) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); - -}} diff --git a/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java b/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java deleted file mode 100644 index 2b0b3ba..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.example.matching.api.controller; - -import lombok.RequiredArgsConstructor; -import org.example.matching.Wallets.WalletService; -import org.example.matching.api.dto.DepositRequest; -import org.example.matching.api.dto.WalletResponse; -import org.example.matching.model.Wallet; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.Map; -import java.util.stream.Collectors; - -@RestController -@RequestMapping("/api/wallets") -@RequiredArgsConstructor -public class WalletController { - - private final WalletService walletService; - - @GetMapping("/{userId}") - public ResponseEntity getWallet(@PathVariable String userId){ - Wallet wallet = walletService.getWallet(userId); - - // Convert AtomicLong maps to Long maps for JSON response - Map availableShares = wallet.getAvailableShares().entrySet().stream() - .collect(Collectors.toMap( - Map.Entry::getKey, - entry -> entry.getValue().get() - )); - - Map reservedShares = wallet.getReservedShares().entrySet().stream() - .collect(Collectors.toMap( - Map.Entry::getKey, - entry -> entry.getValue().get() - )); - - WalletResponse response = WalletResponse.builder() - .userId(userId) - .availableCash(wallet.getAvailableCash()) - .reservedCash(wallet.getReservedCash()) - .availableShares(availableShares) - .reservedShares(reservedShares) - .build(); - return ResponseEntity.ok(response); - } - @PostMapping("/depositCash") - public ResponseEntity depositCash(@RequestBody DepositRequest request) { - walletService.creditUserCash(request.getUserId(), request.getAmount()); - return ResponseEntity.ok("Deposit Successful"); - } - - @PostMapping("/depositShares") - public ResponseEntity depositShares(@RequestBody DepositRequest request) { - // Credit shares for the specific instrument - walletService.creditUserShares(request.getUserId(), request.getInstrument(), request.getAmount()); - return ResponseEntity.ok("Shares Deposited Successfully"); - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/DepositRequest.java b/matching-engine/src/main/java/org/example/matching/api/dto/DepositRequest.java deleted file mode 100644 index 875f533..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/DepositRequest.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.example.matching.api.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class DepositRequest { - private String userId; - private long amount; - private String instrument; // Optional: used if depositing shares instead of cash - - public String getUserId() { - return userId; - } - - public long getAmount() { - return amount; - } - - public String getInstrument() { - return instrument; - } -} diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java b/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java deleted file mode 100644 index 0cc6eb7..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.example.matching.api.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.example.matching.api.dto.enums.EventStatus; - -import java.time.LocalDateTime; -import java.util.concurrent.atomic.AtomicLong; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class MarketEvent { - private String eventID; - private String questions; - private String yesTicker; - private String noTicker; - private LocalDateTime expiry; - private EventStatus status; - private String outcome; - private long liquidity; - - // This is the ONLY thing that drives price. Starts at 0. - private final AtomicLong virtualNetSold = new AtomicLong(0); - - // Getter for the virtual counter - public AtomicLong getVirtualNetSold() { - return virtualNetSold; - } - - // Fix the field name typo - public String getEventID() { - return eventID; - } - - public void setEventID(String eventID) { - this.eventID = eventID; - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/OrderBookResponse.java b/matching-engine/src/main/java/org/example/matching/api/dto/OrderBookResponse.java deleted file mode 100644 index ce2e349..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/OrderBookResponse.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.example.matching.api.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class OrderBookResponse { - private String instrument; - private List bids; - private List asks; -} diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/OrderMapper.java b/matching-engine/src/main/java/org/example/matching/api/dto/OrderMapper.java deleted file mode 100644 index 5368db0..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/OrderMapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.example.matching.api.dto; - -import org.example.matching.model.Order; -import org.example.matching.model.OrderSide; - -import java.util.UUID; - -public class OrderMapper { - public static Order toDomain(OrderRequest request) { - String orderId; - if ("HOUSE_BOT".equals(request.getUserId())) { - orderId = "HOUSE_BOT-" + request.getInstrument() + "-" + request.getSide() + "-" + System.currentTimeMillis(); - } else { - orderId = UUID.randomUUID().toString(); - } - - Order order = new Order( - orderId, - request.getUserId(), - request.getPrice(), - (int) request.getQuantity(), - System.currentTimeMillis(), - OrderSide.valueOf(request.getSide().toUpperCase()) - ); - order.setInstrument(request.getInstrument()); - return order; - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/OrderRequest.java b/matching-engine/src/main/java/org/example/matching/api/dto/OrderRequest.java deleted file mode 100644 index eba85b9..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/OrderRequest.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.example.matching.api.dto; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Positive; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@NoArgsConstructor -@Data -@Getter -public class OrderRequest { - @NotBlank(message = "User ID is required") - private String userId; - - @NotBlank(message = "Instrument is required") - private String instrument; - - @Pattern(regexp = "BUY|SELL", message = "Side must be BUY or SELL") - private String side; - - @Positive(message = "Price must be greater than zero") - private long price; - - @Positive(message = "Quantity must be greater than zero") - private long quantity; - - @NotBlank(message = "Idempotency key is required") - private String idempotencyKey; // Unique string from the frontend - - public String getUserId() { - return userId; - } - - public String getInstrument() { - return instrument; - } - - public String getSide() { - return side; - } - - public long getPrice() { - return price; - } - - public long getQuantity() { - return quantity; - } - - public String getIdempotencyKey() { - return idempotencyKey; - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/OrderResponse.java b/matching-engine/src/main/java/org/example/matching/api/dto/OrderResponse.java deleted file mode 100644 index f9013fe..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/OrderResponse.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.example.matching.api.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class OrderResponse { - private String orderId; - private String status; // "ACCEPTED", "REJECTED" - private String message; // "Insufficient funds" or "Success" - private long timestamp; - - public String getStatus() { - return status; - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/PriceLevel.java b/matching-engine/src/main/java/org/example/matching/api/dto/PriceLevel.java deleted file mode 100644 index 42a886a..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/PriceLevel.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.example.matching.api.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class PriceLevel { - private Long price; - private Long quantity; -} diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/WalletResponse.java b/matching-engine/src/main/java/org/example/matching/api/dto/WalletResponse.java deleted file mode 100644 index ef95c21..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/WalletResponse.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.example.matching.api.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.Map; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class WalletResponse { - private String userId; - private long availableCash; - private long reservedCash; - private Map availableShares; - private Map reservedShares; -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/dto/enums/EventStatus.java b/matching-engine/src/main/java/org/example/matching/api/dto/enums/EventStatus.java deleted file mode 100644 index 6680a51..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/dto/enums/EventStatus.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.example.matching.api.dto.enums; - -public enum EventStatus { - OPEN, - SETTLED, - CANCELLED -} diff --git a/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java b/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java deleted file mode 100644 index 0821485..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.example.matching.api.service; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.example.matching.api.dto.OrderRequest; -import org.example.matching.api.dto.OrderResponse; -import org.example.matching.api.dto.MarketEvent; -import org.example.matching.matching.MatchingEngine; -import org.example.matching.model.OrderSide; -import org.springframework.stereotype.Service; - -import java.util.concurrent.atomic.AtomicLong; - -@Slf4j -@Service -@RequiredArgsConstructor -public class LiquidBotService { - - private final OrderService orderService; - private final MarketManagmentService marketManagmentService; - private final MatchingEngine matchingEngine; - - private static final String BOT_ID = "HOUSE_BOT"; - private static final int SPREAD = 4; - - public void updateMarketTrigger(String ticker) { - // 🎯 KEY CHECK: Only provide liquidity for prediction markets (events), not regular stocks - if (!marketManagmentService.isEventTicker(ticker)) { - log.info("Ticker {} is not an event ticker - bot will not provide liquidity", ticker); - return; - } - - MarketEvent event = marketManagmentService.getEventByTicker(ticker); - if (event == null || event.getStatus() != org.example.matching.api.dto.enums.EventStatus.OPEN) { - // Don't provide liquidity for settled events - return; - } - - // 1. Get the displacement (starts at 0) - long netSold = event.getVirtualNetSold().get(); - long L = event.getLiquidity(); - - // 2. SIGMOID MATH (Guaranteed 50¢ if netSold is 0) - double exponent = (double) netSold / L; - long fairPriceYes = (long) (100.0 / (1.0 + Math.exp(-exponent))); - fairPriceYes = Math.max(1, Math.min(99, fairPriceYes)); - long fairPriceNo = 100 - fairPriceYes; - - // 3. SMART BOT LOGIC - Only provide liquidity if there's insufficient natural liquidity - boolean needsLiquidity = shouldProvideLiquidity(ticker, fairPriceYes, fairPriceNo); - - if (needsLiquidity) { - refreshBotOrders(event.getYesTicker(), fairPriceYes, true); - refreshBotOrders(event.getNoTicker(), fairPriceNo, false); - log.info("Bot providing liquidity for {}: YES {}¢, NO {}¢ (virtualNetSold: {})", - ticker, fairPriceYes, fairPriceNo, netSold); - } else { - // Step back - cancel bot orders when there's natural liquidity - matchingEngine.cancelAllOrdersForUser(BOT_ID, event.getYesTicker()); - matchingEngine.cancelAllOrdersForUser(BOT_ID, event.getNoTicker()); - log.info("Bot stepping back for {} - natural liquidity available", ticker); - } - } - - private boolean shouldProvideLiquidity(String ticker, long fairPriceYes, long fairPriceNo) { - // Get both order books for the event - String yesTicker = ticker.contains("_Y") ? ticker : ticker.replace("_N", "_Y"); - String noTicker = ticker.contains("_N") ? ticker : ticker.replace("_Y", "_N"); - - var yesBook = matchingEngine.getOrderBookForMarketData(yesTicker); - var noBook = matchingEngine.getOrderBookForMarketData(noTicker); - - boolean yesHasNaturalOrders = hasNaturalOrders(yesBook); - boolean noHasNaturalOrders = hasNaturalOrders(noBook); - - log.info("Liquidity check for {} - YES has natural: {}, NO has natural: {}", - ticker, yesHasNaturalOrders, noHasNaturalOrders); - - // Only provide liquidity if there's insufficient natural liquidity on either side - // But also consider if there are ANY natural orders at all - boolean hasAnyNaturalOrders = yesHasNaturalOrders || noHasNaturalOrders; - - // Step back if there are natural orders, provide liquidity if market is empty - return !hasAnyNaturalOrders; - } - - private boolean hasNaturalOrders(org.example.matching.model.OrderBook book) { - if (book.getBids().isEmpty() && book.getAsks().isEmpty()) { - return false; // Empty book - need liquidity - } - - // Check if there are orders from users other than bot - boolean naturalBids = book.getBids().values().stream().anyMatch(orders -> - orders.stream().anyMatch(order -> !order.getUserId().equals(BOT_ID))); - boolean naturalAsks = book.getAsks().values().stream().anyMatch(orders -> - orders.stream().anyMatch(order -> !order.getUserId().equals(BOT_ID))); - - log.info("Order book check - Natural bids: {}, Natural asks: {}, Total bid levels: {}, Total ask levels: {}", - naturalBids, naturalAsks, book.getBids().size(), book.getAsks().size()); - - return naturalBids || naturalAsks; - } - - // This is called by your OrderService AFTER a trade happens - public void recordTrade(String ticker, long quantity, String side) { - MarketEvent event = marketManagmentService.getEventByTicker(ticker); - if (event == null) return; - - // If a human BUYS from the bot, displacement goes UP - // If a human SELLS to the bot, displacement goes DOWN - if (side.equalsIgnoreCase("BUY")) { - event.getVirtualNetSold().addAndGet(quantity); - } else { - event.getVirtualNetSold().addAndGet(-quantity); - } - - log.info("Recorded trade: {} {} {} shares (virtualNetSold: {})", - ticker, side, quantity, event.getVirtualNetSold().get()); - - // Immediately trigger price update based on new displacement - updateMarketTrigger(ticker); - } - - private void refreshBotOrders(String ticker, long fairPrice, boolean isYesTicker) { - // Cancel all existing bot orders for this ticker - matchingEngine.cancelAllOrdersForUser(BOT_ID, ticker); - - // Place new orders with 1-cent spread around fair price for better visibility - long bidPrice = Math.max(1, fairPrice - 1); - long askPrice = Math.min(99, fairPrice + 1); - - // Place bid order (buy from users) - placeBotOrder(ticker, OrderSide.BUY, bidPrice, 500); - - // Place ask order (sell to users) - placeBotOrder(ticker, OrderSide.SELL, askPrice, 500); - } - - private void placeBotOrder(String ticker, OrderSide side, long price, long quantity) { - OrderRequest req = new OrderRequest(); - req.setUserId(BOT_ID); - req.setInstrument(ticker); - req.setSide(side.name()); - req.setPrice(price); - req.setQuantity(quantity); - req.setIdempotencyKey(BOT_ID + "-" + ticker + "-" + side + "-" + System.currentTimeMillis()); - - try { - orderService.processOrder(req); - } catch (Exception e) { - log.error("Failed to place bot order: {}", req, e); - } - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java b/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java deleted file mode 100644 index f9ec75e..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.example.matching.api.service; - -import org.springframework.stereotype.Service; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; - -@Service -public class MarketDataService { - //stores last price each stock is traded at - private final Map lastPrices = new ConcurrentHashMap<>(); - - // Average V(volume)WAP = sum(price*quantity)/sum(quantity) - private final Map sumPV = new ConcurrentHashMap<>(); - private final Map sumV = new ConcurrentHashMap<>(); - // To track the "Top of Book" (Best Bid/Ask) - private final Map bestBids = new ConcurrentHashMap<>(); - private final Map bestAsks = new ConcurrentHashMap<>(); - - public void UpdateTrade(String instrument, long price , long quantity){ - lastPrices.put(instrument,(double)price); - sumPV.merge(instrument,(double)(price*quantity),Double::sum); - sumV.merge(instrument,quantity,Long::sum); - } - - public void updateBookTops(String instrument, Double bestBid, Double bestAsk) { - if (bestBid != null) bestBids.put(instrument, bestBid); - if (bestAsk != null) bestAsks.put(instrument, bestAsk); - } - - - public Map getSnapshots(String instrument){ - double ltp = lastPrices.getOrDefault(instrument,0.0); - double vwap = sumV.getOrDefault(instrument,0L)==0?0: sumPV.get(instrument)/ sumV.get(instrument); - double bid = bestBids.getOrDefault(instrument, 0.0); - double ask = bestAsks.getOrDefault(instrument, 0.0); - double mid = (bid > 0 && ask > 0) ? (bid + ask) / 2.0 : ltp; - - return Map.of( - "instrument", instrument, - "lastPrice", ltp, - "vwap", vwap, - "bid", bid, - "ask", ask, - "mid", mid, - "spread", (ask - bid) - ); - } - - // A list of the last 10 prices - private final Map> priceHistory = new ConcurrentHashMap<>(); - - public void updateTrade(String instrument, long price, long quantity) { - lastPrices.put(instrument, (double) price); - - // Track History - priceHistory.computeIfAbsent(instrument, k -> new CopyOnWriteArrayList<>()) - .add(0, (double) price); // Add to the front - - // Keep only the last 10 - List history = priceHistory.get(instrument); - if (history.size() > 10) history.remove(history.size() - 1); - - // ... (rest of VWAP logic) - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java b/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java deleted file mode 100644 index 9784e52..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.example.matching.api.service; - -import org.example.matching.Wallets.WalletService; -import org.example.matching.api.dto.MarketEvent; -import org.example.matching.api.dto.enums.EventStatus; -import org.example.matching.api.service.LiquidBotService; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; - -@Service -public class MarketManagmentService { - private final WalletService walletService; - private final LiquidBotService liquidBotService; - - // Stores Event by ID - private final Map events = new ConcurrentHashMap<>(); - // Stores Ticker Pairs (YES <-> NO) - private final Map tickerPairs = new ConcurrentHashMap<>(); - // NEW: Maps Tickers to the Event they belong to - private final Map tickerToEvent = new ConcurrentHashMap<>(); - - // Use constructor injection to avoid circular dependency issues - public MarketManagmentService(WalletService walletService, @Lazy LiquidBotService liquidBotService) { - this.walletService = walletService; - this.liquidBotService = liquidBotService; - } - - public void registerEvent(MarketEvent event) { - tickerPairs.put(event.getYesTicker(), event.getNoTicker()); - tickerPairs.put(event.getNoTicker(), event.getYesTicker()); - - // Map both tickers to this event object - tickerToEvent.put(event.getYesTicker(), event); - tickerToEvent.put(event.getNoTicker(), event); - } - - public String getPartnerTicker(String ticker) { - return tickerPairs.get(ticker); - } - - public MarketEvent getEvent(String id) { - return events.get(id); - } - - public MarketEvent getEventByTicker(String ticker) { - return tickerToEvent.get(ticker); - } - - // Check if ticker belongs to an event (prediction market) vs regular stock - public boolean isEventTicker(String ticker) { - return tickerToEvent.containsKey(ticker); - } - - public MarketEvent createEvent(String id, String question, String yesTicker, String noTicker, int minutesFromNow, Long liquidity) { - // Use provided liquidity or default to 10000 - long eventLiquidity = (liquidity != null) ? liquidity : 10000L; - - MarketEvent event = MarketEvent.builder() - .eventID(id) - .questions(question) - .yesTicker(yesTicker) - .noTicker(noTicker) - .expiry(LocalDateTime.now().plusMinutes(minutesFromNow)) - .status(EventStatus.OPEN) - .liquidity(eventLiquidity) // Use configurable liquidity - .build(); - - events.put(id, event); - registerEvent(event); // Populate the lookup maps - - // Admin can make a "mistake" here (e.g. 100,000) - // and it won't break the 50-50 start! - walletService.creditUserShares("HOUSE_BOT", yesTicker, 100000); - walletService.creditUserShares("HOUSE_BOT", noTicker, 100000); - - // Give bot cash to place BUY orders (for market making) - walletService.creditUserCash("HOUSE_BOT", 10000000); // 100,000 dollars = 10,000,000 cents - - // Force the first quote at 50/50 - liquidBotService.updateMarketTrigger(yesTicker); - return event; - } - - public java.util.Collection getAllOpenEvents() { - return events.values().stream() - .filter(event -> event.getStatus() == EventStatus.OPEN) - .collect(java.util.stream.Collectors.toList()); - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java b/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java deleted file mode 100644 index c242208..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.example.matching.api.service; - -import lombok.RequiredArgsConstructor; -import org.example.matching.Wallets.RiskManager; -import org.example.matching.Wallets.WalletService; -import org.example.matching.api.dto.MarketEvent; -import org.example.matching.api.dto.OrderMapper; -import org.example.matching.api.dto.OrderRequest; -import org.example.matching.api.dto.OrderResponse; -import org.example.matching.api.dto.enums.EventStatus; -import org.example.matching.journal.EventJournal; -import org.example.matching.matching.MatchingEngine; -import org.example.matching.model.Order; -import org.example.matching.model.Trade; -import org.example.matching.orderbook.OrderRepository; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Service -@RequiredArgsConstructor -public class OrderService implements ApplicationContextAware { - - private final MarketDataService marketDataService; - private final RiskManager riskManager; - private final MatchingEngine matchingEngine; - private final WalletService walletService; - private final EventJournal eventJournal; - private final OrderRepository orderRepository; - private final MarketManagmentService marketManagmentService; - - private ApplicationContext applicationContext; - private LiquidBotService liquidBotService; - - // In-memory idempotency store: Key -> Previous Response - private final Map idempotencyStore = new ConcurrentHashMap<>(); - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - - private LiquidBotService getLiquidBotService() { - if (liquidBotService == null) { - liquidBotService = applicationContext.getBean(LiquidBotService.class); - } - return liquidBotService; - } - - public OrderResponse processOrder(OrderRequest request) { - if (idempotencyStore.containsKey(request.getIdempotencyKey())) { - return idempotencyStore.get(request.getIdempotencyKey()); - } - - Order order = OrderMapper.toDomain(request); - - // Check if event is settled FIRST (before any other validation) - MarketEvent event = marketManagmentService.getEventByTicker(order.getInstrument()); - if (event != null && EventStatus.SETTLED.equals(event.getStatus())) { - return buildResponse(order, "REJECTED", "Event is settled - no more orders allowed"); - } - - if (!riskManager.checkAndReserve(order)) { - return buildResponse(order, "REJECTED", "Insufficient funds or shares"); - } - - orderRepository.save(order); - eventJournal.appendRaw("ORDER_PLACED: " + order.getId()); - - // Matching Engine execution - List trades = matchingEngine.placeOrder(order); - - for (Trade trade : trades) { - // so once trade is done manage the cash and shares of the users using their ids and stuff in walletService below - walletService.settleTrade(trade); - marketDataService.UpdateTrade(order.getInstrument(),trade.getPrice(),trade.getQuantity()); - eventJournal.appendRaw("TRADE_SETTLED: " + trade.getBuyOrderId() + " <-> " + trade.getSellOrderId()); - - // Debug: Log trade details - System.out.println("DEBUG: Trade occurred - BuyID: " + trade.getBuyOrderId() + - ", SellID: " + trade.getSellOrderId() + - ", UserID: " + order.getUserId() + - ", Side: " + order.getSide()); - - // If trade involves HOUSE_BOT, record it for virtual counter - if (order.getUserId().equals("HOUSE_BOT") || - (trade.getBuyOrderId().startsWith("HOUSE_BOT") || trade.getSellOrderId().startsWith("HOUSE_BOT"))) { - System.out.println("DEBUG: Recording trade for virtual counter"); - getLiquidBotService().recordTrade(order.getInstrument(), trade.getQuantity(), order.getSide().name()); - } else { - System.out.println("DEBUG: Trade does not involve HOUSE_BOT - skipping virtual counter"); - } - } - var book = matchingEngine.getOrderBookForMarketData(order.getInstrument()); - marketDataService.updateBookTops( - order.getInstrument(), - book.getBestBid(), - book.getBestAsk() - ); - - OrderResponse response = buildResponse(order, "ACCEPTED", "Success"); - idempotencyStore.put(request.getIdempotencyKey(), response); - return response; - } - - private OrderResponse buildResponse(Order order, String status, String msg) { - return OrderResponse.builder() - .orderId(order.getId()) - .status(status) - .message(msg) - .timestamp(System.currentTimeMillis()) - .build(); - } -} - diff --git a/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java b/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java deleted file mode 100644 index cda3ba5..0000000 --- a/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.example.matching.api.service; - -import lombok.RequiredArgsConstructor; -import org.example.matching.Wallets.RiskManager; -import org.example.matching.Wallets.WalletService; -import org.example.matching.api.dto.MarketEvent; -import org.example.matching.api.dto.enums.EventStatus; -import org.example.matching.matching.MatchingEngine; -import org.example.matching.model.Order; -import org.example.matching.model.Wallet; -import org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.List; - -@Service -@RequiredArgsConstructor -public class SettlementService { - private final WalletService walletService; - private final MarketManagmentService marketService; - private final RiskManager riskManager; - private final MatchingEngine matchingEngine; - - - public void settleEvent(String eventId, String winningOutcome){ - MarketEvent event = marketService.getEvent(eventId); - if(event==null|| event.getStatus()== EventStatus.SETTLED) return;; - - // winningTicker will get assigned the outcome , if it is yes , the winningTicker is yes and no so winningTicker is No - String winningTicker = winningOutcome.equalsIgnoreCase("YES")? event.getYesTicker():event.getNoTicker(); - - //losingTicker will get assigned the outcome , if it is yes , the losingTicker is yes and no so losingTicker is NOx - String losingTicker = winningOutcome.equalsIgnoreCase("YES")?event.getNoTicker():event.getYesTicker(); - - Collection allWallets = walletService.getAllWallets(); - - for (Wallet wallet : allWallets) { - // 2. Pay out the Winners ($1.00 per share) - - //checking how many shares of yes one has - long winningShares = wallet.getAvailableShares().containsKey(winningTicker) - ? wallet.getAvailableShares().get(winningTicker).get() - : 0L; if (winningShares > 0) { - long payout = winningShares * 100; // 100 cents = $1.00 - wallet.addAvailableCash(payout); - wallet.getAvailableShares().remove(winningTicker); - } - - // 3. Remove the Losing Shares (Worth $0) - wallet.getAvailableShares().remove(losingTicker); - } - - event.setStatus(EventStatus.SETTLED); - event.setOutcome(winningOutcome); - - // Clear order books for both tickers after settlement - refundTicker(event.getYesTicker()); - refundTicker(event.getNoTicker()); - } - - - //check if walletService or RiskManager - private void refundTicker(String ticker){ - List orders = matchingEngine.clearBook(ticker); - for (Order o:orders){ - walletService.releaseReservation(o.getId()); - } - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/app/MatchingEngineApplication.java b/matching-engine/src/main/java/org/example/matching/app/MatchingEngineApplication.java deleted file mode 100644 index 3482542..0000000 --- a/matching-engine/src/main/java/org/example/matching/app/MatchingEngineApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.example.matching.app; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; - -@SpringBootApplication -@ComponentScan(basePackages = "org.example.matching") -public class MatchingEngineApplication { - public static void main(String[] args) { - SpringApplication.run(MatchingEngineApplication.class, args); - } -} diff --git a/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java b/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java deleted file mode 100644 index cb616ec..0000000 --- a/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.example.matching.config; - -import org.example.matching.Wallets.InMemoryWalletService; -import org.example.matching.Wallets.RiskManager; -import org.example.matching.Wallets.WalletService; -import org.example.matching.journal.EventJournal; -import org.example.matching.matching.MatchingEngine; -import org.example.matching.orderbook.InMemoryOrderRepository; -import org.example.matching.orderbook.OrderRepository; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class MatchingEngineConfig { - - @Bean - public EventJournal eventJournal() { - return new EventJournal(); - } - - @Bean - public OrderRepository orderRepository() { - return new InMemoryOrderRepository(); - } - - @Bean - public RiskManager riskManager(WalletService walletService, OrderRepository orderRepository) { - return new RiskManager(walletService, orderRepository); - } - - @Bean - public MatchingEngine matchingEngine(OrderRepository orderRepository, - WalletService walletService, - EventJournal eventJournal) { - return new MatchingEngine(orderRepository, walletService, eventJournal); - } -} diff --git a/matching-engine/src/main/java/org/example/matching/journal/EventJournal.java b/matching-engine/src/main/java/org/example/matching/journal/EventJournal.java deleted file mode 100644 index 48d66f5..0000000 --- a/matching-engine/src/main/java/org/example/matching/journal/EventJournal.java +++ /dev/null @@ -1,100 +0,0 @@ -//import lombok.AllArgsConstructor; -// -//import java.io.FileWriter; -//import java.io.IOException; -//import java.nio.file.FileAlreadyExistsException; -//import java.nio.file.Files; -//import java.nio.file.Path; -// -//@AllArgsConstructor -//public class EventJournal { -// private final Path journalDir; -// private final Path journalFile; -// -// public EventJournal() { -// try { -// journalDir = Path.of("journals"); -// Files.createDirectories(journalDir); -// -// journalFile = Path.of("engine.log"); -// try { -// Files.createFile(journalFile); -// } catch (FileAlreadyExistsException e) { -// // File already exists, that's fine -// } -// } catch (IOException e) { -//throw new RuntimeException("failed to journal"); -// } -// -// } -// public synchronized void append(String event){ -// try(FileWriter writer = new FileWriter(journalFile.toFile(),true)){ -// writer.write(event); -// writer.write(System.lineSeparator()); -// writer.flush(); -// } catch (IOException e) { -// throw new RuntimeException("Failed to write to journal", e); -// } -// } -// } -// - -package org.example.matching.journal; - -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.*; -import java.util.List; - -public class EventJournal { - - private final Path journalDir; - private final Path journalFile; - - public EventJournal() { - try { - journalDir = Path.of("journals"); - Files.createDirectories(journalDir); - journalFile = journalDir.resolve("engine.log"); - if (!Files.exists(journalFile)) { - Files.createFile(journalFile); - } - } catch (IOException e) { - throw new RuntimeException("Failed to initialize EventJournal", e); - } - } - - public synchronized void appendRaw(String eventBody) { - try (FileWriter writer = new FileWriter(journalFile.toFile(), true)) { - writer.write(eventBody); - writer.write(System.lineSeparator()); - writer.flush(); - // for production consider FileDescriptor.sync to fsync() - } catch (IOException e) { - throw new RuntimeException("Failed to write to journal", e); - } - } - - // helpers with consistent format - public void appendOrder(String id, String user, String side, long price, int qty, long ts) { - String line = String.format("ORDER %s %s %s %d %d %d", id, user, side, price, qty, ts); - appendRaw(line); - } - - public void appendTrade(String buyId, String sellId, long price, int qty, long ts) { - String line = String.format("TRADE %s %s %d %d %d", buyId, sellId, price, qty, ts); - appendRaw(line); - } - - public List readAllLines() { - try { - return Files.readAllLines(journalFile); - } catch (IOException e) { - throw new RuntimeException("Failed to read journal", e); - } - } - - public Path getJournalFile() { - return journalFile; - } -} diff --git a/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java b/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java deleted file mode 100644 index f5a2ce3..0000000 --- a/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.example.matching.matching; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.example.matching.Wallets.WalletService; -import org.example.matching.api.dto.OrderBookResponse; -import org.example.matching.api.dto.PriceLevel; -import org.example.matching.journal.EventJournal; -import org.example.matching.model.Order; -import org.example.matching.model.OrderBook; -import org.example.matching.model.OrderSide; -import org.example.matching.model.Trade; -import org.example.matching.orderbook.OrderRepository; -import org.springframework.stereotype.Service; - -import jakarta.annotation.PostConstruct; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Matching engine is responsible for matching orders and maintaining order books. - */ -@Slf4j -@Service -@RequiredArgsConstructor -public class MatchingEngine { - - private final OrderRepository orderRepository; - private final WalletService walletService; - private final EventJournal journal; - - private final Map orderBooks = new HashMap<>(); - - @PostConstruct - public void init() { - // Pre-create books for stocks you want to test - orderBooks.put("AAPL", new OrderBook()); - orderBooks.put("TSLA", new OrderBook()); - orderBooks.put("BTC", new OrderBook()); - } - - public OrderBookResponse getSnapshot(String instrument) { - OrderBook book = getOrderBook(instrument); - return OrderBookResponse.builder() - .instrument(instrument) - .bids(getPriceLevels(book.getBids())) - .asks(getPriceLevels(book.getAsks())) - .build(); - } - - public OrderBook getOrderBookForMarketData(String instrument) { - return orderBooks.getOrDefault(instrument, new OrderBook()); - } - - public List placeOrder(Order order) { - return placeOrder(order, true); - } - - public List placeOrder(Order order, boolean record) { - if (record) { - journal.appendOrder(order.getId(), order.getUserId(), order.getSide().name(), order.getPrice(), order.getQuantity(), order.getTimestamp()); - } - - OrderBook book = getOrderBook(order.getInstrument()); - List trades; - - if (order.getSide() == OrderSide.BUY) { - trades = book.matchBuy(order); - } else { - trades = book.matchSell(order); - } - - // Record trades - for (Trade trade : trades) { - if (record) { - journal.appendTrade(trade.getBuyOrderId(), trade.getSellOrderId(), trade.getPrice(), (int) trade.getQuantity(), trade.getTimestamp()); - } - } - - return trades; - } - - public List cancelAllOrdersForUser(String userId, String instrument) { - List cancelledOrders = new ArrayList<>(); - OrderBook book = orderBooks.get(instrument); - if (book != null) { - cancelledOrders = book.cancelAllOrdersForUser(userId); - } - return cancelledOrders; - } - - public List clearBook(String ticker) { - List cancelledOrders = new ArrayList<>(); - OrderBook book = orderBooks.get(ticker); - if (book != null) { - cancelledOrders = book.cancelAllOrdersForUser("*"); // Cancel all orders - } - return cancelledOrders; - } - - private OrderBook getOrderBook(String instrument) { - return orderBooks.computeIfAbsent(instrument, k -> new OrderBook()); - } - - private List getPriceLevels(Map> side) { - return side.entrySet().stream() - .map(entry -> { - long price = entry.getKey(); - long totalQty = entry.getValue().stream() - .mapToLong(Order::getQuantity) - .sum(); - return new PriceLevel(price, totalQty); - }) - .toList(); - } -} diff --git a/matching-engine/src/main/java/org/example/matching/model/Order.java b/matching-engine/src/main/java/org/example/matching/model/Order.java deleted file mode 100644 index 1862ae1..0000000 --- a/matching-engine/src/main/java/org/example/matching/model/Order.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.example.matching.model; - -import lombok.RequiredArgsConstructor; - -import java.time.LocalDateTime; - -@RequiredArgsConstructor -public class Order { - String id; - String userId; - long price; - int quantity; - long timestamp; - OrderSide side; - String instrument; // Added for multi-book support - - public Order(String userId, long price, int quantity, long timestamp, OrderSide side) { - this.userId = userId; - this.price = price; - this.quantity = quantity; - this.timestamp = timestamp; - this.side = side; - this.instrument = "DEFAULT"; // Default instrument - } - - public Order(String id, String userId, long price, int quantity, long timestamp, OrderSide side) { - this.id = id; - this.userId = userId; - this.price = price; - this.quantity = quantity; - this.timestamp = timestamp; - this.side = side; - this.instrument = "DEFAULT"; // Default instrument - } - - public Order(String id, String userId, long price, int quantity, long timestamp, OrderSide side, String instrument) { - this.id = id; - this.userId = userId; - this.price = price; - this.quantity = quantity; - this.timestamp = timestamp; - this.side = side; - this.instrument = instrument != null ? instrument : "DEFAULT"; - } - - public String getId() { - return id; - } - - public String getUserId() { - return userId; - } - - public long getPrice() { - return price; - } - - public int getQuantity() { - return quantity; - } - - public long getTimestamp() { - return timestamp; - } - - public OrderSide getSide() { - return side; - } - public void reduceQuantity(long delta) { - if (delta < 0) throw new IllegalArgumentException("delta must be >= 0"); - if (delta > quantity) throw new IllegalArgumentException("reduce more than remaining"); - this.quantity -= (int) delta; - } - - public boolean isFilled() { - return quantity <= 0; - } - - @Override - public String toString() { - return "Order{" + - "id='" + id + '\'' + - ", userId='" + userId + '\'' + - ", price=" + price + - ", quantity=" + quantity + - ", timestamp=" + timestamp + - ", side=" + side + - '}'; - } - - public String getInstrument() { - return instrument; - } - - public void setInstrument(String instrument) { - this.instrument = instrument; - } -} diff --git a/matching-engine/src/main/java/org/example/matching/model/OrderBook.java b/matching-engine/src/main/java/org/example/matching/model/OrderBook.java deleted file mode 100644 index a354f6e..0000000 --- a/matching-engine/src/main/java/org/example/matching/model/OrderBook.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.example.matching.model; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -import java.util.*; - -@Data -@Slf4j -public class OrderBook { - private final String instrument; - private final TreeMap> bids; // Price -> Orders (descending for bids) - private final TreeMap> asks; // Price -> Orders (ascending for asks) - - public OrderBook() { - this.instrument = ""; - this.bids = new TreeMap<>((a, b) -> Long.compare(b, a)); // Descending - this.asks = new TreeMap<>(); // Ascending - } - - public OrderBook(String instrument) { - this.instrument = instrument; - this.bids = new TreeMap<>((a, b) -> Long.compare(b, a)); // Descending - this.asks = new TreeMap<>(); // Ascending - } - - public void addOrder(Order order) { - if (order.getSide() == OrderSide.BUY) { - bids.computeIfAbsent(order.getPrice(), k -> new ArrayList<>()).add(order); - } else { - asks.computeIfAbsent(order.getPrice(), k -> new ArrayList<>()).add(order); - } - } - - public List matchBuy(Order buyOrder) { - List trades = new ArrayList<>(); - - while (buyOrder.getQuantity() > 0 && !asks.isEmpty()) { - Long bestAskPrice = asks.firstKey(); - if (bestAskPrice > buyOrder.getPrice()) { - break; // No match available - } - - List askOrders = asks.get(bestAskPrice); - if (askOrders.isEmpty()) { - asks.remove(bestAskPrice); - continue; - } - - Order askOrder = askOrders.get(0); - int tradeQuantity = Math.min(buyOrder.getQuantity(), askOrder.getQuantity()); - - // Create trade - Trade trade = new Trade(buyOrder.getId(), askOrder.getId(), bestAskPrice, tradeQuantity, buyOrder.getInstrument()); - trades.add(trade); - - // Update quantities - buyOrder.reduceQuantity(tradeQuantity); - askOrder.reduceQuantity(tradeQuantity); - - // Remove filled orders - if (askOrder.isFilled()) { - askOrders.remove(0); - if (askOrders.isEmpty()) { - asks.remove(bestAskPrice); - } - } - } - - // Add remaining buy order if not filled - if (buyOrder.getQuantity() > 0) { - addOrder(buyOrder); - } - - return trades; - } - - public List matchSell(Order sellOrder) { - List trades = new ArrayList<>(); - - while (sellOrder.getQuantity() > 0 && !bids.isEmpty()) { - Long bestBidPrice = bids.firstKey(); - if (bestBidPrice < sellOrder.getPrice()) { - break; // No match available - } - - List bidOrders = bids.get(bestBidPrice); - if (bidOrders.isEmpty()) { - bids.remove(bestBidPrice); - continue; - } - - Order bidOrder = bidOrders.get(0); - int tradeQuantity = Math.min(sellOrder.getQuantity(), bidOrder.getQuantity()); - - // Create trade - Trade trade = new Trade(bidOrder.getId(), sellOrder.getId(), bestBidPrice, tradeQuantity, sellOrder.getInstrument()); - trades.add(trade); - - // Update quantities - sellOrder.reduceQuantity(tradeQuantity); - bidOrder.reduceQuantity(tradeQuantity); - - // Remove filled orders - if (bidOrder.isFilled()) { - bidOrders.remove(0); - if (bidOrders.isEmpty()) { - bids.remove(bestBidPrice); - } - } - } - - // Add remaining sell order if not filled - if (sellOrder.getQuantity() > 0) { - addOrder(sellOrder); - } - - return trades; - } - - public List cancelAllOrdersForUser(String userId) { - List cancelledOrders = new ArrayList<>(); - - // Cancel bids - bids.values().forEach(orderList -> { - orderList.removeIf(order -> { - if (order.getUserId().equals(userId) || userId.equals("*")) { - cancelledOrders.add(order); - return true; - } - return false; - }); - }); - - // Remove empty price levels - bids.entrySet().removeIf(entry -> entry.getValue().isEmpty()); - - // Cancel asks - asks.values().forEach(orderList -> { - orderList.removeIf(order -> { - if (order.getUserId().equals(userId) || userId.equals("*")) { - cancelledOrders.add(order); - return true; - } - return false; - }); - }); - - // Remove empty price levels - asks.entrySet().removeIf(entry -> entry.getValue().isEmpty()); - - return cancelledOrders; - } - - public Double getBestBid() { - return bids.isEmpty() ? 0.0 : bids.firstKey().doubleValue(); - } - - public Double getBestAsk() { - return asks.isEmpty() ? 0.0 : asks.firstKey().doubleValue(); - } - - public String dumpBook() { - StringBuilder sb = new StringBuilder(); - sb.append("BIDS:\n"); - for (Map.Entry> entry : bids.entrySet()) { - sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue().size()).append(" orders\n"); - } - sb.append("ASKS:\n"); - for (Map.Entry> entry : asks.entrySet()) { - sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue().size()).append(" orders\n"); - } - return sb.toString(); - } -} diff --git a/matching-engine/src/main/java/org/example/matching/model/OrderSide.java b/matching-engine/src/main/java/org/example/matching/model/OrderSide.java deleted file mode 100644 index 2c98b1c..0000000 --- a/matching-engine/src/main/java/org/example/matching/model/OrderSide.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.example.matching.model; - -public enum OrderSide { -BUY, - SELL - - -} diff --git a/matching-engine/src/main/java/org/example/matching/model/Trade.java b/matching-engine/src/main/java/org/example/matching/model/Trade.java deleted file mode 100644 index c5a7f00..0000000 --- a/matching-engine/src/main/java/org/example/matching/model/Trade.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.example.matching.model; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -@ToString -public class Trade { - private String buyOrderId; - private String sellOrderId; - private long price; - private long quantity; - private long timestamp; - private String instrument; - - public Trade(String buyOrderId, String sellOrderId, long price, long quantity, String instrument) { - this.buyOrderId = buyOrderId; - this.sellOrderId = sellOrderId; - this.price = price; - this.quantity = quantity; - this.timestamp = System.currentTimeMillis(); - this.instrument = instrument; - } - - public String getBuyOrderId() { - return buyOrderId; - } - - public String getSellOrderId() { - return sellOrderId; - } - - public long getPrice() { - return price; - } - - public long getQuantity() { - return quantity; - } - - public long getTimestamp() { - return timestamp; - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/model/Wallet.java b/matching-engine/src/main/java/org/example/matching/model/Wallet.java deleted file mode 100644 index 48c7dba..0000000 --- a/matching-engine/src/main/java/org/example/matching/model/Wallet.java +++ /dev/null @@ -1,134 +0,0 @@ -package org.example.matching.model; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; - -public class Wallet { - private final String UserID; - private final AtomicLong availablecash = new AtomicLong(0); - private final AtomicLong reservedcash = new AtomicLong(0); - - // Fixed: Standardized on AtomicLong for all maps - private final ConcurrentHashMap availableShares = new ConcurrentHashMap<>(); - private final ConcurrentHashMap reservedShares = new ConcurrentHashMap<>(); - - public Wallet(String userID) { - this.UserID = userID; - } - - public String getUserId() { - return UserID; - } - - public long getAvailableCash() { - return availablecash.get(); - } - - public long getReservedCash() { - return reservedcash.get(); - } - - public void addAvailableCash(long amount) { - availablecash.addAndGet(amount); - } - - public boolean tryReserveCash(long amount) { - while (true) { - long avail = availablecash.get(); - //if avail less than the amount needed - if (avail < amount) return false; - if (availablecash.compareAndSet(avail, avail - amount)) { - reservedcash.addAndGet(amount); - return true; - //else avail = avail - amount and reserve cash .add amount - } - } - } - - public void releaseReserveCash(long amount) { - //in release reserve cash we just subtract the amount from the reservedcash and add it back to the available cash. - reservedcash.addAndGet(-amount); - availablecash.addAndGet(amount); - } - -//remove the money from the reserves - public void debitReservedCash(long amount) { - reservedcash.addAndGet(-amount); - - } - - // Fixed: Returns along to match AtomicLong, and handles the Map lookup safely - public long getAvailableShares(String instrument) { - AtomicLong val = availableShares.get(instrument); - return (val == null) ? 0L : val.get(); - } - - public long getReservedShares(String instrument) { - AtomicLong val = reservedShares.get(instrument); - return (val == null) ? 0L : val.get(); - } - - // Methods to get all shares for API responses - public ConcurrentHashMap getAvailableShares() { - return availableShares; - } - - public ConcurrentHashMap getReservedShares() { - return reservedShares; - } - - // get the quantity of the shares the available - //get the qty of reserved of shares - //pass the shares and the qty to reserve it - public boolean tryReserveShares(String instrument, long qty) { - // Fixed: Use AtomicLong and handle initialization correctly - // atomiclong does not hold it instead it points to a memory address - AtomicLong avail = availableShares.computeIfAbsent(instrument, k -> new AtomicLong(0)); - AtomicLong reserved = reservedShares.computeIfAbsent(instrument, k -> new AtomicLong(0)); -// - // currently is the - while (true) { - long currentAvail = avail.get(); - if (currentAvail < qty) return false; -//if avail >qty - // avail and subtract from avail - if (avail.compareAndSet(currentAvail, currentAvail - qty)) { - reserved.addAndGet(qty); - return true; - } - } - } - // see if the instrument exists - //if not create it - // -//atomiclong avail = availableshares.computeifabsent(instrument , - //atomiclong resrved = resrvedshares.computeIfabsent(instrument,k-> new AtomicLong(0); - public void releaseReservedShares(String instrument, long qty) { - // Fixed: Use AtomicLong here to match the class fields - AtomicLong reserved = reservedShares.get(instrument); - AtomicLong avail = availableShares.get(instrument); - - if (reserved != null) { - reserved.addAndGet(-qty); - } - - if (avail != null) { - avail.addAndGet(qty); - } - } - - public void debitReservedShares(String instrument, long qty) { - // Fixed: Changed from AtomicInteger to AtomicLong - AtomicLong reserved = reservedShares.get(instrument); - if (reserved != null) { - reserved.addAndGet(-qty); - } - } - - //add so available shares . add the shares and in if not create and add - public void addAvailableShares(String instrument, long qty) { - // Fixed: Used AtomicLong in the lambda - availableShares.computeIfAbsent(instrument, k -> new AtomicLong(0)) - .addAndGet(qty); - } -} \ No newline at end of file diff --git a/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java b/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java deleted file mode 100644 index 88df633..0000000 --- a/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.example.matching.orderbook; - -import org.example.matching.model.Order; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import lombok.RequiredArgsConstructor; - -public class InMemoryOrderRepository implements OrderRepository { - - private final Map orders = new ConcurrentHashMap<>(); - - public InMemoryOrderRepository() { - // Constructor - } - - @Override - public void save(Order order) { - orders.put(order.getId(), order); - } - - @Override - public Optional findById(String id) { - return Optional.ofNullable(orders.get(id)); - } - - @Override - public void deleteById(String id) { - orders.remove(id); - } - - @Override - public void delete(Order order) { - orders.remove(order.getId()); - } -} diff --git a/matching-engine/src/main/java/org/example/matching/orderbook/OrderRepository.java b/matching-engine/src/main/java/org/example/matching/orderbook/OrderRepository.java deleted file mode 100644 index 30d6015..0000000 --- a/matching-engine/src/main/java/org/example/matching/orderbook/OrderRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.example.matching.orderbook; - - -import lombok.AllArgsConstructor; -import org.example.matching.model.Order; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface OrderRepository { - void save(Order order); - Optional findById(String orderId); - void deleteById(String id); - void delete(Order order); -} diff --git a/matching-engine/src/main/resources/application.properties b/matching-engine/src/main/resources/application.properties deleted file mode 100644 index b2860ce..0000000 --- a/matching-engine/src/main/resources/application.properties +++ /dev/null @@ -1,12 +0,0 @@ -# Server Configuration -server.port=8080 - -# Application Configuration -spring.application.name=matching-engine - -# Logging Configuration -logging.level.org.example.matching=DEBUG -logging.level.org.springframework.web=INFO - -# Jackson Configuration (for JSON serialization) -spring.jackson.serialization.indent-output=true diff --git a/matching-engine/target/classes/application.properties b/matching-engine/target/classes/application.properties deleted file mode 100644 index b2860ce..0000000 --- a/matching-engine/target/classes/application.properties +++ /dev/null @@ -1,12 +0,0 @@ -# Server Configuration -server.port=8080 - -# Application Configuration -spring.application.name=matching-engine - -# Logging Configuration -logging.level.org.example.matching=DEBUG -logging.level.org.springframework.web=INFO - -# Jackson Configuration (for JSON serialization) -spring.jackson.serialization.indent-output=true diff --git a/matching-engine/target/classes/org/example/matching/Wallets/InMemoryWalletService.class b/matching-engine/target/classes/org/example/matching/Wallets/InMemoryWalletService.class deleted file mode 100644 index 0092731..0000000 Binary files a/matching-engine/target/classes/org/example/matching/Wallets/InMemoryWalletService.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/Wallets/WalletService.class b/matching-engine/target/classes/org/example/matching/Wallets/WalletService.class deleted file mode 100644 index e3869d6..0000000 Binary files a/matching-engine/target/classes/org/example/matching/Wallets/WalletService.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/controller/EventController.class b/matching-engine/target/classes/org/example/matching/api/controller/EventController.class deleted file mode 100644 index 0ec0ec7..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/controller/EventController.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/controller/MarketController.class b/matching-engine/target/classes/org/example/matching/api/controller/MarketController.class deleted file mode 100644 index c623722..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/controller/MarketController.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/controller/OrderController.class b/matching-engine/target/classes/org/example/matching/api/controller/OrderController.class deleted file mode 100644 index 49b3483..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/controller/OrderController.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/controller/WalletController.class b/matching-engine/target/classes/org/example/matching/api/controller/WalletController.class deleted file mode 100644 index 92d4fff..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/controller/WalletController.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/DepositRequest.class b/matching-engine/target/classes/org/example/matching/api/dto/DepositRequest.class deleted file mode 100644 index 050c968..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/DepositRequest.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/EventRequest$EventRequestBuilder.class b/matching-engine/target/classes/org/example/matching/api/dto/EventRequest$EventRequestBuilder.class deleted file mode 100644 index cbadbde..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/EventRequest$EventRequestBuilder.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/EventRequest.class b/matching-engine/target/classes/org/example/matching/api/dto/EventRequest.class deleted file mode 100644 index ce8b4d5..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/EventRequest.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/MarketEvent$MarketEventBuilder.class b/matching-engine/target/classes/org/example/matching/api/dto/MarketEvent$MarketEventBuilder.class deleted file mode 100644 index c8c5f52..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/MarketEvent$MarketEventBuilder.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/MarketEvent.class b/matching-engine/target/classes/org/example/matching/api/dto/MarketEvent.class deleted file mode 100644 index 960cecc..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/MarketEvent.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class b/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class deleted file mode 100644 index b7f8866..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse.class b/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse.class deleted file mode 100644 index ca71dbc..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/OrderBookResponse.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/OrderMapper.class b/matching-engine/target/classes/org/example/matching/api/dto/OrderMapper.class deleted file mode 100644 index 1113c3b..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/OrderMapper.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/OrderRequest.class b/matching-engine/target/classes/org/example/matching/api/dto/OrderRequest.class deleted file mode 100644 index 1407b0a..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/OrderRequest.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class b/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class deleted file mode 100644 index 9e8af78..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse.class b/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse.class deleted file mode 100644 index 434c332..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/OrderResponse.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/PriceLevel.class b/matching-engine/target/classes/org/example/matching/api/dto/PriceLevel.class deleted file mode 100644 index 03e7eb7..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/PriceLevel.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class b/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class deleted file mode 100644 index 0138625..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse.class b/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse.class deleted file mode 100644 index be44d0c..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/WalletResponse.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/dto/enums/EventStatus.class b/matching-engine/target/classes/org/example/matching/api/dto/enums/EventStatus.class deleted file mode 100644 index cd989c2..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/dto/enums/EventStatus.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/service/LiquidBotService.class b/matching-engine/target/classes/org/example/matching/api/service/LiquidBotService.class deleted file mode 100644 index f23c5e5..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/service/LiquidBotService.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/service/MarketDataService.class b/matching-engine/target/classes/org/example/matching/api/service/MarketDataService.class deleted file mode 100644 index 3d03dad..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/service/MarketDataService.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/service/MarketManagmentService.class b/matching-engine/target/classes/org/example/matching/api/service/MarketManagmentService.class deleted file mode 100644 index 5358723..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/service/MarketManagmentService.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/service/OrderService.class b/matching-engine/target/classes/org/example/matching/api/service/OrderService.class deleted file mode 100644 index 2eadd8c..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/service/OrderService.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/api/service/SettlementService.class b/matching-engine/target/classes/org/example/matching/api/service/SettlementService.class deleted file mode 100644 index 9271096..0000000 Binary files a/matching-engine/target/classes/org/example/matching/api/service/SettlementService.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/app/MatchingEngineApplication.class b/matching-engine/target/classes/org/example/matching/app/MatchingEngineApplication.class deleted file mode 100644 index 0a05346..0000000 Binary files a/matching-engine/target/classes/org/example/matching/app/MatchingEngineApplication.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/config/MatchingEngineConfig.class b/matching-engine/target/classes/org/example/matching/config/MatchingEngineConfig.class deleted file mode 100644 index 89e72b6..0000000 Binary files a/matching-engine/target/classes/org/example/matching/config/MatchingEngineConfig.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/journal/EventJournal.class b/matching-engine/target/classes/org/example/matching/journal/EventJournal.class deleted file mode 100644 index 0844424..0000000 Binary files a/matching-engine/target/classes/org/example/matching/journal/EventJournal.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/matching/MatchingEngine.class b/matching-engine/target/classes/org/example/matching/matching/MatchingEngine.class deleted file mode 100644 index af4d371..0000000 Binary files a/matching-engine/target/classes/org/example/matching/matching/MatchingEngine.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/model/Order.class b/matching-engine/target/classes/org/example/matching/model/Order.class deleted file mode 100644 index 18eb337..0000000 Binary files a/matching-engine/target/classes/org/example/matching/model/Order.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/model/OrderBook.class b/matching-engine/target/classes/org/example/matching/model/OrderBook.class deleted file mode 100644 index 7a543a3..0000000 Binary files a/matching-engine/target/classes/org/example/matching/model/OrderBook.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/model/OrderSide.class b/matching-engine/target/classes/org/example/matching/model/OrderSide.class deleted file mode 100644 index a9a98a8..0000000 Binary files a/matching-engine/target/classes/org/example/matching/model/OrderSide.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/model/Trade.class b/matching-engine/target/classes/org/example/matching/model/Trade.class deleted file mode 100644 index 1a0f9fe..0000000 Binary files a/matching-engine/target/classes/org/example/matching/model/Trade.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/model/Wallet.class b/matching-engine/target/classes/org/example/matching/model/Wallet.class deleted file mode 100644 index 41fecd1..0000000 Binary files a/matching-engine/target/classes/org/example/matching/model/Wallet.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/orderbook/InMemoryOrderRepository.class b/matching-engine/target/classes/org/example/matching/orderbook/InMemoryOrderRepository.class deleted file mode 100644 index 64a216a..0000000 Binary files a/matching-engine/target/classes/org/example/matching/orderbook/InMemoryOrderRepository.class and /dev/null differ diff --git a/matching-engine/target/classes/org/example/matching/orderbook/OrderRepository.class b/matching-engine/target/classes/org/example/matching/orderbook/OrderRepository.class deleted file mode 100644 index ad66fe9..0000000 Binary files a/matching-engine/target/classes/org/example/matching/orderbook/OrderRepository.class and /dev/null differ diff --git a/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst deleted file mode 100644 index acd2cd4..0000000 --- a/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ /dev/null @@ -1,31 +0,0 @@ -org/example/matching/api/controller/MarketController.class -org/example/matching/app/MatchingEngineApplication.class -org/example/matching/api/dto/EventRequest$EventRequestBuilder.class -org/example/matching/model/Trade.class -org/example/matching/model/OrderSide.class -org/example/matching/api/dto/OrderResponse.class -org/example/matching/journal/EventJournal.class -org/example/matching/Wallets/WalletService.class -org/example/matching/Wallets/InMemoryWalletService.class -org/example/matching/model/Reservation.class -org/example/matching/api/controller/OrderController.class -org/example/matching/api/dto/OrderBookResponse$OrderBookResponseBuilder.class -org/example/matching/model/OrderBook.class -org/example/matching/api/dto/WalletResponse$WalletResponseBuilder.class -org/example/matching/api/dto/OrderResponse$OrderResponseBuilder.class -org/example/matching/orderbook/InMemoryOrderRepository.class -org/example/matching/matching/MatchingEngine.class -org/example/matching/model/Order.class -org/example/matching/api/dto/DepositRequest.class -org/example/matching/api/dto/PriceLevel.class -org/example/matching/api/dto/WalletResponse.class -org/example/matching/api/controller/WalletController.class -org/example/matching/Wallets/RiskManager.class -org/example/matching/api/dto/OrderMapper.class -org/example/matching/api/dto/OrderBookResponse.class -org/example/matching/api/dto/enums/EventStatus.class -org/example/matching/api/dto/OrderRequest.class -org/example/matching/config/MatchingEngineConfig.class -org/example/matching/orderbook/OrderRepository.class -org/example/matching/model/Wallet.class -org/example/matching/api/service/OrderService.class diff --git a/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst deleted file mode 100644 index 0a95275..0000000 --- a/matching-engine/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ /dev/null @@ -1,34 +0,0 @@ -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/app/MatchingEngineApplication.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/journal/EventJournal.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Trade.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/enums/EventStatus.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/InMemoryOrderRepository.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/MarketEvent.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/WalletResponse.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/LiquidBotService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/RiskManager.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/OrderBook.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/WalletController.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/EventController.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Wallet.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/InMemoryWalletService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/PriceLevel.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/OrderController.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderResponse.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/SettlementService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/controller/MarketController.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/OrderService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/orderbook/OrderRepository.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Order.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/Reservation.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/Wallets/WalletService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderBookResponse.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/DepositRequest.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/MarketDataService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderRequest.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/matching/MatchingEngine.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/EventRequest.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/service/MarketManagmentService.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/model/OrderSide.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/api/dto/OrderMapper.java -/Users/aumpatel/Desktop/Spring/Strongx/matching-engine/src/main/java/org/example/matching/config/MatchingEngineConfig.java diff --git a/matching-engine/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/matching-engine/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst deleted file mode 100644 index e69de29..0000000 diff --git a/matching-engine/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/matching-engine/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/org/example/Main.java b/src/main/java/org/example/Main.java deleted file mode 100644 index 407f157..0000000 --- a/src/main/java/org/example/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.example; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello world!"); - } -} \ No newline at end of file