├── .editorconfig ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── _config.yml ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── schema_json └── feed │ ├── common │ ├── enums │ │ ├── feed_command.json │ │ ├── feed_message_type.json │ │ └── feed_special_message.json │ ├── interval │ │ └── interval_type.json │ └── message │ │ └── message_line.json │ ├── lookup │ ├── historical │ │ ├── dated_interval.json │ │ ├── enums │ │ │ ├── data_direction.json │ │ │ ├── historical_command.json │ │ │ ├── partial_datapoint.json │ │ │ └── time_label_placement.json │ │ ├── interval.json │ │ └── tick.json │ ├── market_summary │ │ ├── end_of_day_snapshot.json │ │ ├── enums │ │ │ └── market_summary_command.json │ │ ├── five_minute_snapshot.json │ │ └── fundamental_snapshot.json │ ├── news │ │ ├── enums │ │ │ ├── news_command.json │ │ │ ├── news_configuration_message_type.json │ │ │ ├── xml_text_email_option.json │ │ │ └── xml_text_option.json │ │ └── news_configuration_message.json │ ├── option_chains │ │ ├── enums │ │ │ ├── equity_option_month.json │ │ │ ├── future_month.json │ │ │ ├── non_standard_option_types.json │ │ │ ├── option_chains_command.json │ │ │ ├── option_filter_type.json │ │ │ ├── option_type.json │ │ │ └── puts_calls_option.json │ │ ├── future_contract.json │ │ ├── future_spread.json │ │ └── option_contract.json │ └── symbol_market_info │ │ ├── enums │ │ ├── search_code_type.json │ │ ├── search_field.json │ │ ├── symbol_filter_type.json │ │ └── symbol_market_info_command.json │ │ ├── listed_market.json │ │ ├── niac_code.json │ │ ├── security_type.json │ │ ├── sic_code.json │ │ ├── symbol_search_result.json │ │ └── trade_condition.json │ └── streaming │ ├── admin │ ├── client_statistics.json │ └── enums │ │ ├── admin_system_command.json │ │ ├── admin_system_message_type.json │ │ └── on_off_option.json │ ├── common │ ├── enums │ │ └── server_connection_status.json │ └── feed_statistics.json │ ├── derivative │ ├── enums │ │ ├── derivative_command.json │ │ ├── derivative_message_type.json │ │ ├── derivative_system_command.json │ │ └── derivative_system_message_type.json │ ├── interval.json │ └── watched_interval.json │ └── level1 │ ├── customer_information.json │ ├── enums │ ├── level_1_command.json │ ├── level_1_message_type.json │ ├── level_1_system_command.json │ ├── level_1_system_message_type.json │ ├── log_level │ │ └── log_level.json │ ├── summary_update │ │ ├── summary_update_content.json │ │ └── summary_update_field.json │ └── trade_correction │ │ └── correction_type.json │ ├── fundamental_data.json │ ├── news_headline.json │ ├── regional_quote.json │ ├── summary_update.json │ └── trade_correction.json ├── settings.gradle └── src └── main ├── java └── net │ └── jacobpeterson │ └── iqfeed4j │ ├── IQFeed4j.java │ ├── executable │ ├── ExecutablePollingFeed.java │ └── IQConnectExecutable.java │ ├── feed │ ├── AbstractFeed.java │ ├── RequestIDFeedHelper.java │ ├── exception │ │ ├── IQFeedException.java │ │ ├── IQFeedRuntimeException.java │ │ ├── NoDataException.java │ │ └── SyntaxException.java │ ├── lookup │ │ ├── AbstractLookupFeed.java │ │ ├── historical │ │ │ ├── HistoricalFeed.java │ │ │ └── pool │ │ │ │ └── HistoricalFeedPool.java │ │ ├── marketsummary │ │ │ └── MarketSummaryFeed.java │ │ ├── news │ │ │ ├── NewsFeed.java │ │ │ └── xml │ │ │ │ ├── configuration │ │ │ │ ├── NewsCategory.java │ │ │ │ ├── NewsConfiguration.java │ │ │ │ ├── NewsMajorSource.java │ │ │ │ ├── NewsMinorSource.java │ │ │ │ └── NewsSource.java │ │ │ │ ├── deserializers │ │ │ │ ├── NewsHeadlineSymbolsDeserializer.java │ │ │ │ └── NewsStoryIsLinkDeserializer.java │ │ │ │ ├── headline │ │ │ │ ├── NewsHeadline.java │ │ │ │ └── NewsHeadlines.java │ │ │ │ ├── story │ │ │ │ ├── NewsStories.java │ │ │ │ └── NewsStory.java │ │ │ │ └── storycount │ │ │ │ ├── NewsStoryCount.java │ │ │ │ └── NewsStoryCounts.java │ │ ├── optionchains │ │ │ └── OptionChainsFeed.java │ │ └── symbolmarketinfo │ │ │ └── SymbolMarketInfoFeed.java │ ├── message │ │ ├── FeedMessageAdapter.java │ │ ├── FeedMessageListener.java │ │ ├── MultiMessageAccumulator.java │ │ ├── MultiMessageAdapter.java │ │ ├── MultiMessageListener.java │ │ └── SingleMessageFuture.java │ └── streaming │ │ ├── AbstractServerConnectionFeed.java │ │ ├── StreamingCSVMappers.java │ │ ├── admin │ │ └── AdminFeed.java │ │ ├── derivative │ │ ├── DerivativeFeed.java │ │ └── DerivativeFeedEventListener.java │ │ ├── level1 │ │ ├── Level1Feed.java │ │ └── Level1FeedEventListener.java │ │ └── marketdepth │ │ └── MarketDepthFeed.java │ ├── properties │ └── IQFeed4jProperties.java │ └── util │ ├── chars │ └── CharUtil.java │ ├── csv │ ├── CSVUtil.java │ └── mapper │ │ ├── AbstractCSVMapper.java │ │ ├── CSVMapping.java │ │ ├── CSVMappingException.java │ │ ├── index │ │ ├── AbstractIndexCSVMapper.java │ │ ├── DirectIndexCSVMapper.java │ │ ├── IndexCSVMapper.java │ │ └── TrailingIndexCSVMapper.java │ │ ├── list │ │ ├── AbstractListCSVMapper.java │ │ ├── DirectListCSVMapper.java │ │ ├── ListCSVMapper.java │ │ └── NestedListCSVMapper.java │ │ └── map │ │ └── NamedCSVMapper.java │ ├── docs │ └── Documentation2JSONSchema.java │ ├── map │ └── MapUtil.java │ ├── properties │ └── PropertyUtil.java │ ├── split │ └── SplitUtil.java │ ├── string │ └── LineEnding.java │ ├── tradecondition │ └── TradeConditionUtil.java │ └── xml │ └── XMLUtil.java └── resources └── iqfeed4j.default.properties /.gitattributes: -------------------------------------------------------------------------------- 1 | # https://www.gitattributes.io/api/java 2 | # 3 | # Handle line endings automatically for files detected as text 4 | # and leave all files detected as binary untouched. 5 | * text=auto 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | # These files are text and should be normalized (Convert crlf => lf) 11 | *.css text 12 | *.df text 13 | *.htm text 14 | *.html text 15 | *.java text 16 | *.js text 17 | *.json text 18 | *.jsp text 19 | *.jspf text 20 | *.jspx text 21 | *.properties text 22 | *.sh text 23 | *.tld text 24 | *.txt text 25 | *.tag text 26 | *.tagx text 27 | *.xml text 28 | *.yml text 29 | 30 | # Declare files that will always have CRLF line endings on checkout. 31 | *.sln text eol=crlf 32 | *.bat text eol=crlf 33 | 34 | # These files are binary and should be left untouched 35 | # (binary is a macro for -text -diff) 36 | *.class binary 37 | *.dll binary 38 | *.ear binary 39 | *.gif binary 40 | *.ico binary 41 | *.jar binary 42 | *.jpg binary 43 | *.jpeg binary 44 | *.png binary 45 | *.so binary 46 | *.war binary 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | build/ 3 | .gradle/ 4 | .settings 5 | .DS_Store 6 | .idea/ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Jacob Peterson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal 2 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import org.jsonschema2pojo.gradle.GenerateJsonSchemaJavaTask 2 | 3 | buildscript { 4 | repositories { 5 | mavenCentral() 6 | } 7 | dependencies { 8 | // For JSONSchema2POJO 9 | classpath group: 'org.jsonschema2pojo', name: 'jsonschema2pojo-gradle-plugin', version: 'latest.integration' 10 | } 11 | } 12 | 13 | plugins { 14 | id "java" 15 | id "java-library" 16 | 17 | // For Maven Central publishing 18 | id "maven-publish" 19 | id "signing" 20 | } 21 | 22 | // 'apply' here instead of in plugins closure because 'jsonschema2pojo' doesn't exist in the 23 | // Gradle Central Plugin Repository (it's only in Maven central). 24 | apply plugin: 'jsonschema2pojo' 25 | 26 | final def projectGroup = "net.jacobpeterson" 27 | final def projectArtifactID = "iqfeed4j" 28 | final def projectVersion = "6.2-2.0.0-SNAPSHOT" 29 | 30 | group = projectGroup 31 | version = projectVersion 32 | 33 | repositories { 34 | mavenCentral() 35 | } 36 | 37 | dependencies { 38 | // Logging framework 39 | implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.36' 40 | 41 | // Google Guava for a variety of useful methods 42 | implementation group: 'com.google.guava', name: 'guava', version: '31.1-jre' 43 | 44 | // Jackson for XML <-> POJO (de)serialization 45 | // Note: not using JAXB since it's slower and Jackson XML has more features 46 | implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: '2.13.3' 47 | implementation group: 'org.codehaus.woodstox', name: 'woodstox-core-asl', version: '4.4.1' 48 | // For JDK 8 common Jackson (de)serializers 49 | implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.13.3' 50 | 51 | // Apache Commons object pooling 52 | implementation group: 'org.apache.commons', name: 'commons-pool2', version: '2.11.1' 53 | } 54 | 55 | java { 56 | sourceCompatibility = JavaVersion.VERSION_17 57 | targetCompatibility = JavaVersion.VERSION_17 58 | withJavadocJar() 59 | withSourcesJar() 60 | } 61 | [compileJava, compileTestJava]*.options*.encoding = "UTF-8" 62 | 63 | javadoc { 64 | options.addStringOption("source", "17") 65 | options.addStringOption("charset", "UTF-8") 66 | options.addStringOption("Xdoclint:none", "-quiet") // Suppress Javadoc linting warnings 67 | options.addStringOption("link", "https://docs.oracle.com/en/java/javase/17/docs/api/") 68 | } 69 | 70 | // 71 | // BEGIN Publishing 72 | // 73 | 74 | publishing { 75 | publications { 76 | mavenJava(MavenPublication) { 77 | groupId = projectGroup 78 | artifactId = projectArtifactID 79 | version = projectVersion 80 | from(components["java"]) 81 | pom { 82 | name = projectArtifactID 83 | description = "A Java API for the market data vendor DTN IQFeed." 84 | url = "https://github.com/Petersoj/IQFeed4j" 85 | inceptionYear = "2021" 86 | licenses { 87 | license { 88 | name = "MIT License" 89 | url = "https://opensource.org/licenses/MIT" 90 | } 91 | } 92 | developers { 93 | developer { 94 | id = "Petersoj" 95 | name = "Jacob Peterson" 96 | } 97 | } 98 | scm { 99 | url = "https://github.com/Petersoj/IQFeed4j.git" 100 | connection = "scm:git:https://github.com/Petersoj/IQFeed4j.git" 101 | developerConnection = "scm:git:git@github.com/Petersoj/IQFeed4j.git" 102 | } 103 | } 104 | } 105 | } 106 | repositories { 107 | maven { 108 | name = "OSSRH" 109 | url = projectVersion.contains("SNAPSHOT") ? "https://oss.sonatype.org/content/repositories/snapshots/" : 110 | "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 111 | credentials { 112 | username = project.property("nexus.username") 113 | password = project.property("nexus.password") 114 | } 115 | } 116 | } 117 | } 118 | signing { 119 | sign publishing.publications.mavenJava 120 | } 121 | 122 | // 123 | // END Publishing 124 | // 125 | 126 | // 127 | // START POJO generation 128 | // 129 | 130 | // Disable default 'jsonSchema2Pojo' task 131 | tasks.getByName("generateJsonSchema2Pojo").enabled(false) 132 | 133 | // The generated POJOs will be in a package structure analogous to the path in the 'schema_json/' directory 134 | // See: https://github.com/joelittlejohn/jsonschema2pojo/wiki/Reference 135 | 136 | final def targetDirectoryPath = file("${project.buildDir}/generated-sources/schemajson").getPath() 137 | 138 | task generatePOJOs() { 139 | final def jsonSourceDirectory = file("${project.projectDir}/schema_json").getPath() 140 | final def jsonPackageNameStart = 'net.jacobpeterson.iqfeed4j.model' 141 | 142 | // Loop through all files in schema JSON file tree 143 | file(jsonSourceDirectory).eachFileRecurse { jsonFile -> 144 | if (jsonFile.getName().endsWith('.json')) { 145 | def startPackageIndex = jsonFile.getAbsolutePath().indexOf(jsonSourceDirectory) + 146 | jsonSourceDirectory.length() 147 | def targetPackage = jsonPackageNameStart + jsonFile.getParentFile().getAbsolutePath() 148 | .substring(startPackageIndex) 149 | .replace(File.separator, '.').replace('-', '').replace('_', '') 150 | .toLowerCase() 151 | 152 | def jsonToPOJOTask = tasks.create('json2POJOTask-' + targetPackage + '.' + jsonFile.getName(), 153 | GenerateJsonSchemaJavaTask) 154 | 155 | // Configure this task to participate in incremental builds so that it only executes when changes occur 156 | jsonToPOJOTask.configure { 157 | inputs.file(jsonFile) 158 | outputs.dir(targetDirectoryPath) 159 | } 160 | 161 | jsonToPOJOTask.doFirst { 162 | jsonToPOJOTask.configuration.source = files(jsonFile.getAbsolutePath()) 163 | jsonToPOJOTask.configuration.targetDirectory = file(targetDirectoryPath) 164 | jsonToPOJOTask.configuration.targetPackage = targetPackage 165 | } 166 | 167 | dependsOn jsonToPOJOTask 168 | } 169 | } 170 | } 171 | 172 | compileJava { 173 | dependsOn generatePOJOs 174 | } 175 | 176 | sourceSets { 177 | main { 178 | java { 179 | srcDir targetDirectoryPath 180 | } 181 | } 182 | } 183 | 184 | jsonSchema2Pojo { 185 | includeAdditionalProperties = false 186 | targetDirectory = file(targetDirectoryPath) 187 | propertyWordDelimiters = ['-', '_'] as char[] 188 | annotationStyle = 'none' 189 | sourceType = 'jsonschema' 190 | customDateTimePattern = 'yyyy-MM-ddTHH:mm:ssZ' 191 | includeConstructors = true 192 | serializable = true 193 | includeGetters = true 194 | includeSetters = true 195 | } 196 | 197 | javadocJar.dependsOn generatePOJOs 198 | sourcesJar.dependsOn generatePOJOs 199 | jar.dependsOn compileJava 200 | javadocJar.dependsOn compileJava 201 | sourcesJar.dependsOn compileJava 202 | tasks.jar { 203 | manifest { 204 | attributes(Map.of("Automatic-Module-Name", "$projectGroup.$projectArtifactID")) 205 | } 206 | } 207 | 208 | // 209 | // END POJO generation 210 | // 211 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/IQFeed4j/b4f524bc720cdfc2dc3d218345d073da6204806b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /schema_json/feed/common/enums/feed_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "System commands that are sent to IQFeed.", 4 | "enum": [ 5 | "S", 6 | "SET PROTOCOL", 7 | "SET CLIENT NAME" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "SYSTEM" 12 | }, 13 | { 14 | "name": "SET_PROTOCOL" 15 | }, 16 | { 17 | "name": "SET_CLIENT_NAME" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /schema_json/feed/common/enums/feed_message_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Message types that are received from IQFeed.", 4 | "enum": [ 5 | "S", 6 | "E", 7 | "CURRENT PROTOCOL" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "SYSTEM" 12 | }, 13 | { 14 | "name": "ERROR" 15 | }, 16 | { 17 | "name": "CURRENT_PROTOCOL" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /schema_json/feed/common/enums/feed_special_message.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Request errors that are received from IQFeed.", 4 | "enum": [ 5 | "!NO_DATA!", 6 | "!SYNTAX_ERROR!", 7 | "!ENDMSG!" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "NO_DATA_ERROR" 12 | }, 13 | { 14 | "name": "SYNTAX_ERROR" 15 | }, 16 | { 17 | "name": "END_OF_MESSAGE" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /schema_json/feed/common/interval/interval_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for Interval types.", 4 | "enum": [ 5 | "s", 6 | "v", 7 | "t" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "SECONDS" 12 | }, 13 | { 14 | "name": "VOLUME" 15 | }, 16 | { 17 | "name": "TICKS" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /schema_json/feed/common/message/message_line.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Represents a message line.", 4 | "properties": { 5 | "Line": { 6 | "existingJavaType": "java.lang.String" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/historical/dated_interval.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Interval POJO for HDX, HDT, HWX, and HMX requests. See Historical via TCP/IP", 4 | "properties": { 5 | "Date": { 6 | "existingJavaType": "java.time.LocalDate", 7 | "title": "The date." 8 | }, 9 | "High": { 10 | "existingJavaType": "java.lang.Double", 11 | "title": "The high." 12 | }, 13 | "Low": { 14 | "existingJavaType": "java.lang.Double", 15 | "title": "The low." 16 | }, 17 | "Open": { 18 | "existingJavaType": "java.lang.Double", 19 | "title": "The open." 20 | }, 21 | "Close": { 22 | "existingJavaType": "java.lang.Double", 23 | "title": "The close." 24 | }, 25 | "PeriodVolume": { 26 | "existingJavaType": "java.lang.Long", 27 | "title": "The volume in the period." 28 | }, 29 | "OpenInterest": { 30 | "existingJavaType": "java.lang.Integer", 31 | "title": "The open interest." 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/historical/enums/data_direction.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for sorting directions.", 4 | "enum": [ 5 | "0", 6 | "1" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "NEWEST_TO_OLDEST" 11 | }, 12 | { 13 | "name": "OLDEST_TO_NEWEST" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/historical/enums/historical_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Historical feed commands. See Historical via TCP/IP", 4 | "enum": [ 5 | "HTX", 6 | "HTD", 7 | "HTT", 8 | "HIX", 9 | "HID", 10 | "HIT", 11 | "HDX", 12 | "HDT", 13 | "HWX", 14 | "HMX" 15 | ], 16 | "javaEnums": [ 17 | { 18 | "name": "HISTORICAL_TICKS_DATAPOINTS" 19 | }, 20 | { 21 | "name": "HISTORICAL_TICKS_DAYS" 22 | }, 23 | { 24 | "name": "HISTORICAL_TICKS_DATETIMES" 25 | }, 26 | { 27 | "name": "HISTORICAL_INTERVAL_DATAPOINTS" 28 | }, 29 | { 30 | "name": "HISTORICAL_INTERVAL_DAYS" 31 | }, 32 | { 33 | "name": "HISTORICAL_INTERVAL_DATETIMES" 34 | }, 35 | { 36 | "name": "HISTORICAL_DAILY_DATAPOINTS" 37 | }, 38 | { 39 | "name": "HISTORICAL_DAILY_DATES" 40 | }, 41 | { 42 | "name": "HISTORICAL_WEEKLY_DATAPOINTS" 43 | }, 44 | { 45 | "name": "HISTORICAL_MONTHLY_DATAPOINTS" 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/historical/enums/partial_datapoint.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for partial datapoint inclusion.", 4 | "enum": [ 5 | "0", 6 | "1" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "EXCLUDE" 11 | }, 12 | { 13 | "name": "INCLUDE" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/historical/enums/time_label_placement.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for the placement of the time label.", 4 | "enum": [ 5 | "0", 6 | "1" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "END" 11 | }, 12 | { 13 | "name": "BEGINNING" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/historical/interval.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Interval POJO for HIX, HID, and HIT requests.\nSee Historical via TCP/IP", 4 | "properties": { 5 | "Timestamp": { 6 | "existingJavaType": "java.time.LocalDateTime", 7 | "title": "The timestamp." 8 | }, 9 | "High": { 10 | "existingJavaType": "java.lang.Double", 11 | "title": "The high." 12 | }, 13 | "Low": { 14 | "existingJavaType": "java.lang.Double", 15 | "title": "The low." 16 | }, 17 | "Open": { 18 | "existingJavaType": "java.lang.Double", 19 | "title": "The open." 20 | }, 21 | "Close": { 22 | "existingJavaType": "java.lang.Double", 23 | "title": "The close." 24 | }, 25 | "TotalVolume": { 26 | "existingJavaType": "java.lang.Long", 27 | "title": "The total volume." 28 | }, 29 | "PeriodVolume": { 30 | "existingJavaType": "java.lang.Long", 31 | "title": "The volume in the period." 32 | }, 33 | "NumberOfTrades": { 34 | "existingJavaType": "java.lang.Long", 35 | "title": "The number of trades. Will be zero for all requests other than tick interval requests." 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/historical/tick.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "POJO for HTX, HTD, and HTT requests. See HistoricalViaTCPIP", 4 | "properties": { 5 | "Timestamp": { 6 | "existingJavaType": "java.time.LocalDateTime", 7 | "title": "The timestamp." 8 | }, 9 | "Last": { 10 | "existingJavaType": "java.lang.Double", 11 | "title": "The last price." 12 | }, 13 | "LastSize": { 14 | "existingJavaType": "java.lang.Long", 15 | "title": "The last size." 16 | }, 17 | "TotalVolume": { 18 | "existingJavaType": "java.lang.Long", 19 | "title": "The total volume." 20 | }, 21 | "Bid": { 22 | "existingJavaType": "java.lang.Double", 23 | "title": "The bid price." 24 | }, 25 | "Ask": { 26 | "existingJavaType": "java.lang.Double", 27 | "title": "The ask price." 28 | }, 29 | "TickID": { 30 | "existingJavaType": "java.lang.Long", 31 | "title": "The tick ID." 32 | }, 33 | "BasisForLast": { 34 | "type": "string", 35 | "enum": [ 36 | "C", 37 | "E", 38 | "O", 39 | "S" 40 | ], 41 | "javaEnums": [ 42 | { 43 | "name": "LAST_QUALIFIED_TRADE" 44 | }, 45 | { 46 | "name": "EXTENDED_TRADE" 47 | }, 48 | { 49 | "name": "OTHER_TRADE" 50 | }, 51 | { 52 | "name": "SETTLE" 53 | } 54 | ], 55 | "title": "The basis for last trade." 56 | }, 57 | "TradeMarketCenter": { 58 | "existingJavaType": "java.lang.Short", 59 | "title": "The Market Center the trade occurred at." 60 | }, 61 | "TradeConditions": { 62 | "existingJavaType": "java.util.List", 63 | "title": "The trade conditions. Format: One to four, 2 digit hex numbers." 64 | }, 65 | "TradeAggressor": { 66 | "type": "string", 67 | "enum": [ 68 | "0", 69 | "1", 70 | "2", 71 | "3" 72 | ], 73 | "javaEnums": [ 74 | { 75 | "name": "INVALID" 76 | }, 77 | { 78 | "name": "BUY" 79 | }, 80 | { 81 | "name": "SELL" 82 | }, 83 | { 84 | "name": "NEITHER_BUY_NOR_SELL" 85 | } 86 | ], 87 | "title": "The trade aggressor." 88 | }, 89 | "DayCode": { 90 | "existingJavaType": "java.lang.Integer", 91 | "title": "The day of month the trade applies to." 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/market_summary/end_of_day_snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "POJO for EDS (End of Day Summary) requests. See Market Summary via TCP/IP", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String" 7 | }, 8 | "Exchange": { 9 | "existingJavaType": "java.lang.Integer" 10 | }, 11 | "Type": { 12 | "existingJavaType": "java.lang.Integer" 13 | }, 14 | "Last": { 15 | "existingJavaType": "java.lang.Double" 16 | }, 17 | "TradeSize": { 18 | "existingJavaType": "java.lang.Double" 19 | }, 20 | "TradedMarket": { 21 | "existingJavaType": "java.lang.Integer" 22 | }, 23 | "TradeDate": { 24 | "existingJavaType": "java.time.LocalDate" 25 | }, 26 | "TradeTime": { 27 | "existingJavaType": "java.time.LocalTime" 28 | }, 29 | "Open": { 30 | "existingJavaType": "java.lang.Double" 31 | }, 32 | "High": { 33 | "existingJavaType": "java.lang.Double" 34 | }, 35 | "Low": { 36 | "existingJavaType": "java.lang.Double" 37 | }, 38 | "Close": { 39 | "existingJavaType": "java.lang.Double" 40 | }, 41 | "Bid": { 42 | "existingJavaType": "java.lang.Double" 43 | }, 44 | "BidMarket": { 45 | "existingJavaType": "java.lang.Integer" 46 | }, 47 | "BidSize": { 48 | "existingJavaType": "java.lang.Double" 49 | }, 50 | "Ask": { 51 | "existingJavaType": "java.lang.Double" 52 | }, 53 | "AskMarket": { 54 | "existingJavaType": "java.lang.Integer" 55 | }, 56 | "AskSize": { 57 | "existingJavaType": "java.lang.Double" 58 | }, 59 | "Volume": { 60 | "existingJavaType": "java.lang.Double" 61 | }, 62 | "PDayVolume": { 63 | "existingJavaType": "java.lang.Double" 64 | }, 65 | "UpVolume": { 66 | "existingJavaType": "java.lang.Double" 67 | }, 68 | "DownVolume": { 69 | "existingJavaType": "java.lang.Double" 70 | }, 71 | "NeutralVolume": { 72 | "existingJavaType": "java.lang.Double" 73 | }, 74 | "TradeCount": { 75 | "existingJavaType": "java.lang.Double" 76 | }, 77 | "UpTrades": { 78 | "existingJavaType": "java.lang.Double" 79 | }, 80 | "DownTrades": { 81 | "existingJavaType": "java.lang.Double" 82 | }, 83 | "NeutralTrades": { 84 | "existingJavaType": "java.lang.Double" 85 | }, 86 | "VWAP": { 87 | "existingJavaType": "java.lang.Double" 88 | }, 89 | "MutualDiv": { 90 | "existingJavaType": "java.lang.Double" 91 | }, 92 | "SevenDayYield": { 93 | "existingJavaType": "java.lang.Double" 94 | }, 95 | "OpenInterest": { 96 | "existingJavaType": "java.lang.Double" 97 | }, 98 | "Settlement": { 99 | "existingJavaType": "java.lang.Double" 100 | }, 101 | "SettlementDate": { 102 | "existingJavaType": "java.time.LocalDate" 103 | }, 104 | "ExpirationDate": { 105 | "existingJavaType": "java.time.LocalDate" 106 | }, 107 | "Strike": { 108 | "existingJavaType": "java.lang.Double" 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/market_summary/enums/market_summary_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Market Summary feed commands. See Market Summary via TCP/IP", 4 | "enum": [ 5 | "EDS", 6 | "FDS", 7 | "5MS" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "END_OF_DAY_SUMMARY" 12 | }, 13 | { 14 | "name": "FUNDAMENTAL_SUMMARY" 15 | }, 16 | { 17 | "name": "FIVE_MINUTE_SNAPSHOT" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/market_summary/five_minute_snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "POJO for 5MS (5 Minute Snapshot Summary) requests. See Market Summary via TCP/IP", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String" 7 | }, 8 | "Exchange": { 9 | "existingJavaType": "java.lang.Integer" 10 | }, 11 | "Type": { 12 | "existingJavaType": "java.lang.Integer" 13 | }, 14 | "Last": { 15 | "existingJavaType": "java.lang.Double" 16 | }, 17 | "TradeSize": { 18 | "existingJavaType": "java.lang.Double" 19 | }, 20 | "TradedMarket": { 21 | "existingJavaType": "java.lang.Integer" 22 | }, 23 | "TradeDate": { 24 | "existingJavaType": "java.time.LocalDate" 25 | }, 26 | "TradeTime": { 27 | "existingJavaType": "java.time.LocalTime" 28 | }, 29 | "Open": { 30 | "existingJavaType": "java.lang.Double" 31 | }, 32 | "High": { 33 | "existingJavaType": "java.lang.Double" 34 | }, 35 | "Low": { 36 | "existingJavaType": "java.lang.Double" 37 | }, 38 | "Close": { 39 | "existingJavaType": "java.lang.Double" 40 | }, 41 | "Bid": { 42 | "existingJavaType": "java.lang.Double" 43 | }, 44 | "BidMarket": { 45 | "existingJavaType": "java.lang.Integer" 46 | }, 47 | "BidSize": { 48 | "existingJavaType": "java.lang.Double" 49 | }, 50 | "Ask": { 51 | "existingJavaType": "java.lang.Double" 52 | }, 53 | "AskMarket": { 54 | "existingJavaType": "java.lang.Integer" 55 | }, 56 | "AskSize": { 57 | "existingJavaType": "java.lang.Double" 58 | }, 59 | "Volume": { 60 | "existingJavaType": "java.lang.Double" 61 | }, 62 | "PDayVolume": { 63 | "existingJavaType": "java.lang.Double" 64 | }, 65 | "UpVolume": { 66 | "existingJavaType": "java.lang.Double" 67 | }, 68 | "DownVolume": { 69 | "existingJavaType": "java.lang.Double" 70 | }, 71 | "NeutralVolume": { 72 | "existingJavaType": "java.lang.Double" 73 | }, 74 | "TradeCount": { 75 | "existingJavaType": "java.lang.Double" 76 | }, 77 | "UpTrades": { 78 | "existingJavaType": "java.lang.Double" 79 | }, 80 | "DownTrades": { 81 | "existingJavaType": "java.lang.Double" 82 | }, 83 | "NeutralTrades": { 84 | "existingJavaType": "java.lang.Double" 85 | }, 86 | "VWAP": { 87 | "existingJavaType": "java.lang.Double" 88 | }, 89 | "MutualDiv": { 90 | "existingJavaType": "java.lang.Double" 91 | }, 92 | "SevenDayYield": { 93 | "existingJavaType": "java.lang.Double" 94 | }, 95 | "OpenInterest": { 96 | "existingJavaType": "java.lang.Double" 97 | }, 98 | "Settlement": { 99 | "existingJavaType": "java.lang.Double" 100 | }, 101 | "SettlementDate": { 102 | "existingJavaType": "java.time.LocalDate" 103 | }, 104 | "ExpirationDate": { 105 | "existingJavaType": "java.time.LocalDate" 106 | }, 107 | "Strike": { 108 | "existingJavaType": "java.lang.Double" 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/market_summary/fundamental_snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "POJO for FDS (Fundamental Summary) requests. See Market Summary via TCP/IP", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String" 7 | }, 8 | "Description": { 9 | "existingJavaType": "java.lang.String" 10 | }, 11 | "PeRatio": { 12 | "existingJavaType": "java.lang.Double" 13 | }, 14 | "AvgVolume": { 15 | "existingJavaType": "java.lang.Double" 16 | }, 17 | "DivYield": { 18 | "existingJavaType": "java.lang.Double" 19 | }, 20 | "DivAmount": { 21 | "existingJavaType": "java.lang.Double" 22 | }, 23 | "DivRate": { 24 | "existingJavaType": "java.lang.Double" 25 | }, 26 | "PayDate": { 27 | "existingJavaType": "java.time.LocalDate" 28 | }, 29 | "ExDivDate": { 30 | "existingJavaType": "java.time.LocalDate" 31 | }, 32 | "CurrentEps": { 33 | "existingJavaType": "java.lang.Double" 34 | }, 35 | "EstEps": { 36 | "existingJavaType": "java.lang.Double" 37 | }, 38 | "SIC": { 39 | "existingJavaType": "java.lang.Integer" 40 | }, 41 | "Precision": { 42 | "existingJavaType": "java.lang.Integer" 43 | }, 44 | "Display": { 45 | "existingJavaType": "java.lang.Double" 46 | }, 47 | "GrowthPercent": { 48 | "existingJavaType": "java.lang.Double" 49 | }, 50 | "FiscalYearEnd": { 51 | "existingJavaType": "java.time.LocalDate" 52 | }, 53 | "Volatility": { 54 | "existingJavaType": "java.lang.Double" 55 | }, 56 | "ListedMarket": { 57 | "existingJavaType": "java.lang.Integer" 58 | }, 59 | "MaturityDate": { 60 | "existingJavaType": "java.time.LocalDate" 61 | }, 62 | "OptionRoots": { 63 | "existingJavaType": "java.lang.String" 64 | }, 65 | "CouponRate": { 66 | "existingJavaType": "java.lang.Double" 67 | }, 68 | "InstitutionalPercent": { 69 | "existingJavaType": "java.lang.Double" 70 | }, 71 | "YearEndClose": { 72 | "existingJavaType": "java.lang.Double" 73 | }, 74 | "Beta": { 75 | "existingJavaType": "java.lang.Double" 76 | }, 77 | "LEAPs": { 78 | "existingJavaType": "java.lang.String" 79 | }, 80 | "WRAPs": { 81 | "existingJavaType": "java.lang.String" 82 | }, 83 | "Assets": { 84 | "existingJavaType": "java.lang.Double" 85 | }, 86 | "Liabilities": { 87 | "existingJavaType": "java.lang.Double" 88 | }, 89 | "BalanceSheetDate": { 90 | "existingJavaType": "java.time.LocalDate" 91 | }, 92 | "LongTermDebt": { 93 | "existingJavaType": "java.lang.Double" 94 | }, 95 | "CommonSharesOutstanding": { 96 | "existingJavaType": "java.lang.Double" 97 | }, 98 | "MarketCap": { 99 | "existingJavaType": "java.lang.Double" 100 | }, 101 | "52WeekHigh": { 102 | "existingJavaType": "java.lang.Double" 103 | }, 104 | "52WeekHighDate": { 105 | "existingJavaType": "java.time.LocalDate" 106 | }, 107 | "52WeekLow": { 108 | "existingJavaType": "java.lang.Double" 109 | }, 110 | "52WeekLowDate": { 111 | "existingJavaType": "java.time.LocalDate" 112 | }, 113 | "CalHigh": { 114 | "existingJavaType": "java.lang.Double" 115 | }, 116 | "CalHighDate": { 117 | "existingJavaType": "java.time.LocalDate" 118 | }, 119 | "CalLow": { 120 | "existingJavaType": "java.lang.Double" 121 | }, 122 | "CalLowDate": { 123 | "existingJavaType": "java.time.LocalDate" 124 | }, 125 | "Expiration": { 126 | "existingJavaType": "java.lang.String" 127 | }, 128 | "LastSplit": { 129 | "existingJavaType": "java.lang.Double" 130 | }, 131 | "LastSplitDate": { 132 | "existingJavaType": "java.time.LocalDate" 133 | }, 134 | "PrevSplit": { 135 | "existingJavaType": "java.lang.Double" 136 | }, 137 | "PrevSplitDate": { 138 | "existingJavaType": "java.time.LocalDate" 139 | }, 140 | "NAICS": { 141 | "existingJavaType": "java.lang.Integer" 142 | }, 143 | "ShortInterest": { 144 | "existingJavaType": "java.lang.Double" 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/news/enums/news_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for News feed commands. See News Lookup via TCP/IP", 4 | "enum": [ 5 | "NCG", 6 | "NHL", 7 | "NSY", 8 | "NSC" 9 | ], 10 | "javaEnums": [ 11 | { 12 | "name": "NEWS_CONFIGURATION" 13 | }, 14 | { 15 | "name": "NEWS_HEADLINE" 16 | }, 17 | { 18 | "name": "NEWS_STORY" 19 | }, 20 | { 21 | "name": "NEWS_STORY_COUNT" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/news/enums/news_configuration_message_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for news configuration types.", 4 | "enum": [ 5 | "CATEGORY", 6 | "MAJOR", 7 | "MINOR" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/news/enums/xml_text_email_option.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for XML or Text options.", 4 | "javaName": "XMLTextEmailOption", 5 | "enum": [ 6 | "x", 7 | "t", 8 | "e" 9 | ], 10 | "javaEnums": [ 11 | { 12 | "name": "XML" 13 | }, 14 | { 15 | "name": "TEXT" 16 | }, 17 | { 18 | "name": "EMAIL" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/news/enums/xml_text_option.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for XML or Text options.", 4 | "javaName": "XMLTextOption", 5 | "enum": [ 6 | "x", 7 | "t" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "XML" 12 | }, 13 | { 14 | "name": "TEXT" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/news/news_configuration_message.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "POJO for NCG (News Configuration) requests. See News Lookup via TCP/IP", 4 | "properties": { 5 | "Type": { 6 | "existingJavaType": "net.jacobpeterson.iqfeed4j.model.feed.lookup.news.enums.NewsConfigurationMessageType" 7 | }, 8 | "Category": { 9 | "existingJavaType": "java.lang.String", 10 | "title": "Only present for {@link net.jacobpeterson.iqfeed4j.model.feed.lookup.news.enums.NewsConfigurationMessageType#CATEGORY}." 11 | }, 12 | "Source": { 13 | "existingJavaType": "java.lang.String" 14 | }, 15 | "Description": { 16 | "existingJavaType": "java.lang.String" 17 | }, 18 | "AuthCode": { 19 | "existingJavaType": "java.lang.String" 20 | }, 21 | "IconID": { 22 | "existingJavaType": "java.lang.String" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/enums/equity_option_month.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for month codes for Equity Call Option chains.", 4 | "enum": [ 5 | "A", 6 | "B", 7 | "C", 8 | "D", 9 | "E", 10 | "F", 11 | "G", 12 | "H", 13 | "I", 14 | "J", 15 | "K", 16 | "L", 17 | "M", 18 | "N", 19 | "O", 20 | "P", 21 | "Q", 22 | "R", 23 | "S", 24 | "T", 25 | "U", 26 | "V", 27 | "W", 28 | "X" 29 | ], 30 | "javaEnums": [ 31 | { 32 | "name": "JANUARY_CALL" 33 | }, 34 | { 35 | "name": "FEBRUARY_CALL" 36 | }, 37 | { 38 | "name": "MARCH_CALL" 39 | }, 40 | { 41 | "name": "APRIL_CALL" 42 | }, 43 | { 44 | "name": "MAY_CALL" 45 | }, 46 | { 47 | "name": "JUNE_CALL" 48 | }, 49 | { 50 | "name": "JULY_CALL" 51 | }, 52 | { 53 | "name": "AUGUST_CALL" 54 | }, 55 | { 56 | "name": "SEPTEMBER_CALL" 57 | }, 58 | { 59 | "name": "OCTOBER_CALL" 60 | }, 61 | { 62 | "name": "NOVEMBER_CALL" 63 | }, 64 | { 65 | "name": "DECEMBER_CALL" 66 | }, 67 | { 68 | "name": "JANUARY_PUT" 69 | }, 70 | { 71 | "name": "FEBRUARY_PUT" 72 | }, 73 | { 74 | "name": "MARCH_PUT" 75 | }, 76 | { 77 | "name": "APRIL_PUT" 78 | }, 79 | { 80 | "name": "MAY_PUT" 81 | }, 82 | { 83 | "name": "JUNE_PUT" 84 | }, 85 | { 86 | "name": "JULY_PUT" 87 | }, 88 | { 89 | "name": "AUGUST_PUT" 90 | }, 91 | { 92 | "name": "SEPTEMBER_PUT" 93 | }, 94 | { 95 | "name": "OCTOBER_PUT" 96 | }, 97 | { 98 | "name": "NOVEMBER_PUT" 99 | }, 100 | { 101 | "name": "DECEMBER_PUT" 102 | } 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/enums/future_month.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for month codes for Future Option chains.", 4 | "enum": [ 5 | "F", 6 | "G", 7 | "H", 8 | "J", 9 | "K", 10 | "M", 11 | "N", 12 | "Q", 13 | "U", 14 | "V", 15 | "X", 16 | "Z" 17 | ], 18 | "javaEnums": [ 19 | { 20 | "name": "JANUARY" 21 | }, 22 | { 23 | "name": "FEBRUARY" 24 | }, 25 | { 26 | "name": "MARCH" 27 | }, 28 | { 29 | "name": "APRIL" 30 | }, 31 | { 32 | "name": "MAY" 33 | }, 34 | { 35 | "name": "JUNE" 36 | }, 37 | { 38 | "name": "JULY" 39 | }, 40 | { 41 | "name": "AUGUST" 42 | }, 43 | { 44 | "name": "SEPTEMBER" 45 | }, 46 | { 47 | "name": "OCTOBER" 48 | }, 49 | { 50 | "name": "NOVEMBER" 51 | }, 52 | { 53 | "name": "DECEMBER" 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/enums/non_standard_option_types.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for inclusion of non-standard Option contract types.", 4 | "enum": [ 5 | "0", 6 | "1" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "EXCLUDE" 11 | }, 12 | { 13 | "name": "INCLUDE" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/enums/option_chains_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Option Chains feed commands. See Option Chains via TCP/IP", 4 | "enum": [ 5 | "CFU", 6 | "CFS", 7 | "CFO", 8 | "CEO" 9 | ], 10 | "javaEnums": [ 11 | { 12 | "name": "FUTURE_CHAIN" 13 | }, 14 | { 15 | "name": "FUTURE_SPREAD_CHAIN" 16 | }, 17 | { 18 | "name": "FUTURE_OPTION_CHAIN" 19 | }, 20 | { 21 | "name": "EQUITY_OPTION_CHAIN" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/enums/option_filter_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for Option contract filter types.", 4 | "enum": [ 5 | "0", 6 | "1", 7 | "2" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "NONE" 12 | }, 13 | { 14 | "name": "STRIKE_RANGE" 15 | }, 16 | { 17 | "name": "IN_OR_OUT_OF_THE_MONEY" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/enums/option_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for Option contract types.", 4 | "enum": [ 5 | "P", 6 | "C" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "PUT" 11 | }, 12 | { 13 | "name": "CALL" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/enums/puts_calls_option.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for Option contract types.", 4 | "enum": [ 5 | "p", 6 | "c", 7 | "pc" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "PUTS" 12 | }, 13 | { 14 | "name": "CALLS" 15 | }, 16 | { 17 | "name": "PUTS_AND_CALLS" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/future_contract.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "POJO for Future contracts. See Option Chains via TCP/IP", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String", 7 | "title": "The symbol." 8 | }, 9 | "Date": { 10 | "existingJavaType": "java.time.LocalDate", 11 | "title": "The date." 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/future_spread.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "POJO for a Future spread. See Option Chains via TCP/IP", 4 | "properties": { 5 | "From": { 6 | "existingJavaType": "net.jacobpeterson.iqfeed4j.model.feed.lookup.optionchains.FutureContract", 7 | "title": "The 'From' Future." 8 | }, 9 | "To": { 10 | "existingJavaType": "net.jacobpeterson.iqfeed4j.model.feed.lookup.optionchains.FutureContract", 11 | "title": "The 'To' Future." 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/option_chains/option_contract.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "POJO for Option contracts. See Option Chains via TCP/IP", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String", 7 | "title": "The symbol." 8 | }, 9 | "ExpirationDate": { 10 | "existingJavaType": "java.time.LocalDate", 11 | "title": "The expiration date." 12 | }, 13 | "OptionType": { 14 | "existingJavaType": "net.jacobpeterson.iqfeed4j.model.feed.lookup.optionchains.enums.OptionType", 15 | "title": "The type of Option contract." 16 | }, 17 | "StrikePrice": { 18 | "existingJavaType": "java.lang.Double", 19 | "title": "The strike price." 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/enums/search_code_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for request types of search codes.", 4 | "enum": [ 5 | "SBS", 6 | "SBN" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "SIC" 11 | }, 12 | { 13 | "name": "NIAC" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/enums/search_field.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for search fields.", 4 | "enum": [ 5 | "s", 6 | "d" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "SYMBOLS" 11 | }, 12 | { 13 | "name": "DESCRIPTIONS" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/enums/symbol_filter_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for symbol filter types.", 4 | "enum": [ 5 | "e", 6 | "t" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "LISTED_MARKETS" 11 | }, 12 | { 13 | "name": "SECURITY_TYPES" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/enums/symbol_market_info_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Symbol Market Info feed commands. See Symbol Lookup via TCP/IP", 4 | "enum": [ 5 | "SBF", 6 | "SBS", 7 | "SBN", 8 | "SLM", 9 | "SST", 10 | "STC", 11 | "SSC", 12 | "SNC" 13 | ], 14 | "javaEnums": [ 15 | { 16 | "name": "SYMBOLS_BY_FILTER" 17 | }, 18 | { 19 | "name": "SYMBOLS_BY_SIC_CODE" 20 | }, 21 | { 22 | "name": "SYMBOLS_BY_NIAC_CODE" 23 | }, 24 | { 25 | "name": "LISTED_MARKETS" 26 | }, 27 | { 28 | "name": "SECURITY_TYPES" 29 | }, 30 | { 31 | "name": "TRADE_CONDITIONS" 32 | }, 33 | { 34 | "name": "SIC_CODES" 35 | }, 36 | { 37 | "name": "NIAC_CODES" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/listed_market.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Listed market POJO for SLM requests. See Symbol Lookup via TCP/IP", 4 | "properties": { 5 | "ListedMarketID": { 6 | "existingJavaType": "java.lang.Integer", 7 | "title": "Numeric whole number referencing which market the symbol is listed on." 8 | }, 9 | "ShortName": { 10 | "existingJavaType": "java.lang.String", 11 | "title": "The short name." 12 | }, 13 | "LongName": { 14 | "existingJavaType": "java.lang.String", 15 | "title": "The long name." 16 | }, 17 | "GroupID": { 18 | "existingJavaType": "java.lang.Integer", 19 | "title": "The group ID." 20 | }, 21 | "ShortGroupName": { 22 | "existingJavaType": "java.lang.String", 23 | "title": "The short group name." 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/niac_code.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "NIAC code POJO for SNC requests. See Symbol Lookup via TCP/IP", 4 | "javaName": "NIACCode", 5 | "properties": { 6 | "NIACCode": { 7 | "existingJavaType": "java.lang.Integer", 8 | "title": "Numeric Whole number referencing which NIAC code the symbol is listed under." 9 | }, 10 | "Description": { 11 | "existingJavaType": "java.lang.String", 12 | "title": "The description." 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/security_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Security types POJO for SST requests. See Symbol Lookup via TCP/IP", 4 | "properties": { 5 | "SecurityTypeID": { 6 | "existingJavaType": "java.lang.Integer", 7 | "title": "Numeric whole number referencing which security type the symbol falls under." 8 | }, 9 | "ShortName": { 10 | "existingJavaType": "java.lang.String", 11 | "title": "The short name." 12 | }, 13 | "LongName": { 14 | "existingJavaType": "java.lang.String", 15 | "title": "The long name." 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/sic_code.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "SIC code POJO for SSC requests. See Symbol Lookup via TCP/IP", 4 | "javaName": "SICCode", 5 | "properties": { 6 | "SICCode": { 7 | "existingJavaType": "java.lang.Integer", 8 | "title": "Numeric Whole number referencing which SIC code the symbol is listed under." 9 | }, 10 | "Description": { 11 | "existingJavaType": "java.lang.String", 12 | "title": "The description." 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/symbol_search_result.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Symbol search result POJO for SBF, SBS, and SBN requests. See Symbol Lookup via TCP/IP", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String", 7 | "title": "All characters uppercase. Can include Symbols such as (but not limited to) @+-=.# or spaces." 8 | }, 9 | "ListedMarketID": { 10 | "existingJavaType": "java.lang.Integer", 11 | "title": "Numeric whole number referencing which market the symbol is listed on. Valid values can be acquired from the SLM command." 12 | }, 13 | "SecurityTypeID": { 14 | "existingJavaType": "java.lang.Integer", 15 | "title": "Numeric whole number referencing which security type the symbol falls under. Valid values can be acquired from the SST command." 16 | }, 17 | "Description": { 18 | "existingJavaType": "java.lang.String", 19 | "title": "The name of the company or a description of what the symbol represents." 20 | }, 21 | "SICCode": { 22 | "existingJavaType": "java.lang.Integer", 23 | "title": "Numeric Whole number referencing which SIC code the symbol is listed under. Valid values can be acquired from the SSC command. Note: this value is only present for SBS request." 24 | }, 25 | "NIACCode": { 26 | "existingJavaType": "java.lang.Integer", 27 | "title": "Numeric Whole number referencing which NIAC code the symbol is listed under. Valid values can be acquired from the SNC command. Note: this value is only present for SBN request." 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /schema_json/feed/lookup/symbol_market_info/trade_condition.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Trade condition POJO for STC requests. See Symbol Lookup via TCP/IP", 4 | "properties": { 5 | "TradeConditionID": { 6 | "existingJavaType": "java.lang.Integer", 7 | "title": "The trade condition ID in decimal." 8 | }, 9 | "ShortName": { 10 | "existingJavaType": "java.lang.String", 11 | "title": "The short name." 12 | }, 13 | "LongName": { 14 | "existingJavaType": "java.lang.String", 15 | "title": "The long name." 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/admin/client_statistics.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Status message that gives information about each client connected to IQFeed and is received once per second after being turned on. See Admin Port System Messages", 4 | "properties": { 5 | "Type": { 6 | "type": "string", 7 | "enum": [ 8 | "0", 9 | "1", 10 | "2", 11 | "3" 12 | ], 13 | "javaEnums": [ 14 | { 15 | "name": "ADMIN" 16 | }, 17 | { 18 | "name": "LEVEL_1" 19 | }, 20 | { 21 | "name": "LEVEL_2" 22 | }, 23 | { 24 | "name": "LOOKUP" 25 | } 26 | ], 27 | "title": "The type of client." 28 | }, 29 | "ClientID": { 30 | "existingJavaType": "java.lang.Integer", 31 | "title": "Numeric identifier (unique by client type) for each client." 32 | }, 33 | "ClientName": { 34 | "existingJavaType": "java.lang.String", 35 | "title": "Name of the client. This is set by the 3rd party app using the S,SET CLIENT NAME command on the socket connection it is connected to." 36 | }, 37 | "StartTime": { 38 | "existingJavaType": "java.time.LocalDateTime", 39 | "title": "Time the client connected to IQFeed." 40 | }, 41 | "Symbols": { 42 | "existingJavaType": "java.lang.Integer", 43 | "title": "Number of symbols being watched by client. Only valid for Level 1 and Level 2 client types." 44 | }, 45 | "RegionalSymbols": { 46 | "existingJavaType": "java.lang.Integer", 47 | "title": "Number of regional symbols being watched by client. Only valid for Level 1 client type." 48 | }, 49 | "KiloBytesReceived": { 50 | "existingJavaType": "java.lang.Double", 51 | "title": "Number of kilobytes received from the client." 52 | }, 53 | "KiloBytesSent": { 54 | "existingJavaType": "java.lang.Double", 55 | "title": "Number of kilobytes sent to the client." 56 | }, 57 | "KiloBytesQueued": { 58 | "existingJavaType": "java.lang.Double", 59 | "title": "Number of kilobytes queued by IQConnect waiting to be delivered to the client." 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/admin/enums/admin_system_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Admin commands that are sent to IQFeed. See Admin Port System Messages", 4 | "enum": [ 5 | "REGISTER CLIENT APP", 6 | "REMOVE CLIENT APP", 7 | "SET LOGINID", 8 | "SET PASSWORD", 9 | "SET SAVE LOGIN INFO", 10 | "SET AUTOCONNECT", 11 | "CLIENTSTATS ON", 12 | "CLIENTSTATS OFF", 13 | "CONNECT", 14 | "DISCONNECT" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/admin/enums/admin_system_message_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Admin messages that are received from IQFeed. See Admin Port System Messages", 4 | "enum": [ 5 | "REGISTER CLIENT APP COMPLETED", 6 | "REMOVE CLIENT APP COMPLETED", 7 | "CURRENT LOGINID", 8 | "CURRENT PASSWORD", 9 | "LOGIN INFO SAVED", 10 | "LOGIN INFO NOT SAVED", 11 | "AUTOCONNECT ON", 12 | "AUTOCONNECT OFF", 13 | "STATS", 14 | "CLIENTSTATS" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/admin/enums/on_off_option.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Defines enums for on or off.", 4 | "enum": [ 5 | "On", 6 | "Off" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/common/enums/server_connection_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enum for IQFeed server connection status. See IQFeed Documentation", 4 | "enum": [ 5 | "SERVER CONNECTED", 6 | "SERVER DISCONNECTED" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/common/feed_statistics.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Status message that gives information about the feed operation and is received once per second. See Admin Port System Messages", 4 | "properties": { 5 | "ServerIP": { 6 | "existingJavaType": "java.lang.String", 7 | "title": "IP address for least loaded Quote Server." 8 | }, 9 | "ServerPort": { 10 | "existingJavaType": "java.lang.Integer", 11 | "title": "Port for least loaded Quote Server." 12 | }, 13 | "MaxSymbols": { 14 | "existingJavaType": "java.lang.Integer", 15 | "title": "The maximum # of symbols that can be watched at any given time." 16 | }, 17 | "NumberOfSymbols": { 18 | "existingJavaType": "java.lang.Integer", 19 | "title": "The # of symbols that are currently being watched." 20 | }, 21 | "ClientsConnected": { 22 | "existingJavaType": "java.lang.Integer", 23 | "title": "The # of clients that are currently connected." 24 | }, 25 | "SecondsSinceLastUpdate": { 26 | "existingJavaType": "java.lang.Integer", 27 | "title": "The # of seconds since the last update from the Quote server ." 28 | }, 29 | "Reconnections": { 30 | "existingJavaType": "java.lang.Integer", 31 | "title": "The # of times that IQFeed has reconnected." 32 | }, 33 | "AttemptedReconnections": { 34 | "existingJavaType": "java.lang.Integer", 35 | "title": "The # of times that IQFeed has attempted to reconnect, but failed." 36 | }, 37 | "StartTime": { 38 | "existingJavaType": "java.time.LocalDateTime", 39 | "title": "The time that the connection (or reconnection) to IQFeed was made." 40 | }, 41 | "MarketTime": { 42 | "existingJavaType": "java.time.LocalDateTime", 43 | "title": "The current time of the market." 44 | }, 45 | "Status": { 46 | "type": "string", 47 | "enum": [ 48 | "Connected", 49 | "Not Connected" 50 | ], 51 | "title": "Represents whether IQFeed is connected or not." 52 | }, 53 | "IQFeedVersion": { 54 | "existingJavaType": "java.lang.String", 55 | "title": "Represents the version of IQFeed that is running." 56 | }, 57 | "LoginID": { 58 | "existingJavaType": "java.lang.String", 59 | "title": "The Login ID that is currently logged in." 60 | }, 61 | "TotalKiloBytesReceived": { 62 | "existingJavaType": "java.lang.Double", 63 | "title": "Found in the \"Internet Bandwidth\" section of the IQConnection Manager. Formula: total bytes received / 1024." 64 | }, 65 | "KiloBytesPerSecReceived": { 66 | "existingJavaType": "java.lang.Double", 67 | "title": "Found in the \"Internet Bandwidth\" section of the IQConnection Manager. Formula: bytes received in the past second / 1024." 68 | }, 69 | "AvgKiloBytesPerSecRecv": { 70 | "existingJavaType": "java.lang.Double", 71 | "title": "Found in the \"Internet Bandwidth\" section of the IQConnection Manager. Formula: total KB's received / total seconds." 72 | }, 73 | "TotalKiloBytesSent": { 74 | "existingJavaType": "java.lang.Double", 75 | "title": "Found in the \"Local Bandwidth\" section of the IQConnection Manager. Formula: total bytes sent / 1024." 76 | }, 77 | "KiloBytesPerSecSent": { 78 | "existingJavaType": "java.lang.Double", 79 | "title": "Found in the \"Local Bandwidth\" section of the IQConnection Manager. Formula: bytes sent in the past second / 1024." 80 | }, 81 | "AvgKiloBytesPerSecSent": { 82 | "existingJavaType": "java.lang.Double", 83 | "title": "Found in the \"Local Bandwidth\" section of the IQConnection Manager. Formula: total KB's sent / total seconds" 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/derivative/enums/derivative_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Derivative feed commands. See Streaming interval bars via TCP/IP", 4 | "enum": [ 5 | "BW", 6 | "BR" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "BAR_WATCH" 11 | }, 12 | { 13 | "name": "BAR_REMOVE" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/derivative/enums/derivative_message_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Derivative feed message types. See Streaming interval bars via TCP/IP", 4 | "enum": [ 5 | "n" 6 | ], 7 | "javaEnums": [ 8 | { 9 | "name": "SYMBOL_NOT_WATCHED" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/derivative/enums/derivative_system_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Derivative feed system commands. See Streaming interval bars via TCP/IP", 4 | "enum": [ 5 | "REQUEST WATCHES", 6 | "UNWATCH ALL" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/derivative/enums/derivative_system_message_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Derivative feed System message types. See Streaming interval bars via TCP/IP", 4 | "enum": [ 5 | "SYMBOL LIMIT REACHED", 6 | "REPLACED PREVIOUS WATCH", 7 | "WATCHES" 8 | ], 9 | "javaEnums": [ 10 | { 11 | "name": "SYMBOL_LIMIT_REACHED" 12 | }, 13 | { 14 | "name": "REPLACED_PREVIOUSLY_WATCHED_INTERVAL" 15 | }, 16 | { 17 | "name": "WATCHED_INTERVALS" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/derivative/interval.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Represents an interval bar. See Streaming interval bars via TCP/IP", 4 | "properties": { 5 | "UpdateType": { 6 | "type": "string", 7 | "enum": [ 8 | "BU", 9 | "BH", 10 | "BC" 11 | ], 12 | "javaEnums": [ 13 | { 14 | "name": "UPDATED" 15 | }, 16 | { 17 | "name": "COMPLETE_FROM_HISTORY" 18 | }, 19 | { 20 | "name": "COMPLETE_FROM_STREAM" 21 | } 22 | ], 23 | "title": "The update type." 24 | }, 25 | "Symbol": { 26 | "existingJavaType": "java.lang.String", 27 | "title": "The symbol." 28 | }, 29 | "Timestamp": { 30 | "existingJavaType": "java.time.LocalDateTime", 31 | "title": "Date/time of the interval." 32 | }, 33 | "Open": { 34 | "existingJavaType": "java.lang.Double", 35 | "title": "First price in the interval." 36 | }, 37 | "High": { 38 | "existingJavaType": "java.lang.Double", 39 | "title": "Highest price in the interval." 40 | }, 41 | "Low": { 42 | "existingJavaType": "java.lang.Double", 43 | "title": "Lowest price in the interval." 44 | }, 45 | "Last": { 46 | "existingJavaType": "java.lang.Double", 47 | "title": "Last price in the interval." 48 | }, 49 | "CumulativeVolume": { 50 | "existingJavaType": "java.lang.Integer", 51 | "title": "Last cumulative volume in the interval." 52 | }, 53 | "IntervalVolume": { 54 | "existingJavaType": "java.lang.Integer", 55 | "title": "Interval volume for the interval." 56 | }, 57 | "NumberOfTrades": { 58 | "existingJavaType": "java.lang.Integer", 59 | "title": "Number of trades in the interval (only valid for tick interval)." 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/derivative/watched_interval.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Represent a currently watched interval bar. See Streaming interval bars via TCP/IP", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String", 7 | "title": "The symbol." 8 | }, 9 | "Interval": { 10 | "existingJavaType": "java.lang.Integer", 11 | "title": "The interval length." 12 | }, 13 | "RequestID": { 14 | "existingJavaType": "java.lang.String", 15 | "title": "The Request ID." 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/customer_information.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "This message is received after your program connects to IQConnect. It contains customer information. See Level 1 Port System Messages", 4 | "properties": { 5 | "ServiceType": { 6 | "type": "string", 7 | "enum": [ 8 | "real_time", 9 | "delayed" 10 | ], 11 | "title": "The service type." 12 | }, 13 | "IP": { 14 | "existingJavaType": "java.lang.String", 15 | "title": "This is the IP address of the Quote server in use." 16 | }, 17 | "Port": { 18 | "existingJavaType": "java.lang.Integer", 19 | "title": "This will be the port for the Quote server in use." 20 | }, 21 | "Version": { 22 | "existingJavaType": "java.lang.String", 23 | "title": "Will be the most current version of the IQFeed Client." 24 | }, 25 | "VerboseExchanges": { 26 | "existingJavaType": "java.util.List", 27 | "title": "These are the exchanges in text that the customer will get in real-time." 28 | }, 29 | "MaxSymbols": { 30 | "existingJavaType": "java.lang.Integer", 31 | "title": "This is the maximum number of symbols a user can watch at one time on the Level 1 port." 32 | }, 33 | "Flags": { 34 | "existingJavaType": "java.lang.String", 35 | "title": "These are any special flags that may be in their account." 36 | }, 37 | "AccountExpirationDate": { 38 | "existingJavaType": "java.time.LocalDate", 39 | "title": "Date of account expiration (TRIAL CUSTOMERS ONLY)." 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/enums/level_1_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Level 1 feed commands. See Level 1 Port System Messages", 4 | "enum": [ 5 | "w", 6 | "t", 7 | "r", 8 | "f", 9 | "T" 10 | ], 11 | "javaEnums": [ 12 | { 13 | "name": "WATCH" 14 | }, 15 | { 16 | "name": "WATCH_TRADES" 17 | }, 18 | { 19 | "name": "UNWATCH" 20 | }, 21 | { 22 | "name": "FORCE_WATCH_REFRESH" 23 | }, 24 | { 25 | "name": "TIMESTAMP" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/enums/level_1_message_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Level 1 feed message types. See Level 1 via TCP/IP", 4 | "enum": [ 5 | "F", 6 | "P", 7 | "Q", 8 | "R", 9 | "N", 10 | "T", 11 | "C", 12 | "n" 13 | ], 14 | "javaEnums": [ 15 | { 16 | "name": "FUNDAMENTAL" 17 | }, 18 | { 19 | "name": "SUMMARY" 20 | }, 21 | { 22 | "name": "UPDATE" 23 | }, 24 | { 25 | "name": "REGIONAL_UPDATE" 26 | }, 27 | { 28 | "name": "NEWS_HEADLINE" 29 | }, 30 | { 31 | "name": "TIMESTAMP" 32 | }, 33 | { 34 | "name": "TRADE_CORRECTION" 35 | }, 36 | { 37 | "name": "SYMBOL_NOT_WATCHED" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/enums/level_1_system_command.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Level 1 feed system commands. See Level 1 Port System Messages", 4 | "enum": [ 5 | "TIMESTAMPSOFF", 6 | "TIMESTAMPSON", 7 | "REGON", 8 | "REGOFF", 9 | "NEWSON", 10 | "NEWSOFF", 11 | "REQUEST STATS", 12 | "REQUEST FUNDAMENTAL FIELDNAMES", 13 | "REQUEST ALL UPDATE FIELDNAMES", 14 | "REQUEST CURRENT UPDATE FIELDNAMES", 15 | "SELECT UPDATE FIELDS", 16 | "SET LOG LEVELS", 17 | "REQUEST WATCHES", 18 | "UNWATCH ALL", 19 | "CONNECT", 20 | "DISCONNECT" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/enums/level_1_system_message_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Level 1 feed System message types. See Level 1 Port System Messages", 4 | "enum": [ 5 | "KEY", 6 | "KEYOK", 7 | "SERVER RECONNECT FAILED", 8 | "SYMBOL LIMIT REACHED", 9 | "IP", 10 | "CUST", 11 | "STATS", 12 | "FUNDAMENTAL FIELDNAMES", 13 | "UPDATE FIELDNAMES", 14 | "CURRENT UPDATE FIELDNAMES", 15 | "CURRENT LOG LEVELS", 16 | "WATCHES" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/enums/log_level/log_level.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for log levels. See IQConnect Logging", 4 | "enum": [ 5 | "Admin", 6 | "L1Data", 7 | "L1Request", 8 | "L1System", 9 | "L1Error", 10 | "L2Data", 11 | "L2Request", 12 | "L2System", 13 | "L2Error", 14 | "LookupData", 15 | "LookupRequest", 16 | "LookupError", 17 | "Information", 18 | "Debug", 19 | "Connectivity" 20 | ], 21 | "javaEnums": [ 22 | { 23 | "title": "Admin log entries are non-data and non-request related log entries. Enabled by default." 24 | }, 25 | { 26 | "title": "L1Data log entries consists of all data transferred over the Level 1 Port." 27 | }, 28 | { 29 | "title": "L1Request log entries consists of all requests received from client apps on the Level 1 Port." 30 | }, 31 | { 32 | "title": "L1System log entries consists of all system messages received from and sent to the client on the Level 1 Port." 33 | }, 34 | { 35 | "title": "L1Error log entries consists of all errors sent to the client on the Level 1 Port." 36 | }, 37 | { 38 | "title": "L2Data log entries consists of all data transferred over the Level 2 Port." 39 | }, 40 | { 41 | "title": "L2Request log entries consists of all requests received from client apps on the Level 2 Port." 42 | }, 43 | { 44 | "title": "L2System log entries consists of all system messages received from and sent to the client on the Level 2 Port." 45 | }, 46 | { 47 | "title": "L2Error log entries consists of all errors sent to the client on the Level 2 Port." 48 | }, 49 | { 50 | "title": "LookupData log entries consists of all data transferred over the Lookup Port." 51 | }, 52 | { 53 | "title": "LookupRequest log entries consists of all requests received from client apps on the Lookup Port." 54 | }, 55 | { 56 | "title": "LookupError log entries consists of all errors sent to the client on the Lookup Port." 57 | }, 58 | { 59 | "title": "Information logging enables basic information, such as username and product id to be logged to the file. Enabled by default." 60 | }, 61 | { 62 | "title": "Debug logs enable more granular logging to aid in troubleshooting issues." 63 | }, 64 | { 65 | "title": "Connectivity log entries are related to the connection/reconnection between IQConnect and the server. Enabled by default." 66 | } 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/enums/summary_update/summary_update_content.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for Level 1 feed Summary Update message content. See Update/Message Summary Format", 4 | "enum": [ 5 | "C", 6 | "E", 7 | "O", 8 | "b", 9 | "a", 10 | "o", 11 | "h", 12 | "l", 13 | "c", 14 | "s", 15 | "v" 16 | ], 17 | "javaEnums": [ 18 | { 19 | "name": "LAST_QUALIFIED_TRADE" 20 | }, 21 | { 22 | "name": "EXTENDED_TRADE", 23 | "title": "Form T trade." 24 | }, 25 | { 26 | "name": "OTHER_TRADE", 27 | "title": "Any trade not accounted for by {@link SummaryUpdateContent#LAST_QUALIFIED_TRADE} or {@link SummaryUpdateContent#EXTENDED_TRADE}." 28 | }, 29 | { 30 | "name": "BID_UPDATE" 31 | }, 32 | { 33 | "name": "ASK_UPDATE" 34 | }, 35 | { 36 | "name": "OPEN" 37 | }, 38 | { 39 | "name": "HIGH" 40 | }, 41 | { 42 | "name": "LOW" 43 | }, 44 | { 45 | "name": "CLOSE" 46 | }, 47 | { 48 | "name": "SETTLEMENT" 49 | }, 50 | { 51 | "name": "VOLUME_UPDATE" 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/enums/summary_update/summary_update_field.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Enums for {@link net.jacobpeterson.iqfeed4j.model.feed.streaming.level1.SummaryUpdate} fields for an Update message. See Dynamic Fieldsets", 4 | "enum": [ 5 | "Symbol", 6 | "Exchange ID", 7 | "Last", 8 | "Change", 9 | "Percent Change", 10 | "Total Volume", 11 | "High", 12 | "Low", 13 | "Bid", 14 | "Ask", 15 | "Bid Size", 16 | "Ask Size", 17 | "Tick", 18 | "Range", 19 | "Open Interest", 20 | "Open", 21 | "Close", 22 | "Spread", 23 | "Settle", 24 | "Delay", 25 | "Restricted Code", 26 | "Net Asset Value", 27 | "Average Maturity", 28 | "Seven Day Yield", 29 | "Extended Trading Change", 30 | "Extended Trading Difference", 31 | "Price-Earnings Ratio", 32 | "Percent Off Average Volume", 33 | "Bid Change", 34 | "Ask Change", 35 | "Change From Open", 36 | "Market Open", 37 | "Volatility", 38 | "Market Capitalization", 39 | "Fraction Display Code", 40 | "Decimal Precision", 41 | "Days to Expiration", 42 | "Previous Day Volume", 43 | "Open Range 1", 44 | "Close Range 1", 45 | "Open Range 2", 46 | "Close Range 2", 47 | "Number of Trades Today", 48 | "VWAP", 49 | "TickID", 50 | "Financial Status Indicator", 51 | "Settlement Date", 52 | "Bid Market Center", 53 | "Ask Market Center", 54 | "Available Regions", 55 | "Last Size", 56 | "Last Time", 57 | "Last Market Center", 58 | "Most Recent Trade", 59 | "Most Recent Trade Size", 60 | "Most Recent Trade Time", 61 | "Most Recent Trade Conditions", 62 | "Most Recent Trade Market Center", 63 | "Extended Trade", 64 | "Extended Trade Size", 65 | "Extended Trade Time", 66 | "Extended Trade Market Center", 67 | "Message Contents", 68 | "Ask Time", 69 | "Bid Time", 70 | "Last Date", 71 | "Extended Trade Date", 72 | "Most Recent Trade Date", 73 | "Most Recent Trade Aggressor", 74 | "Most Recent Trade Day Code" 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/enums/trade_correction/correction_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string", 3 | "title": "Represents a trade correction message. See Trade Correction Message Format", 4 | "enum": [ 5 | "I", 6 | "X" 7 | ], 8 | "javaEnums": [ 9 | { 10 | "name": "INSERTION", 11 | "title": "Trade insertion." 12 | }, 13 | { 14 | "name": "DELETION", 15 | "title": "Trade deletion." 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/news_headline.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Represents a news headline. See Streaming News Messages", 4 | "properties": { 5 | "DistributorCode": { 6 | "existingJavaType": "java.lang.String", 7 | "title": "Distributor Type code." 8 | }, 9 | "StoryID": { 10 | "existingJavaType": "java.lang.Integer", 11 | "title": "Numerical story ID." 12 | }, 13 | "SymbolList": { 14 | "existingJavaType": "java.util.List", 15 | "title": "Symbols associated with the story." 16 | }, 17 | "Timestamp": { 18 | "existingJavaType": "java.time.LocalDateTime", 19 | "title": "The timestamp." 20 | }, 21 | "Headline": { 22 | "existingJavaType": "java.lang.String", 23 | "title": "The text of the headline." 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/regional_quote.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Represents a Regional message. See Regional Messages", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String", 7 | "title": "Symbol." 8 | }, 9 | "Exchange": { 10 | "existingJavaType": "java.lang.String", 11 | "title": "Deprecated - Use {@link #marketCenter}. See: Market Center Codes." 12 | }, 13 | "Regional Bid": { 14 | "existingJavaType": "java.lang.Double", 15 | "title": "Regional Bid." 16 | }, 17 | "Regional Bid Size": { 18 | "existingJavaType": "java.lang.Integer", 19 | "title": "Regional Bid Size." 20 | }, 21 | "Regional BidTime": { 22 | "existingJavaType": "java.time.LocalTime", 23 | "title": "Currently Time of last Trade." 24 | }, 25 | "Regional Ask": { 26 | "existingJavaType": "java.lang.Double", 27 | "title": "Regional Ask." 28 | }, 29 | "Regional Ask Size": { 30 | "existingJavaType": "java.lang.Integer", 31 | "title": "Regional Ask Size." 32 | }, 33 | "Regional AskTime": { 34 | "existingJavaType": "java.time.LocalTime", 35 | "title": "Currently Time of last Trade." 36 | }, 37 | "Fraction Display Code": { 38 | "existingJavaType": "java.lang.Integer", 39 | "title": "Display formatting code. See Price Format Codes." 40 | }, 41 | "Decimal Precision": { 42 | "existingJavaType": "java.lang.Integer", 43 | "title": "Last Precision used." 44 | }, 45 | "Market Center": { 46 | "existingJavaType": "java.lang.Integer", 47 | "title": "The regional exchange that the update occurred at. See the Listed Markets Codes for a list of possible values." 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /schema_json/feed/streaming/level1/trade_correction.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Represents a trade correction message. See Trade Correction Message Format", 4 | "properties": { 5 | "Symbol": { 6 | "existingJavaType": "java.lang.String", 7 | "title": "The symbol ID to match with the watch request." 8 | }, 9 | "CorrectionType": { 10 | "existingJavaType": "net.jacobpeterson.iqfeed4j.model.feed.streaming.level1.enums.tradecorrection.CorrectionType", 11 | "title": "Either {@link net.jacobpeterson.iqfeed4j.model.feed.streaming.level1.enums.tradecorrection.CorrectionType#INSERTION} indicating a trade insert or {@link net.jacobpeterson.iqfeed4j.model.feed.streaming.level1.enums.tradecorrection.CorrectionType#DELETION} indicating a trade delete." 12 | }, 13 | "TradeDate": { 14 | "existingJavaType": "java.time.LocalDate", 15 | "title": "Date of the trade that is being inserted or deleted." 16 | }, 17 | "TradeTime": { 18 | "existingJavaType": "java.time.LocalTime", 19 | "title": "Time (including microseconds) of the trade that is being inserted or deleted." 20 | }, 21 | "TradePrice": { 22 | "existingJavaType": "java.lang.Double", 23 | "title": "Price of the trade that is being inserted or deleted." 24 | }, 25 | "TradeSize": { 26 | "existingJavaType": "java.lang.Integer", 27 | "title": "Size of the trade that is being inserted or deleted." 28 | }, 29 | "TickID": { 30 | "existingJavaType": "java.lang.Long", 31 | "title": "Identifier for the trade that is being inserted or deleted." 32 | }, 33 | "TradeConditions": { 34 | "existingJavaType": "java.util.List", 35 | "title": "Conditions that identify the type of trade that occurred for the trade that is being inserted or deleted." 36 | }, 37 | "TradeMarketCenter": { 38 | "existingJavaType": "java.lang.Integer", 39 | "title": "Market Center of the trade that is being inserted or deleted." 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'iqfeed4j' 2 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/executable/ExecutablePollingFeed.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.executable; 2 | 3 | import net.jacobpeterson.iqfeed4j.feed.AbstractFeed; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * {@link ExecutablePollingFeed} is an {@link AbstractFeed} used exclusively for 9 | * {@link IQConnectExecutable#waitForConnection(String, int, int, long)}. 10 | */ 11 | class ExecutablePollingFeed extends AbstractFeed { 12 | 13 | private static final Logger LOGGER = LoggerFactory.getLogger(ExecutablePollingFeed.class); 14 | 15 | /** 16 | * Instantiates a new {@link ExecutablePollingFeed}. 17 | * 18 | * @param hostname the hostname 19 | * @param port the port 20 | */ 21 | public ExecutablePollingFeed(String hostname, int port) { 22 | super(LOGGER, "IQFeed4j Polling Feed", hostname, port, COMMA_DELIMITED_SPLITTER, false, false); 23 | } 24 | 25 | @Override 26 | protected void onMessageReceived(String[] csv) {} 27 | 28 | @Override 29 | protected void onFeedSocketException(Exception exception) {} 30 | 31 | @Override 32 | protected void onFeedSocketClose() {} 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/RequestIDFeedHelper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed; 2 | 3 | import net.jacobpeterson.iqfeed4j.feed.lookup.AbstractLookupFeed; 4 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedMessageType; 5 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedSpecialMessage; 6 | 7 | import java.util.Collections; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueEquals; 12 | 13 | /** 14 | * {@link RequestIDFeedHelper} is a helper for an {@link AbstractFeed} that uses Request IDs. 15 | */ 16 | public class RequestIDFeedHelper { 17 | 18 | private final Set requestIDs; 19 | 20 | /** 21 | * Instantiates a new {@link AbstractLookupFeed}. 22 | */ 23 | public RequestIDFeedHelper() { 24 | requestIDs = Collections.synchronizedSet(new HashSet<>()); 25 | } 26 | 27 | /** 28 | * Checks for a request ID error message format. 29 | *
30 | * e.g. [Request ID], E, <Error Text> 31 | * 32 | * @param csv the CSV 33 | * @param requestID the request ID 34 | * 35 | * @return true if the CSV represents an {@link FeedMessageType#ERROR} message 36 | */ 37 | public boolean isRequestErrorMessage(String[] csv, String requestID) { 38 | return valueEquals(csv, 0, requestID) && valueEquals(csv, 1, FeedMessageType.ERROR.value()); 39 | } 40 | 41 | /** 42 | * Check if a message matches the following format: 43 | *
44 | * [Request ID], {@link FeedSpecialMessage#END_OF_MESSAGE} 45 | * 46 | * @param csv the CSV 47 | * @param requestID the request ID 48 | * 49 | * @return true if the message represents an {@link FeedSpecialMessage#END_OF_MESSAGE} message 50 | */ 51 | public boolean isRequestEndOfMessage(String[] csv, String requestID) { 52 | return valueEquals(csv, 0, requestID) && valueEquals(csv, 1, FeedSpecialMessage.END_OF_MESSAGE.value()); 53 | } 54 | 55 | /** 56 | * Check if a message matches the following format: 57 | *
58 | * [Request ID], E, {@link FeedSpecialMessage#NO_DATA_ERROR} 59 | * 60 | * @param csv the CSV 61 | * 62 | * @return true if the message represents a {@link FeedSpecialMessage#NO_DATA_ERROR} message 63 | */ 64 | public boolean isRequestNoDataError(String[] csv) { 65 | return valueEquals(csv, 2, FeedSpecialMessage.NO_DATA_ERROR.value()); 66 | } 67 | 68 | /** 69 | * Check if a message matches the following format: 70 | *
71 | * [Request ID], E, {@link FeedSpecialMessage#SYNTAX_ERROR} 72 | * 73 | * @param csv the CSV 74 | * 75 | * @return true if the message represents a {@link FeedSpecialMessage#SYNTAX_ERROR} message 76 | */ 77 | public boolean isRequestSyntaxError(String[] csv) { 78 | return valueEquals(csv, 2, FeedSpecialMessage.SYNTAX_ERROR.value()); 79 | } 80 | 81 | /** 82 | * Gets a new Request ID. This method is thread safe. 83 | * 84 | * @return a new request ID 85 | */ 86 | public String getNewRequestID() { 87 | int maxRequestID; 88 | synchronized (requestIDs) { 89 | maxRequestID = requestIDs.stream().max(Integer::compareTo).orElse(0) + 1; 90 | } 91 | requestIDs.add(maxRequestID); 92 | return String.valueOf(maxRequestID); 93 | } 94 | 95 | /** 96 | * Removes a Request ID. This method is thread safe. 97 | * 98 | * @param requestID the request ID 99 | */ 100 | public void removeRequestID(String requestID) { 101 | requestIDs.remove(Integer.parseInt(requestID)); 102 | } 103 | 104 | /** 105 | * Clears all Request IDs so that {@link #getNewRequestID()} starts at 0. 106 | */ 107 | public void clearRequestIDs() { 108 | requestIDs.clear(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/exception/IQFeedException.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.exception; 2 | 3 | /** 4 | * {@link IQFeedException} represents IQFeed (checked) {@link Exception}s resembling a recoverable error due to an issue 5 | * outside the control of this program. 6 | */ 7 | public class IQFeedException extends Exception { 8 | 9 | /** 10 | * Instantiates a new {@link IQFeedException}. 11 | */ 12 | public IQFeedException() {} 13 | 14 | /** 15 | * Instantiates a new {@link IQFeedException}. 16 | * 17 | * @param message the message 18 | */ 19 | public IQFeedException(String message) { 20 | super(message); 21 | } 22 | 23 | /** 24 | * Instantiates a new {@link IQFeedException}. 25 | * 26 | * @param message the message 27 | * @param cause the cause 28 | */ 29 | public IQFeedException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | 33 | /** 34 | * Instantiates a new {@link IQFeedException} 35 | * 36 | * @param cause the cause 37 | */ 38 | public IQFeedException(Throwable cause) { 39 | super(cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/exception/IQFeedRuntimeException.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.exception; 2 | 3 | /** 4 | * {@link IQFeedRuntimeException} represents (unchecked) IQFeed {@link RuntimeException}s resembling an unrecoverable 5 | * error due to faulty program logic. 6 | */ 7 | public class IQFeedRuntimeException extends RuntimeException { 8 | 9 | /** 10 | * Instantiates a new {@link IQFeedRuntimeException}. 11 | */ 12 | public IQFeedRuntimeException() {} 13 | 14 | /** 15 | * Instantiates a new {@link IQFeedRuntimeException}. 16 | * 17 | * @param message the message 18 | */ 19 | public IQFeedRuntimeException(String message) { 20 | super(message); 21 | } 22 | 23 | /** 24 | * Instantiates a new {@link IQFeedRuntimeException}. 25 | * 26 | * @param message the message 27 | * @param cause the cause 28 | */ 29 | public IQFeedRuntimeException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | 33 | /** 34 | * Instantiates a new {@link IQFeedRuntimeException} 35 | * 36 | * @param cause the cause 37 | */ 38 | public IQFeedRuntimeException(Throwable cause) { 39 | super(cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/exception/NoDataException.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.exception; 2 | 3 | /** 4 | * {@link NoDataException} is an {@link IQFeedException} for when no data is present. 5 | */ 6 | public class NoDataException extends IQFeedException { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/exception/SyntaxException.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.exception; 2 | 3 | /** 4 | * {@link SyntaxException} is an {@link IQFeedRuntimeException} for when a syntax error in a request occurs. 5 | */ 6 | public class SyntaxException extends IQFeedRuntimeException { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/AbstractLookupFeed.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup; 2 | 3 | import com.google.common.base.Splitter; 4 | import net.jacobpeterson.iqfeed4j.feed.AbstractFeed; 5 | import net.jacobpeterson.iqfeed4j.feed.RequestIDFeedHelper; 6 | import net.jacobpeterson.iqfeed4j.feed.exception.IQFeedRuntimeException; 7 | import net.jacobpeterson.iqfeed4j.feed.exception.NoDataException; 8 | import net.jacobpeterson.iqfeed4j.feed.exception.SyntaxException; 9 | import net.jacobpeterson.iqfeed4j.feed.message.MultiMessageListener; 10 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedMessageType; 11 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedSpecialMessage; 12 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.index.AbstractIndexCSVMapper; 13 | import org.slf4j.Logger; 14 | 15 | import java.util.Arrays; 16 | import java.util.Map; 17 | 18 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueEquals; 19 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace; 20 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valuePresent; 21 | 22 | /** 23 | * {@link AbstractLookupFeed} represents a {@link AbstractFeed} for Lookup data. 24 | */ 25 | public abstract class AbstractLookupFeed extends AbstractFeed { 26 | 27 | private static final String FEED_NAME_SUFFIX = " Lookup Feed"; 28 | 29 | protected final RequestIDFeedHelper requestIDFeedHelper; 30 | 31 | /** 32 | * Instantiates a new {@link AbstractLookupFeed}. 33 | * 34 | * @param logger the {@link Logger} 35 | * @param lookupFeedName the lookup feed name 36 | * @param hostname the host name 37 | * @param port the port 38 | * @param csvSplitter the CSV {@link Splitter} 39 | */ 40 | public AbstractLookupFeed(Logger logger, String lookupFeedName, String hostname, int port, Splitter csvSplitter) { 41 | super(logger, lookupFeedName + FEED_NAME_SUFFIX, hostname, port, csvSplitter, true, true); 42 | 43 | requestIDFeedHelper = new RequestIDFeedHelper(); 44 | } 45 | 46 | /** 47 | * Handles a standard message for a {@link MultiMessageListener} by: checking for request error messages, handling 48 | * {@link FeedSpecialMessage#END_OF_MESSAGE} messages, and performing 49 | * {@link AbstractIndexCSVMapper#map(String[], int)} on the csv to call 50 | * {@link MultiMessageListener#onMessageReceived(Object)}. 51 | * 52 | * @param the type of {@link MultiMessageListener} 53 | * @param csv the CSV 54 | * @param requestID the Request ID 55 | * @param offset the offset to add to CSV indices 56 | * @param listenersOfRequestIDs the {@link Map} with the keys being the Request IDs and the values being the 57 | * corresponding {@link MultiMessageListener}s 58 | * @param indexCSVMapper the {@link AbstractIndexCSVMapper} for the message 59 | * 60 | * @return true if the requestID was a key inside listenersOfRequestIDs, false otherwise 61 | */ 62 | protected boolean handleStandardMultiMessage(String[] csv, String requestID, int offset, 63 | Map> listenersOfRequestIDs, AbstractIndexCSVMapper indexCSVMapper) { 64 | MultiMessageListener listener = listenersOfRequestIDs.get(requestID); 65 | 66 | if (listener == null) { 67 | return false; 68 | } 69 | 70 | if (requestIDFeedHelper.isRequestErrorMessage(csv, requestID)) { 71 | if (requestIDFeedHelper.isRequestNoDataError(csv)) { 72 | listener.onMessageException(new NoDataException()); 73 | } else if (requestIDFeedHelper.isRequestSyntaxError(csv)) { 74 | listener.onMessageException(new SyntaxException()); 75 | } else { 76 | listener.onMessageException(new IQFeedRuntimeException( 77 | valuePresent(csv, 2) ? 78 | String.join(",", Arrays.copyOfRange(csv, 2, csv.length)) : 79 | "Error message not present.")); 80 | } 81 | } else if (requestIDFeedHelper.isRequestEndOfMessage(csv, requestID)) { 82 | listenersOfRequestIDs.remove(requestID); 83 | requestIDFeedHelper.removeRequestID(requestID); 84 | listener.handleEndOfMultiMessage(); 85 | } else { 86 | try { 87 | T message = indexCSVMapper.map(csv, offset); 88 | listener.onMessageReceived(message); 89 | } catch (Exception exception) { 90 | listener.onMessageException(exception); 91 | } 92 | } 93 | 94 | return true; 95 | } 96 | 97 | /** 98 | * Checks if a message is a {@link FeedMessageType#ERROR} message or that the first CSV value is whitespace to check 99 | * if a Request ID is present. 100 | * 101 | * @param csv the CSV 102 | * 103 | * @return true if the message is an error or is invalid 104 | */ 105 | protected boolean isErrorOrInvalidMessage(String[] csv) { 106 | if (valueEquals(csv, 0, FeedMessageType.ERROR.value())) { 107 | logger.error("Received error message! {}", (Object) csv); 108 | return true; 109 | } 110 | 111 | // Messages sent on this feed have a numeric Request ID first 112 | if (!valueNotWhitespace(csv, 0)) { 113 | logger.error("Received unknown message format: {}", (Object) csv); 114 | return true; 115 | } 116 | 117 | return false; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/historical/pool/HistoricalFeedPool.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.historical.pool; 2 | 3 | import net.jacobpeterson.iqfeed4j.feed.lookup.historical.HistoricalFeed; 4 | import org.apache.commons.pool2.ObjectPool; 5 | import org.apache.commons.pool2.PooledObject; 6 | import org.apache.commons.pool2.PooledObjectFactory; 7 | import org.apache.commons.pool2.impl.DefaultEvictionPolicy; 8 | import org.apache.commons.pool2.impl.DefaultPooledObject; 9 | import org.apache.commons.pool2.impl.GenericObjectPool; 10 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 11 | 12 | import java.time.Duration; 13 | import java.util.concurrent.TimeUnit; 14 | import java.util.function.Consumer; 15 | 16 | import static com.google.common.base.Preconditions.checkArgument; 17 | 18 | /** 19 | * {@link HistoricalFeedPool} contains a thread-safe pool of {@link HistoricalFeed}s and has IQFeed's history request 20 | * rate limit built in. This class is designed to allow a user to make as many simultaneous {@link HistoricalFeed} 21 | * requests that IQFeed allows. 22 | */ 23 | public class HistoricalFeedPool { 24 | 25 | /** 26 | * Defines the feed request time delay in milliseconds which is currently 20 ms (50 requests/second) plus 1 ms for 27 | * margin. 28 | */ 29 | public static final int DEFAULT_FEED_REQUEST_TIME_DELAY_MILLIS = 20 + 1; 30 | 31 | private final ObjectPool pool; 32 | private final Object lastRequestMillisLock; 33 | private long lastRequestMillis; 34 | 35 | /** 36 | * Instantiates a new {@link HistoricalFeedPool} using {@link Factory} as the {@link HistoricalFeed} 37 | * {@link PooledObjectFactory} and a customized {@link GenericObjectPoolConfig}. 38 | * 39 | * @param historicalFeedName the {@link HistoricalFeed} name 40 | * @param hostname the hostname 41 | * @param port the port 42 | */ 43 | public HistoricalFeedPool(String historicalFeedName, String hostname, int port) { 44 | final GenericObjectPoolConfig feedPoolConfig = new GenericObjectPoolConfig<>(); 45 | // Configure max/min pool objects 46 | feedPoolConfig.setMaxTotal(100); 47 | feedPoolConfig.setMaxIdle(-1); 48 | feedPoolConfig.setMinIdle(0); 49 | feedPoolConfig.setBlockWhenExhausted(true); 50 | // Test the feed instance to validate it before use 51 | feedPoolConfig.setTestOnBorrow(true); 52 | feedPoolConfig.setTestOnCreate(true); 53 | feedPoolConfig.setTestOnReturn(true); 54 | // Configure eviction of idle feeds 55 | feedPoolConfig.setEvictionPolicy(new DefaultEvictionPolicy<>()); 56 | feedPoolConfig.setMinEvictableIdleTime(Duration.ofSeconds(30)); 57 | feedPoolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(25)); 58 | feedPoolConfig.setNumTestsPerEvictionRun(-3); // Evict 1/3rd of idle feeds 59 | 60 | pool = new GenericObjectPool<>(new Factory(historicalFeedName, hostname, port), feedPoolConfig); 61 | lastRequestMillisLock = new Object(); 62 | } 63 | 64 | /** 65 | * Instantiates a new {@link HistoricalFeedPool} using {@link Factory} as the {@link HistoricalFeed} 66 | * {@link PooledObjectFactory} and a given {@link GenericObjectPoolConfig}. 67 | * 68 | * @param historicalFeedName the {@link HistoricalFeed} name 69 | * @param hostname the hostname 70 | * @param port the port 71 | * @param feedPoolConfig the {@link HistoricalFeed} {@link GenericObjectPoolConfig} 72 | */ 73 | public HistoricalFeedPool(String historicalFeedName, String hostname, int port, 74 | GenericObjectPoolConfig feedPoolConfig) { 75 | pool = new GenericObjectPool<>(new Factory(historicalFeedName, hostname, port), feedPoolConfig); 76 | lastRequestMillisLock = new Object(); 77 | } 78 | 79 | /** 80 | * Instantiates a new {@link HistoricalFeedPool}. 81 | * 82 | * @param objectPool the {@link ObjectPool} of {@link HistoricalFeed}s to use 83 | */ 84 | public HistoricalFeedPool(ObjectPool objectPool) { 85 | checkArgument(objectPool.getNumActive() <= 0); 86 | this.pool = objectPool; 87 | lastRequestMillisLock = new Object(); 88 | } 89 | 90 | /** 91 | * Stops this instance of {@link HistoricalFeedPool} (stops/closes all {@link HistoricalFeed}s in the feed pool). 92 | */ 93 | public void stop() { 94 | pool.close(); 95 | } 96 | 97 | /** 98 | * Synchronously makes a request to a {@link HistoricalFeed} in this {@link HistoricalFeedPool} given a request 99 | * {@link Consumer}. This method may block for up to {@link Factory#getFeedRequestTimeDelayMillis()} milliseconds. 100 | * 101 | * @param historicalFeedConsumer the {@link HistoricalFeed} {@link Consumer} 102 | * 103 | * @throws Exception thrown for a variety of {@link Exception}s 104 | */ 105 | public void request(Consumer historicalFeedConsumer) throws Exception { 106 | blockIfRateLimited(); 107 | 108 | HistoricalFeed borrowedHistoricalFeed = pool.borrowObject(); 109 | try { 110 | historicalFeedConsumer.accept(borrowedHistoricalFeed); 111 | } finally { 112 | pool.returnObject(borrowedHistoricalFeed); 113 | } 114 | } 115 | 116 | /** 117 | * Blocks until time since {@link #lastRequestMillis} is greater than {@link #getFeedRequestTimeDelayMillis()}. Also 118 | * sets {@link #lastRequestMillis} to the current time. 119 | * 120 | * @throws InterruptedException thrown for {@link InterruptedException}s 121 | */ 122 | private void blockIfRateLimited() throws InterruptedException { 123 | synchronized (lastRequestMillisLock) { 124 | long elapsedMillis = System.currentTimeMillis() - lastRequestMillis; 125 | if (elapsedMillis < getFeedRequestTimeDelayMillis()) { 126 | Thread.sleep(getFeedRequestTimeDelayMillis() - elapsedMillis); 127 | } 128 | 129 | lastRequestMillis = System.currentTimeMillis(); 130 | } 131 | } 132 | 133 | /** 134 | * Gets the time delay in milliseconds for feed request rate limiting. 135 | * 136 | * @return a positive number of milliseconds 137 | */ 138 | protected long getFeedRequestTimeDelayMillis() { 139 | return DEFAULT_FEED_REQUEST_TIME_DELAY_MILLIS; 140 | } 141 | 142 | /** 143 | * {@link Factory} is a {@link PooledObjectFactory} for {@link HistoricalFeed}s. 144 | */ 145 | public static class Factory implements PooledObjectFactory { 146 | 147 | private final String historicalFeedName; 148 | private final String hostname; 149 | private final int port; 150 | 151 | /** 152 | * Instantiates a new {@link Factory}. 153 | * 154 | * @param historicalFeedName the {@link HistoricalFeed} name 155 | * @param hostname the hostname 156 | * @param port the port 157 | */ 158 | public Factory(String historicalFeedName, String hostname, int port) { 159 | this.historicalFeedName = historicalFeedName; 160 | this.hostname = hostname; 161 | this.port = port; 162 | } 163 | 164 | @Override 165 | public PooledObject makeObject() throws Exception { 166 | HistoricalFeed historicalFeed = new HistoricalFeed(historicalFeedName, hostname, port); 167 | historicalFeed.start(); 168 | historicalFeed.waitForProtocolVersionValidation(15, TimeUnit.SECONDS); 169 | 170 | return new DefaultPooledObject<>(historicalFeed); 171 | } 172 | 173 | @Override 174 | public void activateObject(PooledObject pooledObject) {} 175 | 176 | @Override 177 | public void passivateObject(PooledObject pooledObject) {} 178 | 179 | @Override 180 | public void destroyObject(PooledObject pooledObject) throws Exception { 181 | pooledObject.getObject().stop(); 182 | } 183 | 184 | @Override 185 | public boolean validateObject(PooledObject pooledObject) { 186 | return pooledObject.getObject().isValid(); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/configuration/NewsCategory.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.configuration; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; 4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 5 | 6 | import java.util.ArrayList; 7 | 8 | /** 9 | * {@link NewsCategory} contains {@link NewsMajorSource}s. 10 | */ 11 | public class NewsCategory { 12 | 13 | @JacksonXmlProperty(localName = "major_type") 14 | @JacksonXmlElementWrapper(useWrapping = false) 15 | private ArrayList majorSources; 16 | @JacksonXmlProperty(localName = "name") 17 | private String name; 18 | 19 | /** 20 | * Gets {@link #majorSources}. 21 | * 22 | * @return a {@link ArrayList} of {@link NewsMajorSource}s 23 | */ 24 | public ArrayList getMajorSources() { 25 | return majorSources; 26 | } 27 | 28 | /** 29 | * Sets {@link #majorSources}. 30 | * 31 | * @param majorSources a {@link ArrayList} of {@link NewsMajorSource}s 32 | */ 33 | public void setMajorSources(ArrayList majorSources) { 34 | this.majorSources = majorSources; 35 | } 36 | 37 | /** 38 | * Gets {@link #name}. 39 | * 40 | * @return the name 41 | */ 42 | public String getName() { 43 | return name; 44 | } 45 | 46 | /** 47 | * Sets {@link #name}. 48 | * 49 | * @param name the name 50 | */ 51 | public void setName(String name) { 52 | this.name = name; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "NewsCategory{" + 58 | "majorSources=" + majorSources + 59 | ", name='" + name + '\'' + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/configuration/NewsConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.configuration; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; 4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 5 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * {@link NewsConfiguration} contains {@link NewsCategory}s. 11 | */ 12 | @JacksonXmlRootElement(localName = "DynamicNewsConf") 13 | public class NewsConfiguration { 14 | 15 | @JacksonXmlProperty(localName = "category") 16 | @JacksonXmlElementWrapper(useWrapping = false) 17 | private ArrayList categories; 18 | 19 | /** 20 | * Gets {@link #categories}. 21 | * 22 | * @return a {@link ArrayList} of {@link NewsCategory}s 23 | */ 24 | public ArrayList getCategories() { 25 | return categories; 26 | } 27 | 28 | /** 29 | * Sets {@link #categories}. 30 | * 31 | * @param categories a {@link ArrayList} of {@link NewsCategory}s 32 | */ 33 | public void setCategories(ArrayList categories) { 34 | this.categories = categories; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "NewsConfiguration{" + 40 | "categories=" + categories + 41 | '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/configuration/NewsMajorSource.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.configuration; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; 4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 5 | 6 | import java.util.ArrayList; 7 | 8 | /** 9 | * {@link NewsMajorSource} is a major {@link NewsSource}. 10 | */ 11 | public class NewsMajorSource extends NewsSource { 12 | 13 | @JacksonXmlProperty(localName = "minor_type") 14 | @JacksonXmlElementWrapper(useWrapping = false) 15 | private ArrayList minorSources; 16 | 17 | /** 18 | * Gets {@link #minorSources}. 19 | * 20 | * @return a {@link ArrayList} of {@link NewsMinorSource}s 21 | */ 22 | public ArrayList getMinorSources() { 23 | return minorSources; 24 | } 25 | 26 | /** 27 | * Sets {@link #minorSources}. 28 | * 29 | * @param minorSources a {@link ArrayList} of {@link NewsMinorSource}s 30 | */ 31 | public void setMinorSources(ArrayList minorSources) { 32 | this.minorSources = minorSources; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "NewsMajorSource{" + 38 | "minorSources=" + minorSources + 39 | "} " + super.toString(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/configuration/NewsMinorSource.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.configuration; 2 | 3 | /** 4 | * {@link NewsMinorSource} is a minor {@link NewsSource}. 5 | */ 6 | public class NewsMinorSource extends NewsSource { 7 | 8 | @Override 9 | public String toString() { 10 | return "NewsMinorSource{} " + super.toString(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/configuration/NewsSource.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.configuration; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 4 | 5 | /** 6 | * {@link NewsSource} represents a type of news source. 7 | */ 8 | public class NewsSource { 9 | 10 | @JacksonXmlProperty(localName = "type", isAttribute = true) 11 | protected String id; 12 | @JacksonXmlProperty(localName = "name", isAttribute = true) 13 | protected String name; 14 | @JacksonXmlProperty(localName = "auth_code", isAttribute = true) 15 | protected String authCode; 16 | @JacksonXmlProperty(localName = "icon_id", isAttribute = true) 17 | protected Integer iconID; 18 | @JacksonXmlProperty(localName = "fixed_page", isAttribute = true) 19 | protected Boolean fixedPage; 20 | 21 | /** 22 | * Instantiates a new {@link NewsSource}. 23 | */ 24 | public NewsSource() {} 25 | 26 | /** 27 | * Gets {@link #id}. 28 | * 29 | * @return the ID 30 | */ 31 | public String getID() { 32 | return id; 33 | } 34 | 35 | /** 36 | * Sets {@link #id}. 37 | * 38 | * @param id the ID 39 | */ 40 | public void setID(String id) { 41 | this.id = id; 42 | } 43 | 44 | /** 45 | * Gets {@link #name}. 46 | * 47 | * @return the name 48 | */ 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | /** 54 | * Sets {@link #name}. 55 | * 56 | * @param name the name 57 | */ 58 | public void setName(String name) { 59 | this.name = name; 60 | } 61 | 62 | /** 63 | * Gets {@link #authCode}. 64 | * 65 | * @return the auth code 66 | */ 67 | public String getAuthCode() { 68 | return authCode; 69 | } 70 | 71 | /** 72 | * Sets {@link #authCode}. 73 | * 74 | * @param authCode the auth code 75 | */ 76 | public void setAuthCode(String authCode) { 77 | this.authCode = authCode; 78 | } 79 | 80 | /** 81 | * Gets {@link #iconID}. 82 | * 83 | * @return the icon ID 84 | */ 85 | public Integer getIconID() { 86 | return iconID; 87 | } 88 | 89 | /** 90 | * Sets {@link #iconID}. 91 | * 92 | * @param iconID the icon ID 93 | */ 94 | public void setIconID(Integer iconID) { 95 | this.iconID = iconID; 96 | } 97 | 98 | /** 99 | * Gets {@link #fixedPage}. 100 | * 101 | * @return the fixed page 102 | */ 103 | public Boolean isFixedPage() { 104 | return fixedPage; 105 | } 106 | 107 | /** 108 | * Sets {@link #fixedPage}. 109 | * 110 | * @param fixedPage the fixed page 111 | */ 112 | public void setFixedPage(Boolean fixedPage) { 113 | this.fixedPage = fixedPage; 114 | } 115 | 116 | @Override 117 | public String toString() { 118 | return "NewsSource{" + 119 | "id='" + id + '\'' + 120 | ", name='" + name + '\'' + 121 | ", authCode='" + authCode + '\'' + 122 | ", iconID=" + iconID + 123 | ", fixedPage=" + fixedPage + 124 | '}'; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/deserializers/NewsHeadlineSymbolsDeserializer.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.deserializers; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | import com.google.common.base.Splitter; 7 | import net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.headline.NewsHeadline; 8 | 9 | import java.io.IOException; 10 | import java.util.List; 11 | import java.util.Objects; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * {@link NewsHeadlineSymbolsDeserializer} is for {@link NewsHeadline#getSymbols()} XML deserialization. 16 | */ 17 | public class NewsHeadlineSymbolsDeserializer extends JsonDeserializer> { 18 | 19 | @Override 20 | @SuppressWarnings("UnstableApiUsage") 21 | public List deserialize(JsonParser parser, DeserializationContext context) throws IOException { 22 | return Splitter.on(':').splitToStream(parser.getValueAsString()) 23 | .filter(Objects::nonNull) 24 | .filter(s -> !s.isEmpty()) 25 | .collect(Collectors.toList()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/deserializers/NewsStoryIsLinkDeserializer.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.deserializers; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | import net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.story.NewsStory; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * {@link NewsStoryIsLinkDeserializer} is for {@link NewsStory#isLink()} XML deserialization. 12 | */ 13 | public class NewsStoryIsLinkDeserializer extends JsonDeserializer { 14 | 15 | @Override 16 | public Boolean deserialize(JsonParser parser, DeserializationContext context) throws IOException { 17 | String value = parser.getValueAsString(); 18 | if (value.equals("Y")) { 19 | return true; 20 | } else if (value.equals("N")) { 21 | return false; 22 | } else { 23 | return null; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/headline/NewsHeadline.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.headline; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 6 | import net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.deserializers.NewsHeadlineSymbolsDeserializer; 7 | 8 | import java.time.LocalDateTime; 9 | import java.util.List; 10 | 11 | /** 12 | * {@link NewsHeadline} represents a news headline from a news feed. 13 | */ 14 | public class NewsHeadline { 15 | 16 | @JacksonXmlProperty(localName = "id") 17 | protected String id; 18 | @JacksonXmlProperty(localName = "source") 19 | protected String source; 20 | @JacksonXmlProperty(localName = "timestamp") 21 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyyMMddHHmmss") 22 | protected LocalDateTime timestamp; 23 | @JacksonXmlProperty(localName = "symbols") 24 | @JsonDeserialize(using = NewsHeadlineSymbolsDeserializer.class) 25 | protected List symbols; 26 | @JacksonXmlProperty(localName = "text") 27 | protected String text; 28 | 29 | /** 30 | * Instantiates a new {@link NewsHeadline}. 31 | */ 32 | public NewsHeadline() {} 33 | 34 | /** 35 | * Gets {@link #id}. 36 | * 37 | * @return the ID 38 | */ 39 | public String getID() { 40 | return id; 41 | } 42 | 43 | /** 44 | * Sets {@link #id}. 45 | * 46 | * @param id the ID 47 | */ 48 | public void setID(String id) { 49 | this.id = id; 50 | } 51 | 52 | /** 53 | * Gets {@link #source}. 54 | * 55 | * @return the source 56 | */ 57 | public String getSource() { 58 | return source; 59 | } 60 | 61 | /** 62 | * Sets {@link #source}. 63 | * 64 | * @param source the source 65 | */ 66 | public void setSource(String source) { 67 | this.source = source; 68 | } 69 | 70 | /** 71 | * Gets {@link #timestamp}. 72 | * 73 | * @return the timestamp 74 | */ 75 | public LocalDateTime getTimestamp() { 76 | return timestamp; 77 | } 78 | 79 | /** 80 | * Sets {@link #timestamp}. 81 | * 82 | * @param timestamp the timestamp 83 | */ 84 | public void setTimestamp(LocalDateTime timestamp) { 85 | this.timestamp = timestamp; 86 | } 87 | 88 | /** 89 | * Gets {@link #symbols}. 90 | * 91 | * @return the symbols 92 | */ 93 | public List getSymbols() { 94 | return symbols; 95 | } 96 | 97 | /** 98 | * Sets {@link #symbols}. 99 | * 100 | * @param symbols the symbols 101 | */ 102 | public void setSymbols(List symbols) { 103 | this.symbols = symbols; 104 | } 105 | 106 | /** 107 | * Gets {@link #text}. 108 | * 109 | * @return the text 110 | */ 111 | public String getText() { 112 | return text; 113 | } 114 | 115 | /** 116 | * Sets {@link #text}. 117 | * 118 | * @param text the text 119 | */ 120 | public void setText(String text) { 121 | this.text = text; 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | return "NewsHeadline{" + 127 | "id='" + id + '\'' + 128 | ", source='" + source + '\'' + 129 | ", timestamp=" + timestamp + 130 | ", symbols=" + symbols + 131 | ", text='" + text + '\'' + 132 | '}'; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/headline/NewsHeadlines.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.headline; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; 4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 5 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * {@link NewsHeadlines} contains {@link NewsHeadline}s. 11 | */ 12 | @JacksonXmlRootElement(localName = "news_headlines") 13 | public class NewsHeadlines { 14 | 15 | @JacksonXmlProperty(localName = "news_headline") 16 | @JacksonXmlElementWrapper(useWrapping = false) 17 | private ArrayList headlines; 18 | 19 | /** 20 | * Gets {@link #headlines}. 21 | * 22 | * @return a {@link ArrayList} of {@link NewsHeadline}s 23 | */ 24 | public ArrayList getHeadlines() { 25 | return headlines; 26 | } 27 | 28 | /** 29 | * Sets {@link #headlines}. 30 | * 31 | * @param headlines a {@link ArrayList} of {@link NewsHeadline}s 32 | */ 33 | public void setHeadlines(ArrayList headlines) { 34 | this.headlines = headlines; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "NewsHeadlines{" + 40 | "headlines=" + headlines + 41 | '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/story/NewsStories.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.story; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; 4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 5 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * {@link NewsStories} contains {@link NewsStory}s. 11 | */ 12 | @JacksonXmlRootElement(localName = "news_stories") 13 | public class NewsStories { 14 | 15 | @JacksonXmlProperty(localName = "news_story") 16 | @JacksonXmlElementWrapper(useWrapping = false) 17 | private ArrayList newsStories; 18 | 19 | /** 20 | * Gets {@link #newsStories}. 21 | * 22 | * @return a {@link ArrayList} of {@link NewsStory}s 23 | */ 24 | public ArrayList getNewsStories() { 25 | return newsStories; 26 | } 27 | 28 | /** 29 | * Sets {@link #newsStories}. 30 | * 31 | * @param newsStories a {@link ArrayList} of {@link NewsStory}s 32 | */ 33 | public void setNewsStories(ArrayList newsStories) { 34 | this.newsStories = newsStories; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "NewsStories{" + 40 | "newsStories=" + newsStories + 41 | '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/story/NewsStory.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.story; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 5 | import net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.deserializers.NewsHeadlineSymbolsDeserializer; 6 | import net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.deserializers.NewsStoryIsLinkDeserializer; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * {@link NewsStory} represents a news story from a news feed. 12 | */ 13 | public class NewsStory { 14 | 15 | @JacksonXmlProperty(localName = "is_link") 16 | @JsonDeserialize(using = NewsStoryIsLinkDeserializer.class) 17 | protected Boolean isLink; 18 | @JacksonXmlProperty(localName = "story_text") 19 | protected String text; 20 | @JacksonXmlProperty(localName = "symbols") 21 | @JsonDeserialize(using = NewsHeadlineSymbolsDeserializer.class) 22 | protected List symbols; 23 | 24 | /** 25 | * Instantiates a new {@link NewsStory}. 26 | */ 27 | public NewsStory() {} 28 | 29 | /** 30 | * Gets {@link #isLink}. 31 | * 32 | * @return the link 33 | */ 34 | public Boolean isLink() { 35 | return isLink; 36 | } 37 | 38 | /** 39 | * Sets {@link #isLink}. 40 | * 41 | * @param link the link 42 | */ 43 | public void setLink(Boolean link) { 44 | isLink = link; 45 | } 46 | 47 | /** 48 | * Gets {@link #text}. 49 | * 50 | * @return the text 51 | */ 52 | public String getText() { 53 | return text; 54 | } 55 | 56 | /** 57 | * Sets {@link #text}. 58 | * 59 | * @param text the text 60 | */ 61 | public void setText(String text) { 62 | this.text = text; 63 | } 64 | 65 | /** 66 | * Gets {@link #symbols}. 67 | * 68 | * @return the symbols 69 | */ 70 | public List getSymbols() { 71 | return symbols; 72 | } 73 | 74 | /** 75 | * Sets {@link #symbols}. 76 | * 77 | * @param symbols the symbols 78 | */ 79 | public void setSymbols(List symbols) { 80 | this.symbols = symbols; 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return "NewsStory{" + 86 | "isLink=" + isLink + 87 | ", text='" + text + '\'' + 88 | ", symbols=" + symbols + 89 | '}'; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/storycount/NewsStoryCount.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.storycount; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 4 | 5 | /** 6 | * {@link NewsStoryCount} represents a news story from a news feed. 7 | */ 8 | public class NewsStoryCount { 9 | 10 | @JacksonXmlProperty(localName = "Name") 11 | protected String symbol; 12 | @JacksonXmlProperty(localName = "StoryCount") 13 | protected Integer count; 14 | 15 | /** 16 | * Instantiates a new {@link NewsStoryCount}. 17 | */ 18 | public NewsStoryCount() {} 19 | 20 | /** 21 | * Gets {@link #symbol}. 22 | * 23 | * @return the symbol 24 | */ 25 | public String getSymbol() { 26 | return symbol; 27 | } 28 | 29 | /** 30 | * Sets {@link #symbol}. 31 | * 32 | * @param symbol the symbol 33 | */ 34 | public void setSymbol(String symbol) { 35 | this.symbol = symbol; 36 | } 37 | 38 | /** 39 | * Gets {@link #count}. 40 | * 41 | * @return the count 42 | */ 43 | public Integer getCount() { 44 | return count; 45 | } 46 | 47 | /** 48 | * Sets {@link #count}. 49 | * 50 | * @param count the count 51 | */ 52 | public void setCount(Integer count) { 53 | this.count = count; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "NewsStoryCount{" + 59 | "symbol='" + symbol + '\'' + 60 | ", count=" + count + 61 | '}'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/storycount/NewsStoryCounts.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.storycount; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; 4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 5 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * {@link NewsStoryCounts} contains {@link NewsStoryCount}s. 11 | */ 12 | @JacksonXmlRootElement(localName = "story_counts") 13 | public class NewsStoryCounts { 14 | 15 | @JacksonXmlProperty(localName = "symbol") 16 | @JacksonXmlElementWrapper(useWrapping = false) 17 | private ArrayList newsStoryCounts; 18 | 19 | /** 20 | * Gets {@link #newsStoryCounts}. 21 | * 22 | * @return a {@link ArrayList} of {@link NewsStoryCount}s 23 | */ 24 | public ArrayList getNewsStoryCounts() { 25 | return newsStoryCounts; 26 | } 27 | 28 | /** 29 | * Sets {@link #newsStoryCounts}. 30 | * 31 | * @param newsStoryCounts a {@link ArrayList} of {@link NewsStoryCount}s 32 | */ 33 | public void setNewsStoryCounts(ArrayList newsStoryCounts) { 34 | this.newsStoryCounts = newsStoryCounts; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "NewsStoryCounts{" + 40 | "newsStoryCounts=" + newsStoryCounts + 41 | '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/message/FeedMessageAdapter.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.message; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * {@link FeedMessageAdapter} is an adapter for {@link FeedMessageListener} 8 | */ 9 | public class FeedMessageAdapter implements FeedMessageListener { 10 | 11 | private static final Logger LOGGER = LoggerFactory.getLogger(FeedMessageAdapter.class); 12 | 13 | @Override 14 | public void onMessageReceived(T message) { 15 | LOGGER.info("{}", message); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/message/FeedMessageListener.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.message; 2 | 3 | import net.jacobpeterson.iqfeed4j.feed.AbstractFeed; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * {@link FeedMessageListener} is used to listen to feed messages. 9 | *
10 | * Calling methods in an {@link AbstractFeed} implementation that send feed messages will result in dead-lock! 11 | * Use a separate thread as needed. 12 | * 13 | * @param the type of message 14 | */ 15 | @FunctionalInterface 16 | public interface FeedMessageListener { 17 | 18 | Logger LOGGER = LoggerFactory.getLogger(FeedMessageListener.class); 19 | 20 | /** 21 | * Called when a message is received. This method should NEVER throw an {@link Exception}! 22 | *
23 | * Note that synchronously consuming data via this method will block the underlying feed if the data is not consumed 24 | * fast enough. 25 | * 26 | * @param message the message 27 | */ 28 | void onMessageReceived(T message); 29 | 30 | /** 31 | * Called when a message {@link Exception} has occurred. 32 | * 33 | * @param exception the {@link Exception} 34 | */ 35 | default void onMessageException(Exception exception) { 36 | LOGGER.error("Message Exception!", exception); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/message/MultiMessageAccumulator.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.message; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.ExecutionException; 8 | 9 | /** 10 | * This will accumulate all data/messages in memory to be consumed later. 11 | */ 12 | public class MultiMessageAccumulator extends MultiMessageListener { 13 | 14 | protected final CompletableFuture completionFuture; 15 | protected final List messages; 16 | 17 | /** 18 | * Instantiates a new {@link MultiMessageAccumulator}. 19 | */ 20 | public MultiMessageAccumulator() { 21 | completionFuture = new CompletableFuture<>(); 22 | messages = new ArrayList<>(); 23 | } 24 | 25 | @Override 26 | public void onMessageReceived(T message) { 27 | messages.add(message); 28 | } 29 | 30 | @Override 31 | public void onMessageException(Exception exception) { 32 | completionFuture.completeExceptionally(exception); 33 | } 34 | 35 | @Override 36 | public void onEndOfMultiMessage() { 37 | completionFuture.complete(null); 38 | } 39 | 40 | /** 41 | * Gets the {@link List} for all the messages in {@link #messages}. Note this will block until 42 | * {@link #onEndOfMultiMessage()} has been called by the underlying feed. 43 | * 44 | * @return an {@link List} 45 | * 46 | * @throws ExecutionException thrown for {@link ExecutionException}s 47 | * @throws InterruptedException thrown for {@link InterruptedException}s 48 | */ 49 | public List getMessages() throws ExecutionException, InterruptedException { 50 | completionFuture.get(); 51 | return messages; 52 | } 53 | 54 | /** 55 | * Calls {@link #getMessages()} {@link List#iterator()}. 56 | * 57 | * @return an {@link Iterator} 58 | * 59 | * @throws ExecutionException thrown for {@link ExecutionException}s 60 | * @throws InterruptedException thrown for {@link InterruptedException}s 61 | */ 62 | public Iterator getIterator() throws ExecutionException, InterruptedException { 63 | return getMessages().iterator(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/message/MultiMessageAdapter.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.message; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * {@link MultiMessageAdapter} is an adapter for {@link MultiMessageListener} 8 | */ 9 | public class MultiMessageAdapter extends MultiMessageListener { 10 | 11 | private static final Logger LOGGER = LoggerFactory.getLogger(MultiMessageAdapter.class); 12 | 13 | @Override 14 | public void onMessageReceived(T message) { 15 | LOGGER.info("{}", message); 16 | } 17 | 18 | @Override 19 | public void onEndOfMultiMessage() { 20 | LOGGER.debug("Received End Of Message."); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/message/MultiMessageListener.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.message; 2 | 3 | import net.jacobpeterson.iqfeed4j.feed.AbstractFeed; 4 | 5 | /** 6 | * {@link MultiMessageListener} is an abstract class that represents a {@link FeedMessageListener} for multiple typed 7 | * messages. 8 | * 9 | * @param the type of the message POJO 10 | */ 11 | public abstract class MultiMessageListener implements FeedMessageListener { 12 | 13 | private boolean endOfMultiMessage; 14 | 15 | /** 16 | * Called when all messages have been received. 17 | */ 18 | public abstract void onEndOfMultiMessage(); 19 | 20 | /** 21 | * This is called internally by a {@link AbstractFeed} for when all messages have been received. 22 | */ 23 | public final void handleEndOfMultiMessage() { 24 | endOfMultiMessage = true; 25 | onEndOfMultiMessage(); 26 | } 27 | 28 | /** 29 | * True if no more messages will be received. 30 | * 31 | * @return a boolean 32 | */ 33 | public boolean isEndOfMultiMessage() { 34 | return endOfMultiMessage; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/message/SingleMessageFuture.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.message; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.function.Function; 5 | 6 | /** 7 | * {@link SingleMessageFuture} is a {@link CompletableFuture} that represents a single message in the future. Use 8 | * {@link #get()} to get the message (can be null) and to block the current thread until the resulting 9 | * message is available. 10 | *
11 | * Note that this {@link CompletableFuture} can {@link #completeExceptionally(Throwable)} if there are 12 | * {@link Exception}s thrown when acquiring/converting the associated message. 13 | *
14 | * Also note that any additional {@link CompletableFuture} chaining (e.g. calling 15 | * {@link CompletableFuture#thenApply(Function)} should never block the completion of this 16 | * {@link SingleMessageFuture}!) 17 | * 18 | * @param the type of message 19 | */ 20 | public class SingleMessageFuture extends CompletableFuture { 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/streaming/AbstractServerConnectionFeed.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.streaming; 2 | 3 | import com.google.common.base.Splitter; 4 | import net.jacobpeterson.iqfeed4j.feed.AbstractFeed; 5 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedMessageType; 6 | import net.jacobpeterson.iqfeed4j.model.feed.streaming.common.enums.ServerConnectionStatus; 7 | import org.slf4j.Logger; 8 | 9 | /** 10 | * {@link AbstractServerConnectionFeed} is an {@link AbstractFeed} for feeds that receive the S,SERVER 11 | * CONNECTED>LF< or S,SERVER DISCONNECTED>LF< messages. 12 | */ 13 | public abstract class AbstractServerConnectionFeed extends AbstractFeed { 14 | 15 | protected ServerConnectionStatus serverConnectionStatus; 16 | 17 | /** 18 | * Instantiates a new {@link AbstractServerConnectionFeed}. 19 | * 20 | * @param logger the {@link Logger} 21 | * @param feedName the feed name 22 | * @param hostname the hostname 23 | * @param port the port 24 | * @param csvSplitter the CSV {@link Splitter} 25 | * @param validateProtocolVersion true to send and validate the {@link #CURRENTLY_SUPPORTED_PROTOCOL_VERSION} 26 | * @param sendClientName true to send the client feedName 27 | */ 28 | public AbstractServerConnectionFeed(Logger logger, String feedName, String hostname, int port, Splitter csvSplitter, 29 | boolean validateProtocolVersion, boolean sendClientName) { 30 | super(logger, feedName, hostname, port, csvSplitter, validateProtocolVersion, sendClientName); 31 | } 32 | 33 | /** 34 | * Checks and sets {@link #serverConnectionStatus} if a {@link ServerConnectionStatus} is present. Note this will 35 | * not check if the CSV message is a {@link FeedMessageType#SYSTEM} message so that should be done before calling 36 | * this method. 37 | * 38 | * @param systemMessageType the {@link FeedMessageType#SYSTEM} message type 39 | * 40 | * @return true if the message was a {@link ServerConnectionStatus} message, false otherwise 41 | */ 42 | protected boolean checkServerConnectionStatusMessage(String systemMessageType) { 43 | try { 44 | serverConnectionStatus = ServerConnectionStatus.fromValue(systemMessageType); 45 | 46 | if (serverConnectionStatus == ServerConnectionStatus.SERVER_CONNECTED) { 47 | logger.info("Server is connected."); 48 | } else { 49 | logger.warn("Server is disconnected!"); 50 | } 51 | 52 | return true; 53 | } catch (IllegalArgumentException ignored) { 54 | return false; 55 | } 56 | } 57 | 58 | /** 59 | * Gets {@link #serverConnectionStatus}. 60 | * 61 | * @return the {@link ServerConnectionStatus} 62 | */ 63 | public ServerConnectionStatus getServerConnectionStatus() { 64 | return serverConnectionStatus; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/streaming/StreamingCSVMappers.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.streaming; 2 | 3 | import net.jacobpeterson.iqfeed4j.model.feed.streaming.common.FeedStatistics; 4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper; 5 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.index.IndexCSVMapper; 6 | 7 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.DateTimeConverters.MONTH3_DAY_TIME_AM_PM; 8 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.DOUBLE; 9 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.INTEGER; 10 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.STRING; 11 | 12 | /** 13 | * {@link StreamingCSVMappers} contains {@link AbstractCSVMapper}s for streaming feeds. 14 | */ 15 | public final class StreamingCSVMappers { 16 | 17 | /** 18 | * A {@link IndexCSVMapper} for {@link FeedStatistics}. 19 | */ 20 | public static final IndexCSVMapper FEED_STATISTICS_CSV_MAPPER; 21 | 22 | static { 23 | FEED_STATISTICS_CSV_MAPPER = new IndexCSVMapper<>(FeedStatistics::new); 24 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setServerIP, STRING); 25 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setServerPort, INTEGER); 26 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setMaxSymbols, INTEGER); 27 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setNumberOfSymbols, INTEGER); 28 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setClientsConnected, INTEGER); 29 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setSecondsSinceLastUpdate, INTEGER); 30 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setReconnections, INTEGER); 31 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setAttemptedReconnections, INTEGER); 32 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setStartTime, MONTH3_DAY_TIME_AM_PM); 33 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setMarketTime, MONTH3_DAY_TIME_AM_PM); 34 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setStatus, FeedStatistics.Status::fromValue); 35 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setIQFeedVersion, STRING); 36 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setLoginID, STRING); 37 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setTotalKiloBytesReceived, DOUBLE); 38 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setKiloBytesPerSecReceived, DOUBLE); 39 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setAvgKiloBytesPerSecRecv, DOUBLE); 40 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setTotalKiloBytesSent, DOUBLE); 41 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setKiloBytesPerSecSent, DOUBLE); 42 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setAvgKiloBytesPerSecSent, DOUBLE); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/streaming/derivative/DerivativeFeedEventListener.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.streaming.derivative; 2 | 3 | /** 4 | * {@link DerivativeFeedEventListener} is an arbitrary event listener for {@link DerivativeFeed}. 5 | *
6 | * NOTE: Calling methods in {@link DerivativeFeed} that send feed messages will result in dead-lock! Use a separate 7 | * thread as needed. 8 | */ 9 | public abstract class DerivativeFeedEventListener { 10 | 11 | /** 12 | * Called when a request symbol was not found or the user is not authorized. 13 | * 14 | * @param symbol the symbol 15 | */ 16 | public abstract void onSymbolNotWatched(String symbol); 17 | 18 | /** 19 | * Called when the attempt to watch a symbol has failed, due to the symbol limit being reached. 20 | * 21 | * @param symbol the symbol 22 | */ 23 | public abstract void onSymbolLimitReached(String symbol); 24 | 25 | /** 26 | * Called when the new interval bar watch has replaced the previous watched streaming interval bar watch. 27 | * 28 | * @param symbol the symbol 29 | * @param requestID the request ID 30 | */ 31 | public abstract void onReplacedPreviouslyWatchedSymbol(String symbol, String requestID); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/streaming/level1/Level1FeedEventListener.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.streaming.level1; 2 | 3 | /** 4 | * {@link Level1FeedEventListener} is an arbitrary event listener for {@link Level1Feed}. 5 | *
6 | * Calling methods in {@link Level1Feed} that send feed messages (which is most methods in that class) will 7 | * result in dead-lock! Use a separate thread as needed. 8 | */ 9 | public abstract class Level1FeedEventListener { 10 | 11 | /** 12 | * Called when the attempt to reconnect failed. 13 | */ 14 | public abstract void onServerReconnectFailed(); 15 | 16 | /** 17 | * Called when the attempt to watch a symbol has failed, due to the symbol limit being reached. 18 | * 19 | * @param symbol the symbol 20 | */ 21 | public abstract void onSymbolLimitReached(String symbol); 22 | 23 | /** 24 | * Called when a request symbol was not found or the user is not authorized. 25 | * 26 | * @param symbol the symbol 27 | */ 28 | public abstract void onSymbolNotWatched(String symbol); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/feed/streaming/marketdepth/MarketDepthFeed.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.feed.streaming.marketdepth; 2 | 3 | public class MarketDepthFeed { 4 | // TODO MarketDepthFeed 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/properties/IQFeed4jProperties.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.properties; 2 | 3 | import net.jacobpeterson.iqfeed4j.IQFeed4j; 4 | 5 | import static net.jacobpeterson.iqfeed4j.util.properties.PropertyUtil.getProperty; 6 | 7 | /** 8 | * {@link IQFeed4jProperties} defines properties for {@link IQFeed4j}. 9 | */ 10 | public class IQFeed4jProperties { 11 | 12 | /** The properties file name. */ 13 | public static final String PROPERTIES_FILE = "iqfeed4j.properties"; 14 | /** The default properties file name. */ 15 | public static final String DEFAULT_PROPERTIES_FILE = "iqfeed4j.default.properties"; 16 | 17 | private static final String IQCONNECT_COMMAND_KEY = "iqconnect_command"; 18 | /** The value of {@link #IQCONNECT_COMMAND_KEY} in {@link #PROPERTIES_FILE}. */ 19 | public static final String IQCONNECT_COMMAND = getProperty( 20 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 21 | IQCONNECT_COMMAND_KEY); 22 | 23 | private static final String PRODUCT_ID_KEY = "product_id"; 24 | /** The value of {@link #PRODUCT_ID_KEY} in {@link #PROPERTIES_FILE}. */ 25 | public static final String PRODUCT_ID = getProperty( 26 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 27 | PRODUCT_ID_KEY); 28 | 29 | private static final String APPLICATION_VERSION_KEY = "application_version"; 30 | /** The value of {@link #APPLICATION_VERSION_KEY} in {@link #PROPERTIES_FILE}. */ 31 | public static final String APPLICATION_VERSION = getProperty( 32 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 33 | APPLICATION_VERSION_KEY); 34 | 35 | private static final String LOGIN_KEY = "login"; 36 | /** The value of {@link #LOGIN_KEY} in {@link #PROPERTIES_FILE}. */ 37 | public static final String LOGIN = getProperty( 38 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 39 | LOGIN_KEY); 40 | 41 | private static final String PASSWORD_KEY = "password"; 42 | /** The value of {@link #PASSWORD_KEY} in {@link #PROPERTIES_FILE}. */ 43 | public static final String PASSWORD = getProperty( 44 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 45 | PASSWORD_KEY); 46 | 47 | private static final String AUTOCONNECT_KEY = "autoconnect"; 48 | /** The value of {@link #AUTOCONNECT_KEY} in {@link #PROPERTIES_FILE}. */ 49 | public static final Boolean AUTOCONNECT = Boolean.parseBoolean(getProperty( 50 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 51 | AUTOCONNECT_KEY)); 52 | 53 | private static final String SAVE_LOGIN_INFO_KEY = "save_login_info"; 54 | /** The value of {@link #SAVE_LOGIN_INFO_KEY} in {@link #PROPERTIES_FILE}. */ 55 | public static final Boolean SAVE_LOGIN_INFO = Boolean.parseBoolean(getProperty( 56 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 57 | SAVE_LOGIN_INFO_KEY)); 58 | 59 | private static final String FEED_NAME_KEY = "feed_name"; 60 | /** The value of {@link #FEED_NAME_KEY} in {@link #PROPERTIES_FILE}. */ 61 | public static final String FEED_NAME = getProperty( 62 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 63 | FEED_NAME_KEY); 64 | 65 | private static final String FEED_HOSTNAME_KEY = "feed_hostname"; 66 | /** The value of {@link #FEED_HOSTNAME_KEY} in {@link #PROPERTIES_FILE}. */ 67 | public static final String FEED_HOSTNAME = getProperty( 68 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 69 | FEED_HOSTNAME_KEY); 70 | 71 | private static final String LEVEL_1_FEED_PORT_KEY = "level_1_feed_port"; 72 | /** The value of {@link #LEVEL_1_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */ 73 | public static final Integer LEVEL_1_FEED_PORT = Integer.parseInt(getProperty( 74 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 75 | LEVEL_1_FEED_PORT_KEY)); 76 | 77 | private static final String MARKET_DEPTH_FEED_PORT_KEY = "market_depth_feed_port"; 78 | /** The value of {@link #MARKET_DEPTH_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */ 79 | public static final Integer MARKET_DEPTH_FEED_PORT = Integer.parseInt(getProperty( 80 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 81 | MARKET_DEPTH_FEED_PORT_KEY)); 82 | 83 | private static final String DERIVATIVE_FEED_PORT_KEY = "derivative_feed_port"; 84 | /** The value of {@link #DERIVATIVE_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */ 85 | public static final Integer DERIVATIVE_FEED_PORT = Integer.parseInt(getProperty( 86 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 87 | DERIVATIVE_FEED_PORT_KEY)); 88 | 89 | private static final String ADMIN_FEED_PORT_KEY = "admin_feed_port"; 90 | /** The value of {@link #ADMIN_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */ 91 | public static final Integer ADMIN_FEED_PORT = Integer.parseInt(getProperty( 92 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 93 | ADMIN_FEED_PORT_KEY)); 94 | 95 | private static final String LOOKUP_FEED_PORT_KEY = "lookup_feed_port"; 96 | /** The value of {@link #LOOKUP_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */ 97 | public static final Integer LOOKUP_FEED_PORT = Integer.parseInt(getProperty( 98 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE, 99 | LOOKUP_FEED_PORT_KEY)); 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/chars/CharUtil.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.chars; 2 | 3 | /** 4 | * {@link CharUtil} defines utility methods for {@link Character} or the primitive char type. 5 | */ 6 | public final class CharUtil { 7 | 8 | /** 9 | * Finds the last index of a non-number character in a {@link String}, or -1 if none was found. 10 | * 11 | * @param string the string to search 12 | * @param qualifyDecimalPoint true to qualify a decimal point as a number 13 | * @param qualifyPlusOrMinus true to qualify a '+' or a '-' character 14 | * 15 | * @return the index 16 | */ 17 | public static int lastIndexOfNonNumber(String string, boolean qualifyDecimalPoint, boolean qualifyPlusOrMinus) { 18 | if (string.isEmpty()) { 19 | return -1; 20 | } 21 | 22 | // Start at end of string 23 | for (int index = string.length() - 1; index >= 0; index--) { 24 | char ch = string.charAt(index); 25 | if (!(ch == '0' || ch == '1' || ch == '2' || ch == '3' || ch == '4' || ch == '5' || ch == '6' || 26 | ch == '7' || ch == '8' || ch == '9' || (qualifyDecimalPoint && ch == '.') || 27 | (qualifyPlusOrMinus && (ch == '+' || ch == '-')))) { 28 | return index; 29 | } 30 | } 31 | 32 | return -1; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/CSVUtil.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv; 2 | 3 | /** 4 | * {@link CSVUtil} defines utility methods for CSVs. 5 | */ 6 | public final class CSVUtil { 7 | 8 | /** 9 | * Tests if the csv array has index. 10 | * 11 | * @param csv the CSV 12 | * @param index the index 13 | * 14 | * @return a boolean 15 | */ 16 | public static boolean valueExists(String[] csv, int index) { 17 | return index < csv.length; 18 | } 19 | 20 | /** 21 | * Tests if the csv array has index and if the value at index is not empty. 22 | * 23 | * @param csv the CSV 24 | * @param index the index 25 | * 26 | * @return a boolean 27 | */ 28 | public static boolean valuePresent(String[] csv, int index) { 29 | return index < csv.length && !csv[index].isEmpty(); 30 | } 31 | 32 | /** 33 | * Tests if the csv array has index and if the value at index is not empty 34 | * and not whitespace. 35 | * 36 | * @param csv the CSV 37 | * @param index the index 38 | * 39 | * @return a boolean 40 | */ 41 | public static boolean valueNotWhitespace(String[] csv, int index) { 42 | return index < csv.length && !csv[index].isEmpty() && !csv[index].trim().isEmpty(); 43 | } 44 | 45 | /** 46 | * Tests if the csv array has match at index. 47 | * 48 | * @param csv the CSV 49 | * @param index the index 50 | * @param match the string to check 51 | * 52 | * @return the boolean 53 | */ 54 | public static boolean valueEquals(String[] csv, int index, String match) { 55 | return valueExists(csv, index) && csv[index].equals(match); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/AbstractCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper; 2 | 3 | import java.time.LocalDate; 4 | import java.time.LocalDateTime; 5 | import java.time.LocalTime; 6 | import java.time.format.DateTimeFormatter; 7 | import java.time.format.DateTimeFormatterBuilder; 8 | import java.time.temporal.ChronoField; 9 | import java.util.Locale; 10 | import java.util.function.Function; 11 | import java.util.function.Supplier; 12 | 13 | /** 14 | * {@link AbstractCSVMapper} maps CSV {@link String} values to various types/objects. 15 | * 16 | * @param the type of CSV mapping 17 | */ 18 | public abstract class AbstractCSVMapper { 19 | 20 | /** 21 | * {@link PrimitiveConvertors} contains common {@link Function}s with the argument being the CSV {@link String} 22 | * value and the return value being the converted CSV primitive value. 23 | */ 24 | public static class PrimitiveConvertors { 25 | 26 | public static final Function STRING = (value) -> value; 27 | public static final Function CHAR = (value) -> value.length() == 0 ? '\0' : value.charAt(0); 28 | public static final Function BOOLEAN = Boolean::valueOf; 29 | public static final Function BYTE = Byte::valueOf; 30 | public static final Function SHORT = Short::valueOf; 31 | public static final Function INTEGER = Integer::valueOf; 32 | public static final Function LONG = Long::valueOf; 33 | public static final Function FLOAT = Float::valueOf; 34 | public static final Function DOUBLE = Double::valueOf; 35 | } 36 | 37 | /** 38 | * {@link DateTimeFormatters} contains various {@link DateTimeFormatters} for formatting/converting from/to CSV. 39 | */ 40 | public static class DateTimeFormatters { 41 | 42 | /** Format of: HHmmss */ 43 | public static final DateTimeFormatter TIME = DateTimeFormatter.ofPattern("HHmmss"); 44 | 45 | /** Format of: yyyyMMdd */ 46 | public static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyyMMdd"); 47 | 48 | /** Format of: yyyyMMdd HHmmss */ 49 | public static final DateTimeFormatter DATE_SPACE_TIME = 50 | DateTimeFormatter.ofPattern("yyyyMMdd HHmmss"); 51 | 52 | /** Format of: MMM dd h:mma */ 53 | public static final DateTimeFormatter MONTH3_DAY_TIME_AM_PM = 54 | new DateTimeFormatterBuilder() 55 | .appendPattern("MMM dd h:mma") 56 | .parseDefaulting(ChronoField.YEAR, LocalDate.now().getYear()) 57 | .toFormatter(Locale.ENGLISH); 58 | 59 | /** Format of: yyyy-MM-dd HH:mm:ss.nnnnnn */ 60 | public static final DateTimeFormatter DASHED_DATE_SPACE_TIME_FRACTIONAL = 61 | new DateTimeFormatterBuilder() 62 | .parseCaseInsensitive() 63 | .append(DateTimeFormatter.ISO_LOCAL_DATE) 64 | .appendLiteral(' ') 65 | .append(DateTimeFormatter.ISO_LOCAL_TIME) // Optionally includes micro/nanoseconds 66 | .toFormatter(Locale.ENGLISH); 67 | 68 | /** Format of: yyyy-MM-dd */ 69 | public static final DateTimeFormatter DASHED_DATE = DateTimeFormatter.ISO_LOCAL_DATE; 70 | 71 | /** Format of: MM/dd/yyyy */ 72 | public static final DateTimeFormatter SLASHED_DATE = DateTimeFormatter.ofPattern("MM/dd/yyyy"); 73 | 74 | /** Format of: HH:mm:ss[.nnnnnn] */ 75 | public static final DateTimeFormatter COLON_TIME = DateTimeFormatter.ISO_LOCAL_TIME; 76 | 77 | /** Format of: yyyyMMdd HH:mm:ss */ 78 | public static final DateTimeFormatter DATE_SPACE_COLON_TIME = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss"); 79 | 80 | /** Format of: [Hmmss][HHmmss] */ 81 | public static final DateTimeFormatter OPTIONAL_1_OR_2_DIGIT_HOUR_TIME = 82 | DateTimeFormatter.ofPattern("[Hmmss][HHmmss]"); 83 | } 84 | 85 | /** 86 | * {@link DateTimeConverters} contains common {@link Function}s with the argument being the CSV {@link String} value 87 | * and the return value being the converted CSV date/time value. 88 | */ 89 | public static class DateTimeConverters { 90 | 91 | /** Convertor using {@link DateTimeFormatters#TIME} */ 92 | public static final Function TIME = 93 | value -> LocalTime.parse(value, DateTimeFormatters.TIME); 94 | 95 | /** Convertor using {@link DateTimeFormatters#DATE} */ 96 | public static final Function DATE = 97 | value -> LocalDate.parse(value, DateTimeFormatters.DATE); 98 | 99 | /** Convertor using {@link DateTimeFormatters#DATE_SPACE_TIME} */ 100 | public static final Function DATE_SPACE_TIME = 101 | value -> LocalDateTime.parse(value, DateTimeFormatters.DATE_SPACE_TIME); 102 | 103 | /** Convertor using {@link DateTimeFormatters#MONTH3_DAY_TIME_AM_PM} */ 104 | public static final Function MONTH3_DAY_TIME_AM_PM = 105 | value -> LocalDateTime.parse(value, DateTimeFormatters.MONTH3_DAY_TIME_AM_PM); 106 | 107 | /** Convertor using {@link DateTimeFormatters#DASHED_DATE_SPACE_TIME_FRACTIONAL} */ 108 | public static final Function DASHED_DATE_SPACE_TIME_FRACTIONAL = 109 | value -> LocalDateTime.parse(value, DateTimeFormatters.DASHED_DATE_SPACE_TIME_FRACTIONAL); 110 | 111 | /** Convertor using {@link DateTimeFormatters#DASHED_DATE_SPACE_TIME_FRACTIONAL} */ 112 | public static final Function DASHED_DATE_SPACE_TIME = DASHED_DATE_SPACE_TIME_FRACTIONAL; 113 | 114 | /** Convertor using {@link DateTimeFormatters#DASHED_DATE} */ 115 | public static final Function DASHED_DATE = 116 | value -> LocalDate.parse(value, DateTimeFormatters.DASHED_DATE); 117 | 118 | /** Convertor using {@link DateTimeFormatters#SLASHED_DATE} */ 119 | public static final Function SLASHED_DATE = 120 | value -> LocalDate.parse(value, DateTimeFormatters.SLASHED_DATE); 121 | 122 | /** Convertor using {@link DateTimeFormatters#COLON_TIME} */ 123 | public static final Function COLON_TIME = 124 | value -> LocalTime.parse(value, DateTimeFormatters.COLON_TIME); 125 | 126 | /** Convertor using {@link DateTimeFormatters#DATE_SPACE_COLON_TIME} */ 127 | public static final Function DATE_SPACE_COLON_TIME = 128 | value -> LocalDateTime.parse(value, DateTimeFormatters.DATE_SPACE_COLON_TIME); 129 | 130 | /** Convertor using {@link DateTimeFormatters#OPTIONAL_1_OR_2_DIGIT_HOUR_TIME} */ 131 | public static final Function OPTIONAL_1_OR_2_DIGIT_HOUR_TIME = 132 | value -> LocalTime.parse(value, DateTimeFormatters.OPTIONAL_1_OR_2_DIGIT_HOUR_TIME); 133 | 134 | /** Calls {@link #OPTIONAL_1_OR_2_DIGIT_HOUR_TIME} but returns null on an {@link Exception}. */ 135 | public static final Function OPTIONAL_1_OR_2_DIGIT_HOUR_TIME_NULLABLE = value -> { 136 | try { 137 | return OPTIONAL_1_OR_2_DIGIT_HOUR_TIME.apply(value); 138 | } catch (Exception exception) { 139 | return null; 140 | } 141 | }; 142 | } 143 | 144 | protected final Supplier pojoInstantiator; 145 | 146 | /** 147 | * Instantiates a new {@link AbstractCSVMapper}. 148 | * 149 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO 150 | */ 151 | public AbstractCSVMapper(Supplier pojoInstantiator) { 152 | this.pojoInstantiator = pojoInstantiator; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/CSVMapping.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper; 2 | 3 | import java.util.function.BiConsumer; 4 | import java.util.function.Function; 5 | 6 | /** 7 | * {@link CSVMapping} holds a mapping for a CSV value to POJO field mapping. 8 | * 9 | * @param the type of the POJO 10 | * @param

the type of the POJO field 11 | */ 12 | public final class CSVMapping { 13 | 14 | private final BiConsumer mappingConsumer; 15 | 16 | /** 17 | * Instantiates a new {@link CSVMapping}. 18 | * 19 | * @param fieldSetter the field setter {@link BiConsumer}. The first argument is the POJO instance and 20 | * the second argument is the converted field to be set in the instance. 21 | * @param stringToFieldConverter the string to field converter {@link Function}. The return type is the converted 22 | * POJO field instance and the argument is the CSV value {@link String}. 23 | */ 24 | public CSVMapping(BiConsumer fieldSetter, Function stringToFieldConverter) { 25 | mappingConsumer = (instance, value) -> fieldSetter.accept(instance, stringToFieldConverter.apply(value)); 26 | } 27 | 28 | /** 29 | * Instantiates a new {@link CSVMapping}. 30 | * 31 | * @param csvValueConsumer a {@link BiConsumer} that will takes a POJO instance as the first argument, and a CSV 32 | * {@link String} value as the second argument. This is so that fields inside the POJO can 33 | * be directly set according to the passed in CSV {@link String} value. 34 | */ 35 | public CSVMapping(BiConsumer csvValueConsumer) { 36 | mappingConsumer = csvValueConsumer; 37 | } 38 | 39 | /** 40 | * Applies the mapping. Note this could throw a variety of {@link Exception}s. 41 | * 42 | * @param instance the POJO instance 43 | * @param value the CSV value 44 | */ 45 | public void apply(T instance, String value) { 46 | mappingConsumer.accept(instance, value); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/CSVMappingException.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper; 2 | 3 | /** 4 | * {@link CSVMappingException} is a {@link RuntimeException} for CSV mapping errors. 5 | */ 6 | public class CSVMappingException extends RuntimeException { 7 | 8 | /** 9 | * Instantiates a new {@link CSVMappingException}. 10 | */ 11 | public CSVMappingException() {} 12 | 13 | /** 14 | * Instantiates a new {@link CSVMappingException}. 15 | * 16 | * @param message the message 17 | */ 18 | public CSVMappingException(String message) { 19 | super(message); 20 | } 21 | 22 | /** 23 | * Instantiates a new {@link CSVMappingException}. 24 | * 25 | * @param message the message 26 | * @param cause the cause 27 | */ 28 | public CSVMappingException(String message, Throwable cause) { 29 | super(message, cause); 30 | } 31 | 32 | /** 33 | * Instantiates a new {@link CSVMappingException} 34 | * 35 | * @param cause the cause 36 | */ 37 | public CSVMappingException(Throwable cause) { 38 | super(cause); 39 | } 40 | 41 | /** 42 | * Instantiates a new {@link CSVMappingException} 43 | * 44 | * @param csvIndex the CSV index 45 | * @param cause the cause 46 | */ 47 | public CSVMappingException(int csvIndex, Throwable cause) { 48 | super(String.format("Error mapping at index %d!", csvIndex), cause); 49 | } 50 | 51 | /** 52 | * Instantiates a new {@link CSVMappingException} 53 | * 54 | * @param csvIndex the CSV index 55 | * @param offset the offset index 56 | * @param cause the cause 57 | */ 58 | public CSVMappingException(int csvIndex, int offset, Throwable cause) { 59 | super(String.format("Error mapping at index %d with offset %d!", csvIndex, offset), cause); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/index/AbstractIndexCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.index; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper; 4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping; 5 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 6 | 7 | import java.util.function.Supplier; 8 | 9 | /** 10 | * {@link AbstractIndexCSVMapper} mappings are based off of predefined CSV indices. 11 | */ 12 | public abstract class AbstractIndexCSVMapper extends AbstractCSVMapper { 13 | 14 | /** 15 | * Instantiates a new {@link AbstractIndexCSVMapper}. 16 | * 17 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO 18 | */ 19 | public AbstractIndexCSVMapper(Supplier pojoInstantiator) { 20 | super(pojoInstantiator); 21 | } 22 | 23 | /** 24 | * Maps the given CSV to a POJO. 25 | * 26 | * @param csv the CSV 27 | * @param offset offset to add to CSV indices when applying {@link CSVMapping}s 28 | * 29 | * @return a new POJO 30 | * 31 | * @throws CSVMappingException thrown for {@link CSVMappingException}s 32 | */ 33 | public abstract T map(String[] csv, int offset); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/index/DirectIndexCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.index; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 4 | 5 | import java.util.function.Function; 6 | 7 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace; 8 | 9 | /** 10 | * {@link DirectIndexCSVMapper} mappings are based off of predefined CSV indices, but without any POJO instantiation and 11 | * rather uses direct type conversion. 12 | */ 13 | public class DirectIndexCSVMapper extends AbstractIndexCSVMapper { 14 | 15 | protected final int csvIndex; 16 | protected final Function stringToTypeConverter; 17 | 18 | /** 19 | * Instantiates a new {@link DirectIndexCSVMapper}. 20 | * 21 | * @param csvIndex the CSV index 22 | * @param stringToTypeConverter a {@link Function} that will takes a CSV value {@link String} as the argument, and 23 | * returns the converted CSV value type. 24 | */ 25 | public DirectIndexCSVMapper(int csvIndex, Function stringToTypeConverter) { 26 | super(null); 27 | 28 | this.csvIndex = csvIndex; 29 | this.stringToTypeConverter = stringToTypeConverter; 30 | } 31 | 32 | /** 33 | *
34 | * Note: this will map to a type using the {@link #stringToTypeConverter}. 35 | */ 36 | @Override 37 | public T map(String[] csv, int offset) { 38 | if (!valueNotWhitespace(csv, csvIndex + offset)) { // Don't map empty CSV values 39 | return null; 40 | } 41 | 42 | // apply() could throw a variety of exceptions 43 | try { 44 | return stringToTypeConverter.apply(csv[csvIndex + offset]); 45 | } catch (Exception exception) { 46 | throw new CSVMappingException(csvIndex, offset, exception); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/index/IndexCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.index; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping; 4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 5 | 6 | import java.util.HashMap; 7 | import java.util.function.BiConsumer; 8 | import java.util.function.Function; 9 | import java.util.function.Supplier; 10 | 11 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace; 12 | 13 | /** 14 | */ 15 | public class IndexCSVMapper extends AbstractIndexCSVMapper { 16 | 17 | protected final HashMap> csvMappingsOfCSVIndices; 18 | 19 | /** 20 | * Instantiates a new {@link IndexCSVMapper}. 21 | * 22 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO 23 | */ 24 | public IndexCSVMapper(Supplier pojoInstantiator) { 25 | super(pojoInstantiator); 26 | 27 | csvMappingsOfCSVIndices = new HashMap<>(); 28 | } 29 | 30 | /** 31 | * Adds a CSV index to POJO field mapping as the CSV index being the largest mapped CSV index + 1. 32 | * 33 | * @param

the type of the POJO field 34 | * @param fieldSetter see {@link CSVMapping} constructor doc 35 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc 36 | */ 37 | public

void addMapping(BiConsumer fieldSetter, Function stringToFieldConverter) { 38 | setMapping(getNextCSVIndex(), fieldSetter, stringToFieldConverter); 39 | } 40 | 41 | /** 42 | * Adds a CSV index to POJO field mapping as the CSV index being the largest mapped CSV index + 1. 43 | * 44 | * @param csvValueConsumer see {@link CSVMapping} constructor doc 45 | */ 46 | public void addMapping(BiConsumer csvValueConsumer) { 47 | setMapping(getNextCSVIndex(), csvValueConsumer); 48 | } 49 | 50 | /** 51 | * Adds a CSV index to POJO field mapping as the CSV index being the largest mapped CSV index + 1. 52 | * 53 | * @param csvMapping the {@link CSVMapping} 54 | */ 55 | public void addMapping(CSVMapping csvMapping) { 56 | setMapping(getNextCSVIndex(), csvMapping); 57 | } 58 | 59 | /** 60 | * Gets the largest CSV index in {@link #csvMappingsOfCSVIndices} + 1. 61 | * 62 | * @return an int 63 | */ 64 | private int getNextCSVIndex() { 65 | return csvMappingsOfCSVIndices.keySet().stream().max(Integer::compareTo).orElse(-1) + 1; 66 | } 67 | 68 | /** 69 | * Sets a CSV index to POJO field mapping. 70 | * 71 | * @param

the type of the POJO field 72 | * @param csvIndex the CSV index 73 | * @param fieldSetter see {@link CSVMapping} constructor doc 74 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc 75 | */ 76 | public

void setMapping(int csvIndex, BiConsumer fieldSetter, Function stringToFieldConverter) { 77 | csvMappingsOfCSVIndices.put(csvIndex, new CSVMapping<>(fieldSetter, stringToFieldConverter)); 78 | } 79 | 80 | /** 81 | * Sets a CSV index to POJO field mapping. 82 | * 83 | * @param

the type parameter 84 | * @param csvIndex the CSV index 85 | * @param csvValueConsumer see {@link CSVMapping} constructor doc 86 | */ 87 | public

void setMapping(int csvIndex, BiConsumer csvValueConsumer) { 88 | csvMappingsOfCSVIndices.put(csvIndex, new CSVMapping<>(csvValueConsumer)); 89 | } 90 | 91 | /** 92 | * Sets a CSV index to POJO field mapping. 93 | * 94 | * @param csvIndex the CSV index 95 | * @param csvMapping the {@link CSVMapping} 96 | */ 97 | public void setMapping(int csvIndex, CSVMapping csvMapping) { 98 | csvMappingsOfCSVIndices.put(csvIndex, csvMapping); 99 | } 100 | 101 | /** 102 | * Removes a CSV mapping, if it exists. 103 | * 104 | * @param csvIndex the CSV index 105 | */ 106 | public void removeMapping(int csvIndex) { 107 | csvMappingsOfCSVIndices.remove(csvIndex); 108 | } 109 | 110 | /** 111 | *
112 | * Note this will map with the mappings added via {@link #setMapping(int, BiConsumer, Function)}. 113 | */ 114 | @Override 115 | public T map(String[] csv, int offset) { 116 | T instance = pojoInstantiator.get(); 117 | 118 | // Loop through all added 'CSVMappings' and apply them 119 | for (int csvIndex : csvMappingsOfCSVIndices.keySet()) { 120 | if (!valueNotWhitespace(csv, csvIndex + offset)) { // Don't map empty CSV values 121 | continue; 122 | } 123 | 124 | // apply() could throw a variety of exceptions 125 | try { 126 | csvMappingsOfCSVIndices.get(csvIndex).apply(instance, csv[csvIndex + offset]); 127 | } catch (Exception exception) { 128 | throw new CSVMappingException(csvIndex, offset, exception); 129 | } 130 | } 131 | 132 | return instance; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/index/TrailingIndexCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.index; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping; 4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 5 | 6 | import java.util.HashMap; 7 | import java.util.function.BiConsumer; 8 | import java.util.function.Function; 9 | import java.util.function.Supplier; 10 | 11 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueExists; 12 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace; 13 | 14 | /** 15 | * {@link TrailingIndexCSVMapper} mappings are based off of predefined CSV indices, but with the ability to add a CSV 16 | * mapping that accumulates all CSV values after a certain index into one mapping. 17 | */ 18 | public class TrailingIndexCSVMapper extends AbstractIndexCSVMapper { 19 | 20 | protected final HashMap> csvMappingsOfCSVIndices; 21 | protected int trailingCSVIndex; 22 | protected CSVMapping trailingCSVMapping; 23 | 24 | /** 25 | * Instantiates a new {@link TrailingIndexCSVMapper}. 26 | * 27 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO 28 | */ 29 | public TrailingIndexCSVMapper(Supplier pojoInstantiator) { 30 | super(pojoInstantiator); 31 | 32 | csvMappingsOfCSVIndices = new HashMap<>(); 33 | } 34 | 35 | /** 36 | * Adds a CSV index to POJO field mapping as the CSV index being the largest 37 | * {@link #setMapping(int, BiConsumer, Function)} CSV index + 1. 38 | * 39 | * @param

the type of the POJO field 40 | * @param fieldSetter see {@link CSVMapping} constructor doc 41 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc 42 | */ 43 | public

void addMapping(BiConsumer fieldSetter, Function stringToFieldConverter) { 44 | int nextCSVIndex = csvMappingsOfCSVIndices.keySet().stream().max(Integer::compareTo).orElse(-1) + 1; 45 | setMapping(nextCSVIndex, fieldSetter, stringToFieldConverter); 46 | } 47 | 48 | /** 49 | * Sets a CSV index to POJO field mapping. 50 | * 51 | * @param

the type of the POJO field 52 | * @param csvIndex the CSV index 53 | * @param fieldSetter see {@link CSVMapping} constructor doc 54 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc 55 | */ 56 | @SuppressWarnings("OptionalGetWithoutIsPresent") // Optional will always be present here 57 | public

void setMapping(int csvIndex, BiConsumer fieldSetter, Function stringToFieldConverter) { 58 | csvMappingsOfCSVIndices.put(csvIndex, new CSVMapping<>(fieldSetter, stringToFieldConverter)); 59 | trailingCSVIndex = csvMappingsOfCSVIndices.keySet().stream().max(Integer::compareTo).get() + 1; 60 | } 61 | 62 | /** 63 | * Removes a CSV mapping, if it exists. 64 | * 65 | * @param csvIndex the CSV index 66 | */ 67 | public void removeMapping(int csvIndex) { 68 | csvMappingsOfCSVIndices.remove(csvIndex); 69 | } 70 | 71 | /** 72 | * Sets the trailing CSV mapping that accumulates/maps all CSV values after the being the largest 73 | * {@link #setMapping(int, BiConsumer, Function)} CSV index + 1. 74 | * 75 | * @param

the type of the POJO field 76 | * @param fieldSetter see {@link CSVMapping} constructor doc 77 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc 78 | */ 79 | public

void setTrailingMapping(BiConsumer fieldSetter, Function stringToFieldConverter) { 80 | this.trailingCSVMapping = new CSVMapping<>(fieldSetter, stringToFieldConverter); 81 | } 82 | 83 | /** 84 | *
85 | * Note this will map with the mappings added via {@link #setMapping(int, BiConsumer, Function)} and 86 | * {@link #setTrailingMapping(BiConsumer, Function)}. 87 | */ 88 | @Override 89 | public T map(String[] csv, int offset) { 90 | T instance = pojoInstantiator.get(); 91 | 92 | // Loop through all added 'CSVMapping's and apply them 93 | for (int csvIndex : csvMappingsOfCSVIndices.keySet()) { 94 | if (!valueNotWhitespace(csv, csvIndex + offset)) { // Don't map empty CSV values 95 | continue; 96 | } 97 | 98 | // apply() could throw a variety of exceptions 99 | try { 100 | csvMappingsOfCSVIndices.get(csvIndex).apply(instance, csv[csvIndex + offset]); 101 | } catch (Exception exception) { 102 | throw new CSVMappingException(csvIndex, offset, exception); 103 | } 104 | } 105 | 106 | // Now apply 'trailingCSVMapping' on trailing CSV values 107 | if (valueExists(csv, trailingCSVIndex + offset)) { 108 | StringBuilder trailingCSVBuilder = new StringBuilder(); 109 | for (int csvIndex = trailingCSVIndex + offset; csvIndex < csv.length; csvIndex++) { 110 | trailingCSVBuilder.append(csv[csvIndex]); 111 | 112 | if (csvIndex < csv.length - 1) { // Don't append comma on last CSV value 113 | trailingCSVBuilder.append(","); 114 | } 115 | } 116 | 117 | String trailingCSVString = trailingCSVBuilder.toString(); 118 | // apply() could throw a variety of exceptions 119 | try { 120 | trailingCSVMapping.apply(instance, trailingCSVString); 121 | } catch (Exception exception) { 122 | throw new CSVMappingException(trailingCSVIndex, offset, exception); 123 | } 124 | } 125 | 126 | return instance; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/list/AbstractListCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.list; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper; 4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping; 5 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 6 | 7 | import java.util.List; 8 | import java.util.function.Supplier; 9 | 10 | /** 11 | * {@link AbstractListCSVMapper} maps a CSV list. 12 | */ 13 | public abstract class AbstractListCSVMapper extends AbstractCSVMapper { 14 | 15 | /** 16 | * Instantiates a new {@link AbstractListCSVMapper}. 17 | * 18 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO 19 | */ 20 | public AbstractListCSVMapper(Supplier pojoInstantiator) { 21 | super(pojoInstantiator); 22 | } 23 | 24 | /** 25 | * Maps the given csv list to a {@link List}. 26 | * 27 | * @param csv the CSV 28 | * @param offset offset to add to CSV indices when applying {@link CSVMapping}s 29 | * 30 | * @return a new {@link List} of mapped POJOs 31 | * 32 | * @throws CSVMappingException thrown for {@link CSVMappingException}s 33 | */ 34 | public abstract List mapToList(String[] csv, int offset); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/list/DirectListCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.list; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 4 | 5 | import java.util.List; 6 | import java.util.function.Function; 7 | import java.util.function.Supplier; 8 | 9 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace; 10 | 11 | /** 12 | * {@link DirectListCSVMapper} maps a CSV list to a {@link List} using a direct type conversion. 13 | */ 14 | public class DirectListCSVMapper extends AbstractListCSVMapper { 15 | 16 | protected final Supplier> listInstantiator; 17 | protected final Function stringToTypeConverter; 18 | 19 | /** 20 | * Instantiates a new {@link DirectListCSVMapper}. 21 | * 22 | * @param listInstantiator a {@link Supplier} to instantiate a new {@link List} 23 | * @param stringToTypeConverter a {@link Function} that will takes a CSV value {@link String} as the argument, and 24 | * returns the converted CSV value type. 25 | */ 26 | public DirectListCSVMapper(Supplier> listInstantiator, 27 | Function stringToTypeConverter) { 28 | super(null); 29 | 30 | this.listInstantiator = listInstantiator; 31 | this.stringToTypeConverter = stringToTypeConverter; 32 | } 33 | 34 | /** 35 | *
36 | * Note: this will map to a list with the {@link #stringToTypeConverter} applied. 37 | */ 38 | @Override 39 | public List mapToList(String[] csv, int offset) { 40 | List mappedList = listInstantiator.get(); 41 | 42 | for (int csvIndex = offset; csvIndex < csv.length; csvIndex++) { 43 | if (!valueNotWhitespace(csv, csvIndex)) { // Add null for empty CSV values 44 | mappedList.add(null); 45 | continue; 46 | } 47 | 48 | // accept() could throw a variety of exceptions 49 | try { 50 | mappedList.add(stringToTypeConverter.apply(csv[csvIndex])); 51 | } catch (Exception exception) { 52 | throw new CSVMappingException(csvIndex - offset, offset, exception); 53 | } 54 | } 55 | 56 | return mappedList; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/list/ListCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.list; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 4 | 5 | import java.util.List; 6 | import java.util.function.BiConsumer; 7 | import java.util.function.Supplier; 8 | import java.util.regex.Pattern; 9 | 10 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace; 11 | 12 | /** 13 | * {@link ListCSVMapper} maps a CSV list to a POJO {@link List}. 14 | */ 15 | public class ListCSVMapper extends AbstractListCSVMapper { 16 | 17 | protected final Supplier> listInstantiator; 18 | protected final BiConsumer csvValueConsumer; 19 | protected final Pattern skipPattern; 20 | 21 | /** 22 | * Instantiates a new {@link ListCSVMapper}. 23 | * 24 | * @param listInstantiator a {@link Supplier} to instantiate a new {@link List} 25 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO 26 | * @param csvValueConsumer a {@link BiConsumer} that takes a new POJO instance as the first argument, and the CSV 27 | * list {@link String} value as the second argument. This is so that fields inside the POJO 28 | * can be set according to the passed in CSV list {@link String} value. 29 | * @param skipPattern when a CSV value matches this given {@link Pattern} in {@link #mapToList(String[], int)}, 30 | * then it is skipped (null to not check) 31 | */ 32 | public ListCSVMapper(Supplier> listInstantiator, Supplier pojoInstantiator, 33 | BiConsumer csvValueConsumer, Pattern skipPattern) { 34 | super(pojoInstantiator); 35 | 36 | this.listInstantiator = listInstantiator; 37 | this.csvValueConsumer = csvValueConsumer; 38 | this.skipPattern = skipPattern; 39 | } 40 | 41 | /** 42 | *
43 | * Note: this will map to a list of POJOs with the {@link #csvValueConsumer} applied. 44 | */ 45 | @Override 46 | public List mapToList(String[] csv, int offset) { 47 | List mappedList = listInstantiator.get(); 48 | 49 | for (int csvIndex = offset; csvIndex < csv.length; csvIndex++) { 50 | if (!valueNotWhitespace(csv, csvIndex)) { // Add null for empty CSV values 51 | mappedList.add(null); 52 | continue; 53 | } 54 | 55 | // Skip over matched 'skipPattern's 56 | if (skipPattern != null && skipPattern.matcher(csv[csvIndex]).matches()) { 57 | continue; 58 | } 59 | 60 | T instance = pojoInstantiator.get(); 61 | 62 | // accept() could throw a variety of exceptions 63 | try { 64 | csvValueConsumer.accept(instance, csv[csvIndex]); 65 | mappedList.add(instance); 66 | } catch (Exception exception) { 67 | throw new CSVMappingException(csvIndex - offset, offset, exception); 68 | } 69 | } 70 | 71 | return mappedList; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/list/NestedListCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.list; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping; 4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 5 | 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.function.BiConsumer; 9 | import java.util.function.Function; 10 | import java.util.function.Supplier; 11 | 12 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace; 13 | 14 | /** 15 | * {@link NestedListCSVMapper} mappings are based on a nested CSV lists inside a CSV list (e.g. a group of 3 CSV values 16 | * that repeated in a CSV list). 17 | */ 18 | public class NestedListCSVMapper extends AbstractListCSVMapper { 19 | 20 | protected final Supplier> listInstantiator; 21 | protected final HashMap> csvMappingsOfCSVIndices; 22 | protected final int nestedListLength; 23 | 24 | /** 25 | * Instantiates a new {@link NestedListCSVMapper}. 26 | * 27 | * @param listInstantiator a {@link Supplier} to instantiate a new {@link List} 28 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO 29 | * @param nestedListLength the nested list length 30 | */ 31 | public NestedListCSVMapper(Supplier> listInstantiator, Supplier pojoInstantiator, 32 | int nestedListLength) { 33 | super(pojoInstantiator); 34 | 35 | this.listInstantiator = listInstantiator; 36 | this.nestedListLength = nestedListLength; 37 | 38 | csvMappingsOfCSVIndices = new HashMap<>(); 39 | } 40 | 41 | /** 42 | * Adds a CSV index to POJO field mapping as the CSV index being the largest 43 | * {@link #setMapping(int, BiConsumer, Function)} CSV index + 1. 44 | * 45 | * @param

the type of the POJO field 46 | * @param fieldSetter see {@link CSVMapping} constructor doc 47 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc 48 | */ 49 | public

void addMapping(BiConsumer fieldSetter, Function stringToFieldConverter) { 50 | int nextCSVIndex = csvMappingsOfCSVIndices.keySet().stream().max(Integer::compareTo).orElse(-1) + 1; 51 | setMapping(nextCSVIndex, fieldSetter, stringToFieldConverter); 52 | } 53 | 54 | /** 55 | * Sets a CSV index to POJO field mapping. 56 | * 57 | * @param

the type of the POJO field 58 | * @param csvIndex the CSV index 59 | * @param fieldSetter see {@link CSVMapping} constructor doc 60 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc 61 | */ 62 | public

void setMapping(int csvIndex, BiConsumer fieldSetter, Function stringToFieldConverter) { 63 | csvMappingsOfCSVIndices.put(csvIndex, new CSVMapping<>(fieldSetter, stringToFieldConverter)); 64 | } 65 | 66 | /** 67 | * Removes a CSV mapping, if it exists. 68 | * 69 | * @param csvIndex the CSV index 70 | */ 71 | public void removeMapping(int csvIndex) { 72 | csvMappingsOfCSVIndices.remove(csvIndex); 73 | } 74 | 75 | /** 76 | *
77 | * Note: this will map to a list of POJOs using the mappings added via 78 | * {@link #setMapping(int, BiConsumer, Function)}. This will iterate through the list at {@link #nestedListLength} 79 | * length. 80 | */ 81 | @Override 82 | public List mapToList(String[] csv, int offset) { 83 | List mappedList = listInstantiator.get(); 84 | 85 | for (int csvIndex = offset; csvIndex < csv.length; csvIndex += nestedListLength) { 86 | T instance = pojoInstantiator.get(); 87 | 88 | // Loop through all added 'CSVMapping's and apply them 89 | boolean valueWasMapped = false; 90 | for (int mappedCSVIndex : csvMappingsOfCSVIndices.keySet()) { 91 | if (!valueNotWhitespace(csv, csvIndex + mappedCSVIndex)) { // Don't map empty CSV values 92 | continue; 93 | } 94 | 95 | // apply() could throw a variety of exceptions 96 | try { 97 | csvMappingsOfCSVIndices.get(mappedCSVIndex).apply(instance, csv[csvIndex + mappedCSVIndex]); 98 | valueWasMapped = true; 99 | } catch (Exception exception) { 100 | throw new CSVMappingException( 101 | String.format("Error mapping at index %d with offset %d at mapped CSV index %d", 102 | csvIndex - offset, offset, mappedCSVIndex), exception); 103 | } 104 | } 105 | 106 | if (valueWasMapped) { 107 | mappedList.add(instance); 108 | } 109 | } 110 | 111 | return mappedList; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/map/NamedCSVMapper.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.map; 2 | 3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper; 4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping; 5 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.function.BiConsumer; 10 | import java.util.function.Function; 11 | import java.util.function.Supplier; 12 | 13 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace; 14 | 15 | /** 16 | * {@link NamedCSVMapper} mappings are based off of named CSV indices. 17 | */ 18 | public class NamedCSVMapper extends AbstractCSVMapper { 19 | 20 | private final HashMap> csvMappingsOfCSVIndexNames; 21 | 22 | /** 23 | * Instantiates a new {@link NamedCSVMapper}. 24 | * 25 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO 26 | */ 27 | public NamedCSVMapper(Supplier pojoInstantiator) { 28 | super(pojoInstantiator); 29 | 30 | csvMappingsOfCSVIndexNames = new HashMap<>(); 31 | } 32 | 33 | /** 34 | * Sets a CSV index name to POJO field mapping. 35 | * 36 | * @param

the type of the POJO field 37 | * @param csvIndexName the CSV index name 38 | * @param fieldSetter see {@link CSVMapping} constructor doc 39 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc 40 | */ 41 | public

void setMapping(String csvIndexName, BiConsumer fieldSetter, 42 | Function stringToFieldConverter) { 43 | csvMappingsOfCSVIndexNames.put(csvIndexName, new CSVMapping<>(fieldSetter, stringToFieldConverter)); 44 | } 45 | 46 | /** 47 | * Removes a CSV mapping, if it exists. 48 | * 49 | * @param csvIndexName the csv index name 50 | */ 51 | public void removeMapping(String csvIndexName) { 52 | csvMappingsOfCSVIndexNames.remove(csvIndexName); 53 | } 54 | 55 | /** 56 | * Maps the given CSV to a POJO. 57 | * 58 | * @param csv the CSV 59 | * @param offset offset to add to CSV indices when applying {@link CSVMapping} 60 | * @param csvIndicesOfIndexNames a {@link Map} with they key being the csvIndexNames that were added 61 | * via {@link #setMapping(String, BiConsumer, Function)} and the values being which 62 | * CSV indices they correspond to in the given csv. 63 | * 64 | * @return a new POJO 65 | * 66 | * @throws CSVMappingException thrown for {@link CSVMappingException}s 67 | */ 68 | public T map(String[] csv, int offset, Map csvIndicesOfIndexNames) { 69 | T instance = pojoInstantiator.get(); 70 | 71 | // Loop through all added 'CSVMapping's and apply them with the given 'csvIndicesOfIndexNames' map 72 | for (Map.Entry csvIndexOfIndexName : csvIndicesOfIndexNames.entrySet()) { 73 | CSVMapping csvMapping = csvMappingsOfCSVIndexNames.get(csvIndexOfIndexName.getKey()); 74 | 75 | if (csvMapping == null) { 76 | throw new IllegalArgumentException("The CSV index name " + csvIndexOfIndexName.getKey() + 77 | " does not have CSVMapping! Please report this!"); 78 | } 79 | 80 | int csvNamedIndex = csvIndexOfIndexName.getValue(); 81 | if (!valueNotWhitespace(csv, csvNamedIndex + offset)) { // Don't map empty CSV values 82 | continue; 83 | } 84 | 85 | try { 86 | csvMapping.apply(instance, csv[csvNamedIndex + offset]); 87 | } catch (Exception exception) { 88 | throw new CSVMappingException( 89 | String.format("Error mapping at index %d with offset %d with index name %s", 90 | csvNamedIndex, offset, csvIndexOfIndexName.getKey()), exception); 91 | } 92 | } 93 | 94 | return instance; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/docs/Documentation2JSONSchema.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.docs; 2 | 3 | import org.w3c.dom.Document; 4 | import org.w3c.dom.NodeList; 5 | import org.xml.sax.SAXException; 6 | 7 | import javax.xml.parsers.DocumentBuilder; 8 | import javax.xml.parsers.DocumentBuilderFactory; 9 | import javax.xml.parsers.ParserConfigurationException; 10 | import javax.xml.xpath.XPath; 11 | import javax.xml.xpath.XPathConstants; 12 | import javax.xml.xpath.XPathExpressionException; 13 | import javax.xml.xpath.XPathFactory; 14 | import java.io.ByteArrayInputStream; 15 | import java.io.IOException; 16 | 17 | /** 18 | * {@link Documentation2JSONSchema} is not meant for regular runtime use. It's used to generate jsonschema2POJO schema 19 | * JSON from the HTML in the docs. 20 | */ 21 | public final class Documentation2JSONSchema { 22 | 23 | /** 24 | * This is a standalone helper method that converts properties HTML copied from the IQFeed docs page into a JSON 25 | * schema property list that includes the type and the description. It's kinda hacky, but it helps in the generation 26 | * of JSON POJOs with Javadocs. 27 | * 28 | * @param docHTML the documentation HTML 29 | */ 30 | public static void printIQFeedHTMLWebDocAsJSONSchema(String docHTML) throws Exception { 31 | // For position(): https://stackoverflow.com/questions/2407781/get-nth-child-of-a-node-using-xpath 32 | // It is 1-indexed. 33 | 34 | String nameXPath = "//tbody/tr/td[position()=2]"; 35 | String typeXPath = "//tbody/tr/td[position()=3]"; 36 | String descriptionXPath = "//tbody/tr/td[position()=4]"; 37 | 38 | System.out.println(getSchemaFromDoc(docHTML, nameXPath, typeXPath, descriptionXPath)); 39 | } 40 | 41 | /** 42 | * Converts a generic HTML doc with "name", "type", and "description/note" into schema JSON used for 43 | * jsonSchema2POJO. 44 | * 45 | * @param docHTML the doc html 46 | * @param nameXPath the name Xpath 47 | * @param typeXPath the type Xpath 48 | * @param descriptionXPath the description Xpath 49 | * 50 | * @return the schema from the doc 51 | * 52 | * @throws ParserConfigurationException thrown for {@link ParserConfigurationException}s 53 | * @throws XPathExpressionException thrown for {@link XPathExpressionException}s 54 | * @throws IOException thrown for {@link IOException}s 55 | * @throws SAXException thrown for {@link SAXException}s 56 | */ 57 | private static String getSchemaFromDoc(String docHTML, String nameXPath, String typeXPath, String descriptionXPath) 58 | throws ParserConfigurationException, XPathExpressionException, IOException, SAXException { 59 | StringBuilder schemaJSON = new StringBuilder(); 60 | DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 61 | Document document = builder.parse(new ByteArrayInputStream(docHTML.getBytes())); 62 | XPathFactory xPathFactory = XPathFactory.newInstance(); 63 | XPath xPath = xPathFactory.newXPath(); 64 | 65 | NodeList propertyNameNodes = (NodeList) xPath.compile(nameXPath).evaluate(document, XPathConstants.NODESET); 66 | NodeList propertyTypesNodes = (NodeList) xPath.compile(typeXPath).evaluate(document, XPathConstants.NODESET); 67 | NodeList propertyDescriptionsNodes = (NodeList) xPath.compile(descriptionXPath).evaluate(document, 68 | XPathConstants.NODESET); 69 | 70 | for (int propertyIndex = 0; propertyIndex < propertyNameNodes.getLength(); propertyIndex++) { 71 | String propertyName = propertyNameNodes.item(propertyIndex).getTextContent(); 72 | String propertyType = propertyTypesNodes.item(propertyIndex).getTextContent(); 73 | String propertyDescription = propertyDescriptionsNodes.item(propertyIndex).getTextContent(); 74 | 75 | // Property description may be null 76 | if (propertyDescription == null || propertyDescription.isEmpty()) { 77 | propertyDescription = propertyName; 78 | } 79 | 80 | // Lower Camel Case 81 | propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1); 82 | 83 | schemaJSON.append("\"").append(propertyName).append("\": {\n"); 84 | schemaJSON.append("\"existingJavaType\": \"") 85 | .append(parseDocTypeToSchemaJavaType(propertyType)).append("\",\n"); 86 | schemaJSON.append("\"title\": \"").append(propertyDescription).append("\"\n"); 87 | schemaJSON.append("},\n"); 88 | } 89 | 90 | return schemaJSON.toString(); 91 | } 92 | 93 | /** 94 | * @see jsonSchema2Pojo Reference 95 | */ 96 | private static String parseDocTypeToSchemaJavaType(String docType) { 97 | docType = docType.toLowerCase(); 98 | 99 | if (docType.contains("string") || docType.contains("timestamp")) { 100 | return "java.lang.String"; 101 | } else if (docType.contains("boolean") || docType.contains("bool")) { 102 | return "java.lang.Boolean"; 103 | } else if (docType.contains("int") || docType.contains("integer")) { 104 | return "java.lang.Integer"; 105 | } else if (docType.contains("double") || docType.contains("float") || docType.contains("decimal")) { 106 | return "java.lang.Double"; 107 | } else { 108 | return "UNKNOWN DOC DATA TYPE"; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/map/MapUtil.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.map; 2 | 3 | import java.util.HashSet; 4 | import java.util.Map; 5 | import java.util.Objects; 6 | import java.util.Set; 7 | 8 | /** 9 | * {@link MapUtil} contains utility functions for {@link Map}. 10 | */ 11 | public class MapUtil { 12 | 13 | /** 14 | * Gets keys by the given value for a 'many-to-one' {@link Map} relationship. 15 | * 16 | * @param the key type parameter 17 | * @param the value type parameter 18 | * @param map the {@link Map} 19 | * @param value the value to search for 20 | * 21 | * @return the keys by value {@link Set} or null if none were found 22 | * 23 | * @see Stackoverflow 24 | */ 25 | public static Set getKeysByValue(Map map, E value) { 26 | Set keys = new HashSet<>(); 27 | 28 | for (Map.Entry entry : map.entrySet()) { 29 | if (Objects.equals(value, entry.getValue())) { 30 | keys.add(entry.getKey()); 31 | } 32 | } 33 | 34 | return keys.isEmpty() ? null : keys; 35 | } 36 | 37 | /** 38 | * Gets a key by the given value for a 'one-to-one' {@link Map} relationship. 39 | * 40 | * @param the key type parameter 41 | * @param the value type parameter 42 | * @param map the {@link Map} 43 | * @param value the value to search for 44 | * 45 | * @return the key 46 | * 47 | * @see Stackoverflow 48 | */ 49 | public static T getKeyByValue(Map map, E value) { 50 | for (Map.Entry entry : map.entrySet()) { 51 | if (Objects.equals(value, entry.getValue())) { 52 | return entry.getKey(); 53 | } 54 | } 55 | 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/properties/PropertyUtil.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.properties; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.Properties; 12 | 13 | /** 14 | * {@link PropertyUtil} is a util class for all things {@link Properties}. 15 | */ 16 | public class PropertyUtil { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class); 19 | 20 | public static final Map CACHED_PROPERTIES = Collections.synchronizedMap(new HashMap<>()); 21 | 22 | /** 23 | * Gets a string property from a property file. Will try to IO load the properties in the property file if not 24 | * cached already. 25 | * 26 | * @param propertyFile the property file 27 | * @param key the key 28 | * 29 | * @return the string 30 | */ 31 | public static String getProperty(String propertyFile, String key) { 32 | return getProperty(propertyFile, null, key, null); 33 | } 34 | 35 | /** 36 | * Gets a string property from a property file. Will try to IO load the properties in the property file if not 37 | * cached already. If the desired property is not found in the propertyFile, then the 38 | * defaultPropertyFile is searched. 39 | * 40 | * @param propertyFile the property file 41 | * @param defaultPropertyFile the default property file 42 | * @param key the key 43 | * 44 | * @return the string 45 | */ 46 | public static String getProperty(String propertyFile, String defaultPropertyFile, String key) { 47 | return getProperty(propertyFile, defaultPropertyFile, key, null); 48 | } 49 | 50 | /** 51 | * Gets a string property from a property file. Will try to IO load the properties in the property file if not 52 | * cached already. If the desired property is not found in the propertyFile, then the 53 | * defaultPropertyFile is searched, and if it's not there, then defaultValue is returned. 54 | * 55 | * @param propertyFile the property file 56 | * @param defaultPropertyFile the default property file 57 | * @param key the key 58 | * @param defaultValue the default value (if the desired property wasn't found, then this is returned) 59 | * 60 | * @return the string 61 | */ 62 | public static String getProperty(String propertyFile, String defaultPropertyFile, String key, String defaultValue) { 63 | Properties properties; 64 | 65 | if (!CACHED_PROPERTIES.containsKey(propertyFile)) { 66 | properties = loadPropertyFile(propertyFile, defaultPropertyFile); 67 | CACHED_PROPERTIES.put(propertyFile, properties); 68 | } else { 69 | properties = CACHED_PROPERTIES.get(propertyFile); 70 | } 71 | 72 | return properties == null ? defaultValue : properties.getProperty(key, defaultValue); 73 | } 74 | 75 | /** 76 | * Loads property file {@link Properties}. 77 | * 78 | * @param propertyFile the property file name 79 | * @param defaultPropertyFile the default property file name 80 | * 81 | * @return the properties 82 | */ 83 | public synchronized static Properties loadPropertyFile(String propertyFile, String defaultPropertyFile) { 84 | ClassLoader classLoader = PropertyUtil.class.getClassLoader(); 85 | if (classLoader == null) { 86 | classLoader = ClassLoader.getSystemClassLoader(); 87 | } 88 | 89 | // Load the default property file if exists 90 | Properties defaultProperties = null; 91 | InputStream defaultPropertyStream = classLoader.getResourceAsStream(defaultPropertyFile); 92 | 93 | if (defaultPropertyStream != null) { 94 | defaultProperties = new Properties(); 95 | 96 | // Load the properties 97 | try { 98 | defaultProperties.load(defaultPropertyStream); 99 | LOGGER.debug("Loaded default properties file: {}", defaultPropertyFile); 100 | } catch (IOException exception) { 101 | LOGGER.error("Could not load default property file: {}\n{}", defaultPropertyFile, exception); 102 | } 103 | 104 | // Close the InputStream 105 | try { 106 | defaultPropertyStream.close(); 107 | } catch (IOException exception) { 108 | LOGGER.error("Could not close default property file stream: {}\n{}", defaultPropertyFile, exception); 109 | } 110 | 111 | } else { 112 | LOGGER.warn("No default property file found for: {}", propertyFile); 113 | } 114 | 115 | // Load the property file 116 | Properties properties = null; 117 | InputStream propertyStream = classLoader.getResourceAsStream(propertyFile); 118 | 119 | if (propertyStream != null) { 120 | // Add default properties if they were found 121 | properties = defaultProperties == null ? new Properties() : new Properties(defaultProperties); 122 | 123 | // Load the properties 124 | try { 125 | properties.load(propertyStream); 126 | LOGGER.info("Loaded properties file: {}", propertyFile); 127 | } catch (IOException exception) { 128 | LOGGER.error("Could not load property file: {}\n{}", propertyFile, exception); 129 | } 130 | 131 | // Close the InputStream 132 | try { 133 | propertyStream.close(); 134 | } catch (IOException exception) { 135 | LOGGER.error("Could not close property file stream: {}\n{}", propertyFile, exception); 136 | } 137 | } else { 138 | LOGGER.debug("Could not find property file: {}", propertyFile); 139 | 140 | if (defaultProperties != null) { 141 | LOGGER.info("Using default properties for: {}", propertyFile); 142 | properties = defaultProperties; 143 | } 144 | } 145 | 146 | return properties; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/split/SplitUtil.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.split; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | /** 9 | * {@link SplitUtil} contains utility functions for splitting {@link String}s. 10 | */ 11 | public class SplitUtil { 12 | 13 | private static final Pattern QUOTE_ESCAPED_SPACE_PATTERN = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); 14 | 15 | /** 16 | * Splits a {@link String} using the space key as a delimiter, but ignores spaces surrounded in quotes. 17 | * 18 | * @param toSplit the {@link String} to split 19 | * 20 | * @return a {@link List} of {@link String}s 21 | * 22 | * @see "Regex for splitting a string using space" reference 23 | */ 24 | public static List splitQuoteEscapedSpaces(String toSplit) { 25 | List splitList = new ArrayList<>(); 26 | Matcher regexMatcher = QUOTE_ESCAPED_SPACE_PATTERN.matcher(toSplit); 27 | 28 | while (regexMatcher.find()) { 29 | if (regexMatcher.group(1) != null) { 30 | // Add double-quoted string without the quotes 31 | splitList.add(regexMatcher.group(1)); 32 | } else if (regexMatcher.group(2) != null) { 33 | // Add single-quoted string without the quotes 34 | splitList.add(regexMatcher.group(2)); 35 | } else { 36 | // Add unquoted word 37 | splitList.add(regexMatcher.group()); 38 | } 39 | } 40 | 41 | return splitList; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/string/LineEnding.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.string; 2 | 3 | /** 4 | * {@link LineEnding} defines enums for the various types of ASCII line endings. 5 | */ 6 | public enum LineEnding { 7 | 8 | /** 9 | * <CR><LF> line ending. Note that there is no ASCII char for this, only an ASCII String. 10 | */ 11 | CR_LF((char) 0, "\r\n"), 12 | 13 | /** 14 | * <CR> line ending. 15 | */ 16 | CR('\r', "\r"), 17 | 18 | /** 19 | * <LF> line ending. 20 | */ 21 | LF('\n', "\n"); 22 | 23 | private final String asciiString; 24 | private final char asciiChar; 25 | 26 | /** 27 | * Instantiates a new {@link LineEnding}. 28 | * 29 | * @param asciiChar the ASCII char 30 | * @param asciiString the ASCII string 31 | */ 32 | LineEnding(char asciiChar, String asciiString) { 33 | this.asciiChar = asciiChar; 34 | this.asciiString = asciiString; 35 | } 36 | 37 | /** 38 | * Gets {@link #asciiString}. 39 | * 40 | * @return a {@link String} 41 | */ 42 | public String getASCIIString() { 43 | return asciiString; 44 | } 45 | 46 | /** 47 | * Gets {@link #asciiChar}. 48 | * 49 | * @return a char 50 | */ 51 | public char getASCIIChar() { 52 | return asciiChar; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/tradecondition/TradeConditionUtil.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.tradecondition; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * {@link TradeConditionUtil} contains utility methods for trade conditions. 8 | */ 9 | public class TradeConditionUtil { 10 | 11 | /** 12 | * Gets a list of base-10 integers from a non-delimited list of 2-digit hexadecimal numbers (with no '0x' prefix) 13 | * which represent various trade conditions. 14 | * 15 | * @param tradeConditionString the non-delimited list of hexadecimal trade conditions 16 | * 17 | * @return a new {@link List} or null if no digits were converted 18 | */ 19 | public static List listFromTradeConditionString(String tradeConditionString) { 20 | if (tradeConditionString == null || tradeConditionString.isEmpty()) { 21 | return null; 22 | } 23 | 24 | ArrayList tradeConditions = new ArrayList<>(4); // Max of 4 trade conditions usually 25 | 26 | for (int index = 0; index < tradeConditionString.length(); index++) { 27 | if (index + 1 < tradeConditionString.length()) { // Check if there is another hex digit after current 28 | tradeConditions.add(Integer.parseInt(tradeConditionString.substring(index, index + 2), 16)); 29 | index++; 30 | } else { 31 | throw new IllegalArgumentException("Only found one hex digit for trade condition at index " + index); 32 | } 33 | } 34 | 35 | return tradeConditions; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/jacobpeterson/iqfeed4j/util/xml/XMLUtil.java: -------------------------------------------------------------------------------- 1 | package net.jacobpeterson.iqfeed4j.util.xml; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.XmlMapper; 4 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 5 | 6 | /** 7 | * {@link XMLUtil} contains utility methods for XML. 8 | */ 9 | public class XMLUtil { 10 | 11 | /** 12 | * This is a standard {@link XmlMapper}. 13 | */ 14 | public static final XmlMapper STANDARD_XML_MAPPER = new XmlMapper(); 15 | 16 | static { 17 | STANDARD_XML_MAPPER.registerModule(new JavaTimeModule()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/iqfeed4j.default.properties: -------------------------------------------------------------------------------- 1 | # 2 | # START Example 3 | # 4 | 5 | # For IQConnectExecutable 6 | #iqconnect_command= 7 | #product_id= 8 | #application_version= 9 | #login= 10 | #password= 11 | #autoconnect= 12 | #save_login_info= 13 | 14 | # For Feeds 15 | #feed_name= 16 | #feed_hostname= 17 | #level_1_feed_port= 18 | #market_depth_feed_port= 19 | #derivative_feed_port= 20 | #admin_feed_port= 21 | #lookup_feed_port= 22 | 23 | # 24 | # END Example 25 | # 26 | 27 | # 28 | # START Defaults 29 | # 30 | 31 | application_version=1.0 32 | autoconnect=true 33 | save_login_info=true 34 | 35 | feed_name=IQFeed4j 36 | feed_hostname=localhost 37 | level_1_feed_port=5009 38 | market_depth_feed_port=9200 39 | derivative_feed_port=9400 40 | admin_feed_port=9300 41 | lookup_feed_port=9100 42 | 43 | # 44 | # END Defaults 45 | # 46 | --------------------------------------------------------------------------------