├── .gitignore ├── README.md ├── algo_setting.json ├── data ├── boston-robberies.csv ├── bush.csv ├── dateTimeTestFile.csv ├── ohlcdata.csv ├── test_wines.csv └── tornadoes_1950-2014.csv ├── gateway_setting.json ├── pom.xml ├── tquant-algorithm ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── tquant │ │ │ └── algorithm │ │ │ └── algos │ │ │ ├── BestLimitAlgo.java │ │ │ ├── DmaAlgo.java │ │ │ ├── OptionCoveredCallAlgo.java │ │ │ ├── SmaAlgo.java │ │ │ ├── ThetaGangAlgo.java │ │ │ ├── thetagang │ │ │ ├── PortfolioManager.java │ │ │ └── RollingSellPutConfig.java │ │ │ └── utils │ │ │ ├── JacksonUtils.java │ │ │ └── Utils.java │ └── resources │ │ └── sell_put_config.toml │ └── test │ └── java │ └── com │ └── tquant │ └── algorithm │ └── algo │ └── SmaIndicatorTest.java ├── tquant-asset └── pom.xml ├── tquant-backtester ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── tquant │ │ └── backtester │ │ ├── Backtesting.java │ │ └── BacktestingEngine.java │ └── test │ └── java │ └── com │ └── tquant │ └── backtester │ ├── TablesawBarTest.java │ ├── TablesawBubbleTest.java │ ├── TablesawTest.java │ └── TimeSeriesVisualizations.java ├── tquant-bootstrap ├── pom.xml └── src │ └── main │ ├── assembly │ └── assembly.xml │ ├── java │ └── com │ │ └── tquant │ │ └── bootstrap │ │ ├── TigerQuantBootstrap.java │ │ └── command │ │ ├── CliRunner.java │ │ └── CommandExecuteTemplate.java │ └── resources │ ├── global_setting.json │ └── jdbc.properties ├── tquant-core ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── tquant │ └── core │ ├── TigerQuantException.java │ ├── config │ └── ConfigLoader.java │ ├── core │ ├── AlgoEngine.java │ ├── AlgoTemplate.java │ ├── Engine.java │ ├── Gateway.java │ ├── LogEngine.java │ ├── MainEngine.java │ └── OrderEngine.java │ ├── event │ ├── Event.java │ ├── EventEngine.java │ ├── EventHandler.java │ └── EventType.java │ ├── indicators │ └── Indicators.java │ ├── model │ ├── Config.java │ ├── StockConstants.java │ ├── data │ │ ├── Account.java │ │ ├── Asset.java │ │ ├── Bar.java │ │ ├── BaseData.java │ │ ├── Contract.java │ │ ├── HourTrading.java │ │ ├── Log.java │ │ ├── MarketStatus.java │ │ ├── Order.java │ │ ├── Position.java │ │ ├── RealtimeQuote.java │ │ ├── StockData.java │ │ ├── SymbolName.java │ │ ├── Tick.java │ │ ├── TimelinePoint.java │ │ ├── TimelineQuote.java │ │ ├── TimelineRange.java │ │ ├── Trade.java │ │ └── TradeCalendar.java │ ├── enums │ │ ├── BacktestingMode.java │ │ ├── BarPeriod.java │ │ ├── BarType.java │ │ ├── Direction.java │ │ ├── OrderStatus.java │ │ ├── OrderType.java │ │ ├── SecType.java │ │ └── StockStatus.java │ └── request │ │ ├── ModifyRequest.java │ │ ├── OptionChainFilter.java │ │ ├── OrderRequest.java │ │ └── SubscribeRequest.java │ └── util │ ├── BarGenerator.java │ ├── BarHandler.java │ ├── QuantConstants.java │ └── QuantUtils.java ├── tquant-gateway ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── tquant │ └── gateway │ ├── api │ ├── OptionApi.java │ ├── QuoteApi.java │ └── TradeApi.java │ ├── converter │ └── Converters.java │ └── tiger │ ├── TigerClient.java │ ├── TigerConfig.java │ ├── TigerConfigLoader.java │ ├── TigerGateway.java │ ├── TigerOptionApi.java │ ├── TigerQuoteApi.java │ ├── TigerSubscribeApi.java │ └── TigerTradeApi.java ├── tquant-loader ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── tquant │ └── loader │ ├── BarLoader.java │ ├── ContractLoader.java │ ├── TickLoader.java │ └── command │ ├── CliRunner.java │ └── CommandExecuteTemplate.java └── tquant-storage ├── pom.xml └── src └── main ├── java └── com │ └── tquant │ └── storage │ ├── dao │ ├── BarDAO.java │ ├── BaseDAO.java │ ├── ContractDAO.java │ └── TickDAO.java │ ├── mapper │ ├── BarMapper.java │ ├── ContractMapper.java │ └── TickMapper.java │ └── typehandler │ └── DurationTypeHandler.java └── resources └── datasource ├── mybatis-config.xml └── mysql_table_create.sql /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | *.txt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | .idea/ 26 | target/ 27 | logs/ 28 | tiger-quant.iml 29 | 30 | *.iml 31 | 32 | tiger_gateway_setting.json 33 | 34 | testoutput -------------------------------------------------------------------------------- /algo_setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "BestLimitAlgo": { 3 | "enable": false, 4 | "class":"com.tquant.algorithm.algos.BestLimitAlgo", 5 | "direction": "BUY", 6 | "volume": 100, 7 | "symbol": "00700" 8 | }, 9 | "DmaAlgo": { 10 | "enable": false, 11 | "class":"com.tquant.algorithm.algos.DmaAlgo", 12 | "direction": "BUY", 13 | "price": 13.2, 14 | "volume": 100 15 | }, 16 | "SmaAlgo": { 17 | "enable": false, 18 | "class":"com.tquant.algorithm.algos.SmaAlgo", 19 | "symbol": "AAPL", 20 | "bars": 300 21 | }, 22 | "OptionCoveredCallAlgo": { 23 | "enable": false, 24 | "class": "com.tquant.algorithm.algos.OptionCoveredCallAlgo", 25 | "symbol": "AAPL", 26 | "expiryDate": "2022-09-02" 27 | }, 28 | "ThetaGangAlgo": { 29 | "enable": true, 30 | "class":"com.tquant.algorithm.algos.ThetaGangAlgo", 31 | "account": { 32 | "account_id": "643795", 33 | "cancel_orders": true, 34 | "margin_usage": 0.5, 35 | "market_data_type": 1 36 | }, 37 | "orders": { 38 | "exchange": "SMART", 39 | "algo": { 40 | "strategy": "Adaptive", 41 | "params": [ 42 | [ 43 | "adaptivePriority", 44 | "Patient" 45 | ] 46 | ] 47 | } 48 | }, 49 | "option_chains": { 50 | "expirations": 4, 51 | "strikes": 10 52 | }, 53 | "roll_when": { 54 | "pnl": 0.9, 55 | "dte": 15, 56 | "min_pnl": 0.2, 57 | "close_at_pnl": 0.1, 58 | "calls": { 59 | "itm": true, 60 | "credit_only": false 61 | }, 62 | "puts": { 63 | "itm": false, 64 | "credit_only": false 65 | } 66 | }, 67 | "write_when": { 68 | "calls": { 69 | "green": true, 70 | "cap_factor": 1 71 | }, 72 | "puts": { 73 | "red": true 74 | } 75 | }, 76 | "target": { 77 | "dte": 45, 78 | "delta": 0.3, 79 | "maximum_new_contracts_percent": 0.05, 80 | "minimum_open_interest": 10 81 | }, 82 | "symbols": { 83 | "SPY": { 84 | "weight": 0.4 85 | }, 86 | "QQQ": { 87 | "weight": 0.3, 88 | "puts": { 89 | "delta": 0.5, 90 | "strike_limit": 1000 91 | }, 92 | "calls": { 93 | "strike_limit": 100 94 | } 95 | }, 96 | "FUTU": { 97 | "weight": 0.2, 98 | "delta": 0.4 99 | }, 100 | "BABA": { 101 | "primary_exchange": "NASDAQ", 102 | "weight": 0.05 103 | }, 104 | "JD": { 105 | "primary_exchange": "NYSE", 106 | "weight": 0.05 107 | } 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /data/boston-robberies.csv: -------------------------------------------------------------------------------- 1 | Month,Record,Robberies 2 | 1966-01,1,41 3 | 1966-02,2,39 4 | 1966-03,3,50 5 | 1966-04,4,40 6 | 1966-05,5,43 7 | 1966-06,6,38 8 | 1966-07,7,44 9 | 1966-08,8,35 10 | 1966-09,9,39 11 | 1966-10,10,35 12 | 1966-11,11,29 13 | 1966-12,12,49 14 | 1967-01,13,50 15 | 1967-02,14,59 16 | 1967-03,15,63 17 | 1967-04,16,32 18 | 1967-05,17,39 19 | 1967-06,18,47 20 | 1967-07,19,53 21 | 1967-08,20,60 22 | 1967-09,21,57 23 | 1967-10,22,52 24 | 1967-11,23,70 25 | 1967-12,24,90 26 | 1968-01,25,74 27 | 1968-02,26,62 28 | 1968-03,27,55 29 | 1968-04,28,84 30 | 1968-05,29,94 31 | 1968-06,30,70 32 | 1968-07,31,108 33 | 1968-08,32,139 34 | 1968-09,33,120 35 | 1968-10,34,97 36 | 1968-11,35,126 37 | 1968-12,36,149 38 | 1969-01,37,158 39 | 1969-02,38,124 40 | 1969-03,39,140 41 | 1969-04,40,109 42 | 1969-05,41,114 43 | 1969-06,42,77 44 | 1969-07,43,120 45 | 1969-08,44,133 46 | 1969-09,45,110 47 | 1969-10,46,92 48 | 1969-11,47,97 49 | 1969-12,48,78 50 | 1970-01,49,99 51 | 1970-02,50,107 52 | 1970-03,51,112 53 | 1970-04,52,90 54 | 1970-05,53,98 55 | 1970-06,54,125 56 | 1970-07,55,155 57 | 1970-08,56,190 58 | 1970-09,57,236 59 | 1970-10,58,189 60 | 1970-11,59,174 61 | 1970-12,60,178 62 | 1971-01,61,136 63 | 1971-02,62,161 64 | 1971-03,63,171 65 | 1971-04,64,149 66 | 1971-05,65,184 67 | 1971-06,66,155 68 | 1971-07,67,276 69 | 1971-08,68,224 70 | 1971-09,69,213 71 | 1971-10,70,279 72 | 1971-11,71,268 73 | 1971-12,72,287 74 | 1972-01,73,238 75 | 1972-02,74,213 76 | 1972-03,75,257 77 | 1972-04,76,293 78 | 1972-05,77,212 79 | 1972-06,78,246 80 | 1972-07,79,353 81 | 1972-08,80,339 82 | 1972-09,81,308 83 | 1972-10,82,247 84 | 1972-11,83,257 85 | 1972-12,84,322 86 | 1973-01,85,298 87 | 1973-02,86,273 88 | 1973-03,87,312 89 | 1973-04,88,249 90 | 1973-05,89,286 91 | 1973-06,90,279 92 | 1973-07,91,309 93 | 1973-08,92,401 94 | 1973-09,93,309 95 | 1973-10,94,328 96 | 1973-11,95,353 97 | 1973-12,96,354 98 | 1974-01,97,327 99 | 1974-02,98,324 100 | 1974-03,99,285 101 | 1974-04,100,243 102 | 1974-05,101,241 103 | 1974-06,102,287 104 | 1974-07,103,355 105 | 1974-08,104,460 106 | 1974-09,105,364 107 | 1974-10,106,487 108 | 1974-11,107,452 109 | 1974-12,108,391 110 | 1975-01,109,500 111 | 1975-02,110,451 112 | 1975-03,111,375 113 | 1975-04,112,372 114 | 1975-05,113,302 115 | 1975-06,114,316 116 | 1975-07,115,398 117 | 1975-08,116,394 118 | 1975-09,117,431 119 | 1975-10,118,431 -------------------------------------------------------------------------------- /data/dateTimeTestFile.csv: -------------------------------------------------------------------------------- 1 | Time,Value 2 | 2018-04-02T09:26:21.000,24336 3 | 2018-04-02T09:29:21.000,24342 4 | 2018-04-02T09:31:21.000,24355 5 | 2018-04-02T09:41:21.000,24381 6 | 2018-04-02T09:50:21.000,24391 7 | 2018-04-02T09:55:21.000,24402 8 | 2018-04-02T09:57:21.000,24412 9 | 2018-04-02T10:01:21.000,24407 10 | 2018-04-02T10:04:21.000,24401 11 | 2018-04-02T10:05:21.000,24351 -------------------------------------------------------------------------------- /data/ohlcdata.csv: -------------------------------------------------------------------------------- 1 | Date,Open,High,Low,Close,Volume,Adj Close 2 | 2009-03-31,17.83,18.79,17.78,18.37,92095500,17.81 3 | 2009-03-30,17.74,17.76,17.27,17.48,49633000,16.95 4 | 2009-03-27,18.54,18.62,18.05,18.13,47670400,17.58 5 | 2009-03-26,18.17,18.88,18.12,18.83,63775100,18.26 6 | 2009-03-25,17.98,18.31,17.52,17.88,73927100,17.34 7 | 2009-03-24,18.04,18.21,17.84,17.93,50044100,17.39 8 | 2009-03-23,17.37,18.59,17.31,18.33,71600000,17.77 9 | 2009-03-20,17.32,17.65,16.88,17.06,81725500,16.54 10 | 2009-03-19,17.37,17.45,16.92,17.14,58994600,16.62 11 | 2009-03-18,17.03,17.22,16.6,16.96,70710700,16.45 12 | 2009-03-17,16.32,16.9,16.26,16.9,62481000,16.39 13 | 2009-03-16,16.82,16.96,16.24,16.25,67028900,15.76 14 | 2009-03-13,16.98,17.05,16.18,16.65,82965800,16.14 15 | 2009-03-12,17.01,17.04,16.48,17.01,93694100,16.49 16 | 2009-03-11,16.63,17.2,16.43,17.11,84522200,16.59 17 | 2009-03-10,15.37,16.62,15.25,16.48,95184200,15.98 18 | 2009-03-09,15.2,15.74,15.1,15.15,66479100,14.69 19 | 2009-03-06,15.35,15.62,14.87,15.28,92821400,14.82 20 | 2009-03-05,15.86,15.88,15.27,15.27,89708500,14.81 21 | 2009-03-04,16.12,16.4,15.89,16.12,69285100,15.63 22 | 2009-03-03,16.03,16.24,15.64,15.88,80476600,15.4 23 | 2009-03-02,15.96,16.25,15.72,15.79,80602100,15.31 24 | 2009-02-27,16.29,16.52,16.1,16.15,93428000,15.66 25 | 2009-02-26,17.05,17.08,16.42,16.42,83219500,15.92 26 | 2009-02-25,17.01,17.24,16.46,16.96,105894600,16.45 27 | 2009-02-24,17.03,17.35,16.36,17.17,122674500,16.65 28 | 2009-02-23,18.02,18.15,17.16,17.21,70803400,16.69 29 | 2009-02-20,17.77,18.19,17.66,18,69413800,17.45 30 | 2009-02-19,18.3,18.38,17.81,17.91,49195600,17.37 31 | 2009-02-18,18.22,18.45,18,18.12,54946900,17.57 32 | 2009-02-17,18.49,18.5,17.89,18.09,75853300,17.54 33 | 2009-02-13,19.27,19.47,19.04,19.09,47416000,18.38 34 | 2009-02-12,18.97,19.32,18.54,19.26,75323200,18.55 35 | 2009-02-11,18.94,19.49,18.92,19.21,58599000,18.5 36 | 2009-02-10,19.25,19.8,18.7,18.8,83953200,18.1 37 | 2009-02-09,19.64,19.77,19.26,19.44,52196400,18.72 38 | 2009-02-06,19.16,19.93,19.06,19.66,86746000,18.93 39 | 2009-02-05,18.51,19.14,18.25,19.04,75195200,18.34 40 | 2009-02-04,18.54,19,18.5,18.63,75618000,17.94 41 | 2009-02-03,17.85,18.61,17.6,18.5,86865100,17.82 42 | 2009-02-02,17.03,18.13,17,17.83,88871700,17.17 43 | 2009-01-30,17.74,17.79,17.1,17.1,62370900,16.47 44 | 2009-01-29,17.78,17.96,17.56,17.59,49192800,16.94 45 | 2009-01-28,17.8,18.31,17.76,18.04,64145500,17.37 46 | 2009-01-27,17.78,17.97,17.43,17.66,61695000,17.01 47 | 2009-01-26,17.29,17.81,17.23,17.63,92476500,16.98 48 | 2009-01-23,16.97,17.49,16.75,17.2,117020600,16.56 49 | 2009-01-22,18.05,18.18,17.07,17.11,222436600,16.48 50 | 2009-01-21,18.87,19.45,18.46,19.38,68340900,18.66 51 | 2009-01-20,19.46,19.62,18.37,18.48,89873000,17.8 52 | 2009-01-16,19.63,19.91,19.15,19.71,79634100,18.98 53 | 2009-01-15,19.07,19.3,18.52,19.24,96169800,18.53 54 | 2009-01-14,19.53,19.68,19.01,19.09,80257500,18.38 55 | 2009-01-13,19.52,19.99,19.52,19.82,65843500,19.09 56 | 2009-01-12,19.71,19.79,19.3,19.47,52163500,18.75 57 | 2009-01-09,20.17,20.3,19.41,19.52,49815300,18.8 58 | 2009-01-08,19.63,20.19,19.55,20.12,70229900,19.38 59 | 2009-01-07,20.19,20.29,19.48,19.51,72709900,18.79 60 | 2009-01-06,20.75,21,20.61,20.76,58083400,19.99 61 | 2009-01-05,20.2,20.67,20.06,20.52,61475200,19.76 62 | 2009-01-02,19.53,20.4,19.37,20.33,50084000,19.58 -------------------------------------------------------------------------------- /gateway_setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "gateway": "TigerGateway", 3 | "apiLogEnable": true, 4 | "apiLogPath": "log/", 5 | "tigerId": "", 6 | "account": "", 7 | "privateKey": "" 8 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.tigerbrokers 8 | tiger-quant 9 | pom 10 | 1.0.0 11 | 12 | tquant-core 13 | tquant-backtester 14 | tquant-storage 15 | tquant-loader 16 | tquant-gateway 17 | tquant-asset 18 | tquant-algorithm 19 | tquant-bootstrap 20 | 21 | 22 | 23 | 3.1.0 24 | 2.0.0 25 | 4.13.1 26 | 1.7.28 27 | 0.14 28 | 8.0.28 29 | 3.5.6 30 | 1.18.4 31 | 1.2.83 32 | 1.5.0 33 | 1.2.9 34 | 0.43.1 35 | 21.0 36 | 0.4 37 | 38 | 39 | 40 | 41 | 42 | io.github.tigerbrokers 43 | openapi-java-sdk 44 | ${tigerbrokers.openapi-java-sdk.version} 45 | 46 | 47 | org.slf4j 48 | slf4j-api 49 | ${org.slf4j.slf4j-api.version} 50 | 51 | 52 | org.ta4j 53 | ta4j-core 54 | ${org.ta4j.ta4j-core.version} 55 | 56 | 57 | com.tictactec 58 | ta-lib 59 | 0.4.0 60 | 61 | 62 | junit 63 | junit 64 | ${junit.junit.version} 65 | 66 | 67 | mysql 68 | mysql-connector-java 69 | ${mysql.mysql-connector-java.version} 70 | 71 | 72 | org.mybatis 73 | mybatis 74 | ${org.mybatis.mybatis.version} 75 | 76 | 77 | org.projectlombok 78 | lombok 79 | ${org.projectlombok.lombok.version} 80 | 81 | 82 | com.alibaba 83 | fastjson 84 | ${fastjson.version} 85 | 86 | 87 | commons-cli 88 | commons-cli 89 | ${commons-cli.version} 90 | 91 | 92 | ch.qos.logback 93 | logback-classic 94 | ${logback.version} 95 | 96 | 97 | ch.qos.logback 98 | logback-core 99 | ${logback.version} 100 | 101 | 102 | tech.tablesaw 103 | tablesaw-core 104 | ${tablesaw.version} 105 | 106 | 107 | tech.tablesaw 108 | tablesaw-jsplot 109 | ${tablesaw.version} 110 | 111 | 112 | tech.tablesaw 113 | tablesaw-json 114 | ${tablesaw.version} 115 | 116 | 117 | com.google.guava 118 | guava 119 | ${guava.version} 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-compiler-plugin 129 | 130 | 1.8 131 | 1.8 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /tquant-algorithm/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | tiger-quant 7 | com.tigerbrokers 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | tquant-algorithm 13 | 14 | 15 | 16 | com.tigerbrokers 17 | tquant-core 18 | 1.0.0 19 | 20 | 21 | com.tigerbrokers 22 | tquant-storage 23 | 1.0.0 24 | 25 | 26 | com.tigerbrokers 27 | tquant-gateway 28 | 1.0.0 29 | 30 | 31 | tech.tablesaw 32 | tablesaw-core 33 | 34 | 35 | tech.tablesaw 36 | tablesaw-json 37 | 38 | 39 | -------------------------------------------------------------------------------- /tquant-algorithm/src/main/java/com/tquant/algorithm/algos/BestLimitAlgo.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algos; 2 | 3 | import com.tquant.core.core.AlgoTemplate; 4 | import com.tquant.core.model.data.Bar; 5 | import com.tquant.core.model.data.Order; 6 | import com.tquant.core.model.data.Tick; 7 | import com.tquant.core.model.data.Trade; 8 | import com.tquant.core.model.enums.Direction; 9 | import com.tquant.core.model.enums.OrderType; 10 | import com.tquant.core.util.BarGenerator; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * Description: This strategy works if you subscribe to only one stock 17 | * 18 | * @author kevin 19 | * @date 2019/08/20 20 | */ 21 | public class BestLimitAlgo extends AlgoTemplate { 22 | 23 | private Tick lastTick; 24 | private String direction; 25 | private int volume; 26 | private String orderId; 27 | private double orderPrice; 28 | private int traded = 0; 29 | private String symbol; 30 | private BarGenerator barGenerator; 31 | private BarGenerator min2BarGenerator; 32 | 33 | public BestLimitAlgo() { 34 | } 35 | 36 | public BestLimitAlgo(Map settings) { 37 | super(settings); 38 | } 39 | 40 | @Override 41 | public void init() { 42 | this.direction = (String) settings.get("direction"); 43 | this.volume = (Integer) settings.get("volume"); 44 | this.symbol = (String) settings.get("symbol"); 45 | } 46 | 47 | @Override 48 | public void onStart() { 49 | barGenerator = new BarGenerator(bar -> onBar(bar)); 50 | List symbols = new ArrayList<>(); 51 | symbols.add("AAPL"); 52 | min2BarGenerator = new BarGenerator(symbols, 2, bar -> on2minBar(bar)); 53 | subscribe(symbol); 54 | } 55 | 56 | @Override 57 | public void onTick(Tick tick) { 58 | this.lastTick = tick; 59 | if (this.direction.equalsIgnoreCase(Direction.BUY.name())) { 60 | if (orderId == null) { 61 | buyBestLimit(); 62 | } else if (this.orderPrice != lastTick.getBidPrice()) { 63 | cancelAll(); 64 | } 65 | } else { 66 | if (orderId == null) { 67 | sellBestLimit(); 68 | } else if (this.orderPrice != lastTick.getAskPrice()) { 69 | cancelAll(); 70 | } 71 | } 72 | barGenerator.updateTick(tick); 73 | } 74 | 75 | private void buyBestLimit() { 76 | int orderVolume = volume - traded; 77 | orderPrice = lastTick.getBidPrice(); 78 | if (orderPrice > 0) { 79 | buy(symbol, orderPrice, orderVolume, OrderType.LMT); 80 | } 81 | } 82 | 83 | private void sellBestLimit() { 84 | int orderVolume = volume - traded; 85 | orderPrice = lastTick.getAskPrice(); 86 | if (orderPrice > 0) { 87 | sell(symbol, orderPrice, orderVolume, OrderType.LMT); 88 | } 89 | } 90 | 91 | @Override 92 | public void onOrder(Order order) { 93 | if (!order.isActive()) { 94 | orderId = ""; 95 | orderPrice = 0; 96 | } 97 | } 98 | 99 | @Override 100 | public void onTrade(Trade trade) { 101 | this.traded += trade.getVolume(); 102 | if (traded >= trade.getVolume()) { 103 | log("{} onTrade traded:{},volume:{}", getAlgoName(), traded, trade.getVolume()); 104 | stop(); 105 | } 106 | } 107 | 108 | @Override 109 | public void onBar(Bar bar) { 110 | log("{} onBar {}", getAlgoName(), bar); 111 | min2BarGenerator.updateBar(bar); 112 | } 113 | 114 | public void on2minBar(Bar bar) { 115 | log("{} on2minBar {}", bar); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /tquant-algorithm/src/main/java/com/tquant/algorithm/algos/DmaAlgo.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algos; 2 | 3 | import com.tquant.core.core.AlgoTemplate; 4 | import com.tquant.core.model.data.Order; 5 | import com.tquant.core.model.data.Tick; 6 | import com.tquant.core.model.data.Trade; 7 | import com.tquant.core.model.enums.Direction; 8 | import com.tquant.core.model.enums.OrderType; 9 | 10 | /** 11 | * Description: 12 | * 13 | * @author kevin 14 | * @date 2019/08/26 15 | */ 16 | public class DmaAlgo extends AlgoTemplate { 17 | 18 | private Long orderId; 19 | private String direction; 20 | private double price; 21 | private int volume; 22 | 23 | public DmaAlgo() { 24 | } 25 | 26 | @Override 27 | public void onStart() { 28 | direction = (String) settings.get("direction"); 29 | price = (Double) settings.get("price"); 30 | volume = (Integer) settings.get("volume"); 31 | } 32 | 33 | @Override 34 | public void onTick(Tick tick) { 35 | if (orderId == null) { 36 | if (Direction.BUY.name().equals(direction)) { 37 | orderId = buy(tick.getSymbol(), price, volume, OrderType.LMT); 38 | } else { 39 | orderId = sell(tick.getSymbol(), price, volume, OrderType.LMT); 40 | } 41 | } 42 | } 43 | 44 | @Override 45 | public void onOrder(Order order) { 46 | if (!order.isActive()) { 47 | stop(); 48 | } 49 | } 50 | 51 | @Override 52 | public void onTrade(Trade trade) { 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tquant-algorithm/src/main/java/com/tquant/algorithm/algos/OptionCoveredCallAlgo.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algos; 2 | 3 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionRealTimeQuote; 4 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionRealTimeQuoteGroup; 5 | import com.tquant.core.core.AlgoTemplate; 6 | import com.tquant.core.model.data.Order; 7 | import com.tquant.core.model.data.Tick; 8 | import com.tquant.core.model.data.Trade; 9 | import com.tquant.core.model.enums.OrderType; 10 | import com.tquant.gateway.api.OptionApi; 11 | import com.tquant.gateway.tiger.TigerClient; 12 | import com.tquant.gateway.tiger.TigerOptionApi; 13 | import java.time.DayOfWeek; 14 | import java.time.LocalDate; 15 | import java.time.format.DateTimeFormatter; 16 | import java.time.temporal.TemporalAdjusters; 17 | import java.util.ArrayList; 18 | import java.util.Comparator; 19 | import java.util.List; 20 | 21 | /** 22 | * Description: 23 | * 24 | * @author kevin 25 | * @date 2022/08/30 26 | */ 27 | public class OptionCoveredCallAlgo extends AlgoTemplate { 28 | 29 | private String symbol; 30 | private String expiryDateStr; 31 | private LocalDate expiryDate; 32 | private String optionSymbol; 33 | private Double maxPrice = 180.0; 34 | private volatile boolean isOpen = false; 35 | private volatile boolean isClose = false; 36 | private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 37 | 38 | private OptionApi optionApi = new TigerOptionApi(TigerClient.getInstance()); 39 | 40 | @Override 41 | public void onStart() { 42 | symbol = (String) settings.get("symbol"); 43 | //以下周五的期权为到期日 44 | expiryDate = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY)); 45 | expiryDateStr = expiryDate.format(formatter); 46 | 47 | //根据配置的 symbol 和 expiry 获取期权链 48 | List optionChain = optionApi.getOptionChain(symbol, expiryDateStr, null); 49 | 50 | //只过滤call 期权 51 | List calls = new ArrayList<>(); 52 | optionChain.stream().forEach(chain -> calls.add(chain.getCall())); 53 | 54 | //获取成交量最大的期权行情 55 | OptionRealTimeQuote maxVolumeCallQuote = 56 | calls.stream().max(Comparator.comparingInt(OptionRealTimeQuote::getVolume)).get(); 57 | 58 | optionSymbol = maxVolumeCallQuote.getIdentifier(); 59 | //订阅正股行情,订阅成交量最大的期权行情 60 | subscribe(symbol); 61 | subscribe(optionSymbol); 62 | } 63 | 64 | @Override 65 | public void onTick(Tick tick) { 66 | //价格达到最大限制时,买入正股,并卖出call,只进行一次买入操作 67 | if (!isOpen && tick.getSymbol().equals(symbol) && tick.getLatestPrice() >= maxPrice) { 68 | isOpen = true; 69 | log("match option covered call open position tick: {}", tick); 70 | buy(symbol, null, 100, OrderType.MKT); 71 | sell(optionSymbol, null, 1, OrderType.MKT); 72 | } 73 | 74 | //到达到期日时,平仓 75 | if (!isClose && expiryDate.equals(LocalDate.now())) { 76 | isClose = true; 77 | log("match option covered call close position tick: {}", tick); 78 | sell(symbol, null, 100, OrderType.MKT); 79 | buy(optionSymbol, null, 1, OrderType.MKT); 80 | } 81 | } 82 | 83 | @Override 84 | public void onOrder(Order order) { 85 | 86 | } 87 | 88 | @Override 89 | public void onTrade(Trade trade) { 90 | 91 | } 92 | 93 | @Override 94 | public void onStop() { 95 | //取消行情订阅 96 | cancelSubscribe(symbol); 97 | cancelSubscribe(optionSymbol); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /tquant-algorithm/src/main/java/com/tquant/algorithm/algos/SmaAlgo.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algos; 2 | 3 | import com.tquant.core.core.AlgoTemplate; 4 | import com.tquant.core.indicators.Indicators; 5 | import com.tquant.core.model.data.Bar; 6 | import com.tquant.core.model.data.Order; 7 | import com.tquant.core.model.data.Position; 8 | import com.tquant.core.model.data.Tick; 9 | import com.tquant.core.model.data.Trade; 10 | import com.tquant.core.model.enums.BarType; 11 | import com.tquant.core.model.enums.Direction; 12 | import com.tquant.core.model.enums.OrderStatus; 13 | import com.tquant.core.model.enums.OrderType; 14 | import com.tquant.core.util.BarGenerator; 15 | import com.tquant.storage.dao.BarDAO; 16 | import java.math.BigDecimal; 17 | import java.math.RoundingMode; 18 | import java.time.LocalDateTime; 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | /** 25 | * Description: 26 | * 27 | * @author kevin 28 | * @date 2022/08/09 29 | */ 30 | public class SmaAlgo extends AlgoTemplate { 31 | 32 | private BarGenerator barGenerator; 33 | private List bars; 34 | private volatile int barSize; 35 | private int buyQuantity = 5; 36 | private Map> symbolFilledOrders = new HashMap<>(); 37 | private static String symbol; 38 | private BarDAO barDAO = new BarDAO(); 39 | private List shortSma; 40 | private List longSma; 41 | private Indicators indicators; 42 | 43 | public SmaAlgo() { 44 | } 45 | 46 | public SmaAlgo(Map settings) { 47 | super(settings); 48 | } 49 | 50 | @Override 51 | public void init() { 52 | if (settings == null) { 53 | settings = algoEngine.getSettings(this.getClass().getSimpleName()); 54 | } 55 | symbol = (String) settings.get("symbol"); 56 | barGenerator = new BarGenerator(bar -> onBar(bar)); 57 | 58 | bars = barDAO.queryBar(symbol, BarType.day.getValue(), LocalDateTime.of(2018, 1, 1, 0, 0), 59 | LocalDateTime.of(2018, 1, 15, 0, 0)); 60 | 61 | barSize = bars.size(); 62 | indicators = new Indicators(); 63 | shortSma = (List)indicators.sma(bars, 5); 64 | longSma = (List)indicators.sma(bars, 10); 65 | } 66 | 67 | @Override 68 | public void onStart() { 69 | 70 | } 71 | 72 | @Override 73 | public void onTick(Tick tick) { 74 | } 75 | 76 | @Override 77 | public void onOrder(Order order) { 78 | if (OrderStatus.Filled.name().equalsIgnoreCase(order.getStatus())) { 79 | List orders = symbolFilledOrders.get(order.getSymbol()); 80 | if (orders == null) { 81 | orders = new ArrayList<>(); 82 | symbolFilledOrders.put(order.getSymbol(), orders); 83 | } 84 | orders.add(order); 85 | } 86 | } 87 | 88 | @Override 89 | public void onTrade(Trade trade) { 90 | } 91 | 92 | @Override 93 | public void onBar(Bar bar) { 94 | log("{} onBar {}", getAlgoName(), bar); 95 | bars.add(bar); 96 | barSize++; 97 | shortSma = (List)indicators.sma(bars, 5); 98 | longSma = (List)indicators.sma(bars, 10); 99 | if (shortSma.get(barSize - 1).attr("close_ma5") > longSma.get(barSize - 1).attr("close_ma10")) { 100 | executeOrderCommand(bar.getSymbol(), bar.getClose(), Direction.BUY); 101 | } else { 102 | executeOrderCommand(bar.getSymbol(), bar.getClose(), Direction.SELL); 103 | } 104 | } 105 | 106 | private void executeOrderCommand(String symbol, double price, Direction direction) { 107 | log("Strategy {} entered, price {},position {}", getAlgoName(), price); 108 | String id = null; 109 | sendOrder(symbol, direction, round(price, 2), buyQuantity, false); 110 | log("Entered {} on {} orderId:{},price:{},amount:{}", getAlgoName(), symbol, id, price, buyQuantity); 111 | } 112 | 113 | @Override 114 | public void sendOrder(String symbol, Direction direction, double price, int volume, boolean stop) { 115 | Position position = getPosition(symbol); 116 | if (direction == Direction.BUY) { 117 | buy(symbol, round(price, 2), buyQuantity, OrderType.LMT); 118 | } else { 119 | if (position == null || position.getPosition() < 0) { 120 | log("{} position is null {}", getAlgoName(), position); 121 | return; 122 | } 123 | sell(symbol, round(price, 2), position.getPosition(), OrderType.LMT); 124 | } 125 | } 126 | 127 | public static double round(double value, int scales) { 128 | if (scales < 0) { 129 | throw new IllegalArgumentException(); 130 | } 131 | return BigDecimal.valueOf(value).setScale(scales, RoundingMode.HALF_UP).doubleValue(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /tquant-algorithm/src/main/java/com/tquant/algorithm/algos/ThetaGangAlgo.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algos; 2 | 3 | import com.tquant.algorithm.algos.thetagang.PortfolioManager; 4 | import com.tquant.algorithm.algos.thetagang.RollingSellPutConfig; 5 | import com.tquant.algorithm.algos.utils.JacksonUtils; 6 | import com.tquant.core.core.AlgoTemplate; 7 | import com.tquant.core.model.data.Order; 8 | import com.tquant.core.model.data.Tick; 9 | import com.tquant.core.model.data.Trade; 10 | 11 | /** 12 | * Description: 13 | * suggest to run this strategy weekly, and you can use crontab scripts. 14 | * thetagang strategy description: https://einvestingforbeginners.com/theta-gang-strategies/ 15 | * @author kevin 16 | * @date 2023/02/09 17 | */ 18 | public class ThetaGangAlgo extends AlgoTemplate { 19 | 20 | private PortfolioManager portfolioManager; 21 | 22 | @Override 23 | public void onStart() { 24 | portfolioManager = new PortfolioManager(JacksonUtils.convertValue(settings, RollingSellPutConfig.class), 25 | getAlgoEngine().getMainEngine().getLogger()); 26 | portfolioManager.manage(); 27 | 28 | exit(); 29 | } 30 | 31 | @Override 32 | public void onTick(Tick tick) { 33 | 34 | } 35 | 36 | @Override 37 | public void onOrder(Order order) { 38 | portfolioManager.orderStatusEvent(order); 39 | } 40 | 41 | @Override 42 | public void onTrade(Trade trade) { 43 | 44 | } 45 | 46 | private void exit() { 47 | System.exit(1); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /tquant-algorithm/src/main/java/com/tquant/algorithm/algos/thetagang/RollingSellPutConfig.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algos.thetagang; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Description: 10 | * 11 | * @author kevin 12 | * @date 2023/02/09 13 | */ 14 | @Data 15 | @NoArgsConstructor 16 | public class RollingSellPutConfig { 17 | 18 | private boolean enable; 19 | private Account account; 20 | private OptionChains optionChains; 21 | private RollWhen rollWhen; 22 | private WriteWhen writeWhen; 23 | private Target target; 24 | private Map symbols; 25 | 26 | @Data 27 | @NoArgsConstructor 28 | public static class Account { 29 | private String accountId; 30 | private Boolean cancelOrders; 31 | private Double marginUsage; 32 | } 33 | 34 | @Data 35 | @NoArgsConstructor 36 | public static class OptionChains { 37 | 38 | private int expirations; 39 | private int strikes; 40 | } 41 | 42 | @Data 43 | @NoArgsConstructor 44 | public static class RollWhen { 45 | 46 | private Double pnl; 47 | private Integer dte; 48 | private Integer minDte; 49 | private Integer maxDte; 50 | private Double closeAtPnl; 51 | private Double minPnl; 52 | private Calls calls; 53 | private Puts puts; 54 | 55 | @Data 56 | @NoArgsConstructor 57 | public static class Calls { 58 | 59 | private boolean itm; 60 | private boolean creditOnly; 61 | } 62 | 63 | @Data 64 | @NoArgsConstructor 65 | public static class Puts { 66 | 67 | private boolean itm; 68 | private boolean creditOnly; 69 | } 70 | } 71 | 72 | @Data 73 | @NoArgsConstructor 74 | public static class WriteWhen { 75 | 76 | private Calls calls; 77 | private Puts puts; 78 | 79 | @Data 80 | @NoArgsConstructor 81 | public static class Calls { 82 | 83 | private Boolean green; 84 | private Double capFactor; 85 | } 86 | 87 | @Data 88 | @NoArgsConstructor 89 | public static class Puts { 90 | 91 | private boolean red; 92 | } 93 | } 94 | 95 | @Data 96 | @NoArgsConstructor 97 | public static class Target { 98 | 99 | private int dte; 100 | private double delta; 101 | private double maximumNewContractsPercent; 102 | private int minimumOpenInterest; 103 | } 104 | 105 | @Data 106 | @NoArgsConstructor 107 | public static class Symbol { 108 | 109 | private double weight; 110 | private Puts puts; 111 | private Calls calls; 112 | private String primaryExchange; 113 | private Double delta; 114 | 115 | @Data 116 | @NoArgsConstructor 117 | public static class Puts { 118 | 119 | private Double delta; 120 | private Double strikeLimit; 121 | } 122 | 123 | @Data 124 | @NoArgsConstructor 125 | public static class Calls { 126 | 127 | private Double delta; 128 | private Double strikeLimit; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tquant-algorithm/src/main/java/com/tquant/algorithm/algos/utils/JacksonUtils.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algos.utils; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 8 | import com.fasterxml.jackson.databind.SerializationFeature; 9 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 10 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 11 | import java.time.LocalDate; 12 | import java.time.format.DateTimeFormatter; 13 | import java.util.Map; 14 | 15 | /** 16 | * Description: 17 | * 18 | * @author kevin 19 | * @date 2023/02/13 20 | */ 21 | public class JacksonUtils { 22 | 23 | private static final ObjectMapper objectMapper = new ObjectMapper(); 24 | 25 | static { 26 | JavaTimeModule javaTimeModule = new JavaTimeModule(); 27 | //处理LocalDate 28 | DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 29 | javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter)); 30 | objectMapper.registerModule(javaTimeModule); 31 | objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); 32 | objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); 33 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 34 | objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 35 | objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); 36 | objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); 37 | } 38 | 39 | public static ObjectMapper getObjectMapper() { 40 | return objectMapper; 41 | } 42 | 43 | public static T convertValue(Map map, Class clazz) { 44 | try { 45 | return getObjectMapper().convertValue(map, clazz); 46 | } catch (Exception e) { 47 | e.printStackTrace(); 48 | } 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tquant-algorithm/src/main/java/com/tquant/algorithm/algos/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algos.utils; 2 | 3 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionBriefItem; 4 | import com.tigerbrokers.stock.openapi.client.struct.enums.Right; 5 | import com.tquant.algorithm.algos.thetagang.RollingSellPutConfig; 6 | import com.tquant.core.model.data.Contract; 7 | import com.tquant.core.model.data.Position; 8 | import com.tquant.core.model.data.RealtimeQuote; 9 | import com.tquant.core.model.data.Tick; 10 | import java.time.LocalDate; 11 | import java.util.ArrayList; 12 | import java.time.format.DateTimeFormatter; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * Description: 19 | * 20 | * @author kevin 21 | * @date 2023/02/09 22 | */ 23 | public class Utils { 24 | private static DateTimeFormatter YYYY_MM_DD_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd"); 25 | private static DateTimeFormatter YYYY_MM_DD_SEP_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 26 | private static DateTimeFormatter YYYY_MM_FORMAT = DateTimeFormatter.ofPattern("yyyyMM"); 27 | 28 | public static LocalDate contractDateToDatetime(String expiration) { 29 | if (expiration.length() == 8) { 30 | return LocalDate.parse(expiration, YYYY_MM_DD_FORMAT); 31 | } else { 32 | return LocalDate.parse(expiration, YYYY_MM_FORMAT); 33 | } 34 | } 35 | 36 | public static int optionDte(String expiration) { 37 | if (expiration == null) { 38 | return 0; 39 | } 40 | LocalDate today = LocalDate.now(); 41 | LocalDate contractDate = convertStringToLocalDate(expiration); 42 | return (int) java.time.Duration.between(today.atStartOfDay(), contractDate.atStartOfDay()).toDays(); 43 | } 44 | 45 | public static LocalDate convertStringToLocalDate(String date) { 46 | if (date != null) { 47 | if (date.length() == 8) { 48 | return LocalDate.parse(date, YYYY_MM_DD_FORMAT); 49 | } else if (date.length() == 10) { 50 | return LocalDate.parse(date, YYYY_MM_DD_SEP_FORMAT); 51 | } 52 | } 53 | return null; 54 | } 55 | 56 | public static Map> portfolioPositionsToDict(List portfolioPositions) { 57 | Map> d = new HashMap<>(); 58 | for (Position p : portfolioPositions) { 59 | String symbol = p.getContract().getSymbol(); 60 | if (!d.containsKey(symbol)) { 61 | d.put(symbol, new ArrayList<>()); 62 | } 63 | d.get(symbol).add(p); 64 | } 65 | return d; 66 | } 67 | 68 | public static double positionPnl(Position p) { 69 | return p.getUnrealizedPnl() / Math.abs(p.getAverageCost() * p.getPosition() * (p.getContract().getMultiplier() != null ? p.getContract() 70 | .getMultiplier() : 1)); 71 | } 72 | 73 | /** 74 | * @param ticker 75 | * @return 76 | */ 77 | public static double midpointOrMarketPrice(Tick ticker) { 78 | double midpoint = ticker.getMidpoint(); 79 | if (midpoint <= 0) { 80 | return ticker.getLatestPrice(); 81 | } else { 82 | return midpoint; 83 | } 84 | } 85 | 86 | public static double midpointOrAskPrice(Tick ticker) { 87 | double midpoint = ticker.getMidpoint(); 88 | if (midpoint <= 0D) { 89 | return ticker.getAskPrice(); 90 | } else { 91 | return midpoint; 92 | } 93 | } 94 | 95 | public static int countShortOptionPositions(String symbol, Map> portfolioPositions, String right) { 96 | if (portfolioPositions.containsKey(symbol)) { 97 | return (int) Math.floor( 98 | -portfolioPositions.get(symbol) 99 | .stream() 100 | .filter(p -> p.isOption(right) && p.getPosition() < 0) 101 | .mapToDouble(Position::getPosition) 102 | .sum() 103 | ); 104 | } 105 | 106 | return 0; 107 | } 108 | 109 | public static double getStrikeLimit(RollingSellPutConfig config, String symbol, String right) { 110 | String pOrC = "calls"; 111 | if (right.equalsIgnoreCase(Right.PUT.name())) { 112 | pOrC = "puts"; 113 | } 114 | Map symbols = config.getSymbols(); 115 | if (symbols != null && symbols.containsKey(symbol)) { 116 | if (pOrC.equalsIgnoreCase("calls")) { 117 | RollingSellPutConfig.Symbol.Calls calls = symbols.get(symbol).getCalls(); 118 | if (calls != null) { 119 | return calls.getStrikeLimit(); 120 | } else { 121 | return 0D; 122 | } 123 | } else { 124 | RollingSellPutConfig.Symbol.Puts puts = symbols.get(symbol).getPuts(); 125 | if (puts != null) { 126 | return puts.getStrikeLimit(); 127 | } else { 128 | return 0D; 129 | } 130 | } 131 | } 132 | return 0D; 133 | } 134 | 135 | public static double getCallCap(RollingSellPutConfig config) { 136 | RollingSellPutConfig.WriteWhen writeWhen = config.getWriteWhen(); 137 | if (writeWhen != null) { 138 | RollingSellPutConfig.WriteWhen.Calls calls = writeWhen.getCalls(); 139 | if (calls != null) { 140 | Double capFactor = calls.getCapFactor(); 141 | if (capFactor != null) { 142 | return Math.max(0, Math.min(1.0, (double) capFactor)); 143 | } 144 | } 145 | } 146 | return 1.0; 147 | } 148 | 149 | public static Double getTargetDelta(RollingSellPutConfig config, String symbol, String right) { 150 | RollingSellPutConfig.Symbol configSymbol = config.getSymbols().get(symbol); 151 | if (isCall(right)) { 152 | if (configSymbol.getCalls() != null && configSymbol.getCalls().getDelta() != null) { 153 | return configSymbol.getCalls().getDelta(); 154 | } else if (configSymbol.getDelta() != null) { 155 | return configSymbol.getDelta(); 156 | } else { 157 | return config.getTarget().getDelta(); 158 | } 159 | } else if (isPut(right)) { 160 | if (configSymbol.getPuts() != null && configSymbol.getPuts().getDelta() != null) { 161 | return configSymbol.getPuts().getDelta(); 162 | } else if (configSymbol.getDelta() != null) { 163 | return configSymbol.getDelta(); 164 | } else { 165 | return config.getTarget().getDelta(); 166 | } 167 | } 168 | return null; 169 | } 170 | 171 | public static Double getLowestPrice(Tick tick) { 172 | return Math.min(tick.getMidpoint(), tick.getLatestPrice()); 173 | } 174 | 175 | public static Double getHighestPrice(Tick tick) { 176 | return Math.max(tick.getMidpoint(), tick.getLatestPrice()); 177 | } 178 | 179 | public static RealtimeQuote convertToRealtimeQuote(OptionBriefItem optionBriefItem) { 180 | RealtimeQuote realtimeQuote = new RealtimeQuote(); 181 | realtimeQuote.setSymbol(optionBriefItem.getSymbol()); 182 | realtimeQuote.setOpen(optionBriefItem.getOpen()); 183 | realtimeQuote.setHigh(optionBriefItem.getHigh()); 184 | realtimeQuote.setLow(optionBriefItem.getLow()); 185 | realtimeQuote.setClose(optionBriefItem.getLatestPrice()); 186 | realtimeQuote.setPreClose(optionBriefItem.getPreClose()); 187 | realtimeQuote.setLatestPrice(optionBriefItem.getLatestPrice()); 188 | realtimeQuote.setLatestTime(optionBriefItem.getTimestamp()); 189 | if (optionBriefItem.getAskPrice() != null) { 190 | realtimeQuote.setAskPrice(optionBriefItem.getAskPrice()); 191 | } 192 | if (optionBriefItem.getAskSize() != null) { 193 | realtimeQuote.setAskSize(Long.valueOf(optionBriefItem.getAskSize())); 194 | } 195 | if (optionBriefItem.getBidPrice() != null) { 196 | realtimeQuote.setBidPrice(optionBriefItem.getBidPrice()); 197 | } 198 | if (optionBriefItem.getBidSize() != null) { 199 | realtimeQuote.setBidSize(Long.valueOf(optionBriefItem.getBidSize())); 200 | } 201 | realtimeQuote.setVolume(Long.valueOf(optionBriefItem.getVolume())); 202 | realtimeQuote.setOpenInterest(optionBriefItem.getOpenInterest()); 203 | return realtimeQuote; 204 | } 205 | 206 | public static double roundToNearest(double x) { 207 | double firstDecimal = (x % 1) * 10; 208 | 209 | if (firstDecimal < 5) { 210 | return Math.round(x * 10) / 10; 211 | } else if (firstDecimal > 5) { 212 | return x - firstDecimal / 10 + 0.5; 213 | } else { 214 | return Math.floor(x * 10) / 10; 215 | } 216 | } 217 | 218 | public static boolean isPut(String right) { 219 | return right != null && right.equalsIgnoreCase(Right.PUT.name()); 220 | } 221 | 222 | public static boolean isCall(String right) { 223 | return right != null && right.equalsIgnoreCase(Right.CALL.name()); 224 | } 225 | 226 | public static boolean isValidStrike(String right, double strike, double marketPrice, Double strikeLimit) { 227 | if (Utils.isPut(right)) { 228 | if (strikeLimit != null && strikeLimit > 0) { 229 | // put 要尽量低的价格 230 | return strike <= marketPrice && strike <= strikeLimit; 231 | } else { 232 | return strike <= marketPrice; 233 | } 234 | } else if (Utils.isCall(right)) { 235 | // call 要尽量高的价格 236 | if (strikeLimit != null && strikeLimit > 0) { 237 | return strike >= marketPrice && strike >= strikeLimit; 238 | } else { 239 | return strike >= marketPrice; 240 | } 241 | } 242 | return false; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /tquant-algorithm/src/test/java/com/tquant/algorithm/algo/SmaIndicatorTest.java: -------------------------------------------------------------------------------- 1 | package com.tquant.algorithm.algo; 2 | 3 | import com.tquant.core.indicators.Indicators; 4 | import com.tquant.core.model.data.Bar; 5 | import com.tquant.core.model.enums.BarType; 6 | import com.tquant.storage.dao.BarDAO; 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | 10 | /** 11 | * Description: 12 | * 13 | * @author kevin 14 | * @date 2022/08/10 15 | */ 16 | public class SmaIndicatorTest { 17 | 18 | public static void main(String[] args) { 19 | BarDAO barDAO = new BarDAO(); 20 | List bars = barDAO.queryBar("AAPL", BarType.day.getValue(), LocalDateTime.of(2018, 1, 1, 0, 0), 21 | LocalDateTime.of(2018, 1, 31, 0, 0)); 22 | System.out.println(bars.size()); 23 | 24 | Indicators indicators = new Indicators(); 25 | double[] closes = bars.stream().mapToDouble(bar -> bar.getClose()).toArray(); 26 | double[] shortSma = indicators.sma(closes, 5); 27 | double[] longSma = indicators.sma(closes, 10); 28 | 29 | System.out.println(closes[0] + "," + closes[1]); 30 | System.out.println(shortSma[20] + "," + longSma[20]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tquant-asset/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | tiger-quant 7 | com.tigerbrokers 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | tquant-asset 13 | 14 | 15 | -------------------------------------------------------------------------------- /tquant-backtester/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | tiger-quant 7 | com.tigerbrokers 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | tquant-backtester 13 | 14 | 15 | 16 | com.tigerbrokers 17 | tquant-core 18 | 1.0.0 19 | 20 | 21 | com.tigerbrokers 22 | tquant-storage 23 | 1.0.0 24 | 25 | 26 | com.tigerbrokers 27 | tquant-algorithm 28 | 1.0.0 29 | 30 | 31 | com.tigerbrokers 32 | tquant-gateway 33 | 1.0.0 34 | 35 | 36 | com.tigerbrokers 37 | tquant-bootstrap 38 | 1.0.0 39 | 40 | 41 | tech.tablesaw 42 | tablesaw-core 43 | 44 | 45 | tech.tablesaw 46 | tablesaw-jsplot 47 | 48 | 49 | tech.tablesaw 50 | tablesaw-json 51 | 52 | 53 | junit 54 | junit 55 | 56 | 57 | -------------------------------------------------------------------------------- /tquant-backtester/src/main/java/com/tquant/backtester/Backtesting.java: -------------------------------------------------------------------------------- 1 | package com.tquant.backtester; 2 | 3 | import com.tquant.algorithm.algos.SmaAlgo; 4 | import com.tquant.bootstrap.command.CliRunner; 5 | import com.tquant.bootstrap.command.CommandExecuteTemplate; 6 | import com.tquant.core.core.AlgoEngine; 7 | import com.tquant.core.core.MainEngine; 8 | import com.tquant.core.event.EventEngine; 9 | import com.tquant.core.model.enums.Direction; 10 | import com.tquant.gateway.tiger.TigerGateway; 11 | import com.tquant.core.model.data.Trade; 12 | import com.tquant.core.model.enums.BacktestingMode; 13 | import com.tquant.core.model.enums.OrderType; 14 | import java.time.LocalDateTime; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import org.apache.commons.cli.CommandLine; 18 | import org.apache.commons.cli.Options; 19 | 20 | import static com.tquant.core.util.QuantConstants.ALGO_CONFIG_PATH; 21 | import static com.tquant.core.util.QuantConstants.ALGO_CONFIG_PATH_PROP; 22 | import static com.tquant.core.util.QuantConstants.GATEWAY_CONFIG_PATH; 23 | import static com.tquant.core.util.QuantConstants.TIGER_CONFIG_PATH_PROP; 24 | 25 | /** 26 | * Description: 27 | * 28 | * @author kevin 29 | * @date 2022/08/02 30 | */ 31 | public class Backtesting implements CliRunner { 32 | 33 | public static void main(String[] args) { 34 | CommandExecuteTemplate.execute(args, "-[a][g]", new Backtesting()); 35 | } 36 | 37 | private void initProperties(String algoConfigPath, String gatewayConfigPath) { 38 | System.setProperty(ALGO_CONFIG_PATH_PROP, algoConfigPath); 39 | System.setProperty(TIGER_CONFIG_PATH_PROP, gatewayConfigPath); 40 | } 41 | 42 | private BacktestingEngine initEngine() { 43 | LocalDateTime start = LocalDateTime.of(2021, 1, 1, 0, 0, 0); 44 | LocalDateTime end = LocalDateTime.of(2021, 12, 31, 23, 59, 59); 45 | return new BacktestingEngine("AAPL", 60, "day", start, 3D / 10000, 46 | 0.2, 1, 0.1, 1000000, end, BacktestingMode.BAR, 0.1, 240, 10); 47 | } 48 | 49 | private Map initSettings() { 50 | Map settings = new HashMap<>(); 51 | settings.put("symbol", "AAPL"); 52 | settings.put("bars", 10); 53 | settings.put("mode", BacktestingMode.BAR); 54 | return settings; 55 | } 56 | 57 | private void testBacktesting() { 58 | BacktestingEngine backtestingEngine = initEngine(); 59 | Map settings = initSettings(); 60 | 61 | BacktestingAlgo algoTemplate = new BacktestingAlgo(settings); 62 | EventEngine eventEngine = new EventEngine(); 63 | MainEngine mainEngine = new MainEngine(eventEngine); 64 | mainEngine.addGateway(new TigerGateway(eventEngine)); 65 | AlgoEngine algoEngine = new AlgoEngine(mainEngine, eventEngine); 66 | algoTemplate.setAlgoEngine(algoEngine); 67 | algoTemplate.setBacktestingEngine(backtestingEngine); 68 | 69 | backtestingEngine.addStrategy(algoTemplate); 70 | backtestingEngine.loadData(); 71 | backtestingEngine.runBacktesting(); 72 | backtestingEngine.calculateResult(); 73 | backtestingEngine.calculateStatistics(); 74 | } 75 | 76 | private void testCalculateResult() { 77 | BacktestingEngine backtestingEngine = initEngine(); 78 | LocalDateTime start = backtestingEngine.getStart(); 79 | for (int i = 0; i < 10; i++) { 80 | Trade trade = new Trade(); 81 | trade.setId(i + ""); 82 | trade.setTime(start); 83 | trade.setVolume(100); 84 | trade.setDirection(i % 2 == 0 ? "BUY" : "SELL"); 85 | trade.setPrice(10+i); 86 | trade.setOrderId(1000L+i); 87 | trade.setSymbol("AAPL"); 88 | trade.setOrderType(OrderType.LMT.getType()); 89 | backtestingEngine.addTrade(trade); 90 | start = start.plusDays(1); 91 | } 92 | backtestingEngine.calculateResult(); 93 | backtestingEngine.calculateStatistics(); 94 | } 95 | 96 | @Override 97 | public Options initOptions() { 98 | Options options = new Options(); 99 | options.addOption(ALGO_CONFIG_PATH, true, "Algorithm strategy configuration path, cannot be empty"); 100 | options.addOption(GATEWAY_CONFIG_PATH, true, "Gateway config path, cannot be empty"); 101 | return options; 102 | } 103 | 104 | @Override 105 | public boolean validateOptions(CommandLine cmdLine) { 106 | return (cmdLine.hasOption(ALGO_CONFIG_PATH) && cmdLine.hasOption(GATEWAY_CONFIG_PATH)); 107 | } 108 | 109 | @Override 110 | public void start(CommandLine cmdLine) { 111 | String algoConfigPath = cmdLine.getOptionValue(ALGO_CONFIG_PATH); 112 | String gatewayConfigPath = cmdLine.getOptionValue(GATEWAY_CONFIG_PATH); 113 | initProperties(algoConfigPath, gatewayConfigPath); 114 | startService(); 115 | } 116 | 117 | private void startService() { 118 | Backtesting backtesting = new Backtesting(); 119 | //backtesting.testCalculateResult(); 120 | backtesting.testBacktesting(); 121 | } 122 | } 123 | 124 | class BacktestingAlgo extends SmaAlgo { 125 | private BacktestingEngine backtestingEngine; 126 | 127 | public BacktestingAlgo(Map settings) { 128 | super(settings); 129 | } 130 | 131 | public void setBacktestingEngine(BacktestingEngine backtestingEngine) { 132 | this.backtestingEngine = backtestingEngine; 133 | } 134 | 135 | @Override 136 | public void sendOrder(String symbol, Direction direction, double price, int volume, boolean stop) { 137 | backtestingEngine.sendOrder(direction, price, volume, stop); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tquant-backtester/src/test/java/com/tquant/backtester/TablesawBarTest.java: -------------------------------------------------------------------------------- 1 | package com.tquant.backtester; 2 | 3 | import static tech.tablesaw.aggregate.AggregateFunctions.sum; 4 | 5 | import tech.tablesaw.api.IntColumn; 6 | import tech.tablesaw.api.NumericColumn; 7 | import tech.tablesaw.api.Table; 8 | import tech.tablesaw.columns.numbers.IntColumnType; 9 | import tech.tablesaw.plotly.Plot; 10 | import tech.tablesaw.plotly.api.HorizontalBarPlot; 11 | import tech.tablesaw.plotly.api.VerticalBarPlot; 12 | import tech.tablesaw.plotly.components.Figure; 13 | import tech.tablesaw.plotly.components.Layout; 14 | import tech.tablesaw.plotly.components.Marker; 15 | import tech.tablesaw.plotly.traces.BarTrace; 16 | import tech.tablesaw.plotly.traces.Trace; 17 | 18 | /** 19 | * Description: 20 | * 21 | * @author kevin 22 | * @date 2022/08/10 23 | */ 24 | public class TablesawBarTest { 25 | public static void main(String[] args) throws Exception { 26 | Table table = Table.read().csv("data/tornadoes_1950-2014.csv"); 27 | NumericColumn logNInjuries = table.numberColumn("injuries").add(1).logN(); 28 | logNInjuries.setName("log injuries"); 29 | table.addColumns(logNInjuries); 30 | IntColumn scale = table.intColumn("scale"); 31 | scale.set(scale.isLessThan(0), IntColumnType.missingValueIndicator()); 32 | 33 | Table summaryTable = table.summarize("fatalities", "log injuries", sum).by("Scale"); 34 | 35 | Plot.show( 36 | HorizontalBarPlot.create( 37 | "Tornado Impact", 38 | summaryTable, 39 | "scale", 40 | Layout.BarMode.STACK, 41 | "Sum [Fatalities]", 42 | "Sum [log injuries]")); 43 | 44 | Plot.show( 45 | VerticalBarPlot.create( 46 | "Tornado Impact", 47 | summaryTable, 48 | "scale", 49 | Layout.BarMode.GROUP, 50 | "Sum [Fatalities]", 51 | "Sum [log injuries]")); 52 | 53 | Layout layout = 54 | Layout.builder() 55 | .title("Tornado Impact") 56 | .barMode(Layout.BarMode.GROUP) 57 | .showLegend(true) 58 | .build(); 59 | 60 | String[] numberColNames = {"Sum [Fatalities]", "Sum [log injuries]"}; 61 | String[] colors = {"#85144b", "#FF4136"}; 62 | 63 | Trace[] traces = new Trace[2]; 64 | for (int i = 0; i < 2; i++) { 65 | String name = numberColNames[i]; 66 | BarTrace trace = 67 | BarTrace.builder(summaryTable.categoricalColumn("scale"), summaryTable.numberColumn(name)) 68 | .orientation(BarTrace.Orientation.VERTICAL) 69 | .marker(Marker.builder().color(colors[i]).build()) 70 | .showLegend(true) 71 | .name(name) 72 | .build(); 73 | traces[i] = trace; 74 | } 75 | Plot.show(new Figure(layout, traces)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tquant-backtester/src/test/java/com/tquant/backtester/TablesawBubbleTest.java: -------------------------------------------------------------------------------- 1 | package com.tquant.backtester; 2 | 3 | import java.io.IOException; 4 | import tech.tablesaw.api.Table; 5 | import tech.tablesaw.plotly.Plot; 6 | import tech.tablesaw.plotly.api.BubblePlot; 7 | import tech.tablesaw.plotly.components.Figure; 8 | 9 | /** 10 | * Description: 11 | * 12 | * @author kevin 13 | * @date 2022/08/10 14 | */ 15 | public class TablesawBubbleTest { 16 | 17 | public static void main(String[] args) throws IOException { 18 | 19 | Table wines = Table.read().csv("data/test_wines.csv"); 20 | 21 | Table champagne = 22 | wines.where( 23 | wines 24 | .stringColumn("wine type") 25 | .isEqualTo("Champagne & Sparkling") 26 | .and(wines.stringColumn("region").isEqualTo("California"))); 27 | 28 | Figure figure = 29 | BubblePlot.create( 30 | "Average retail price for champagnes by year and rating", 31 | champagne, // table name 32 | "highest pro score", // x variable column name 33 | "year", // y variable column name 34 | "Mean Retail" // bubble size 35 | ); 36 | 37 | Plot.show(figure); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tquant-backtester/src/test/java/com/tquant/backtester/TablesawTest.java: -------------------------------------------------------------------------------- 1 | package com.tquant.backtester; 2 | 3 | import java.lang.reflect.Field; 4 | import java.time.LocalDate; 5 | import java.util.HashMap; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.Map; 9 | import org.junit.Test; 10 | import tech.tablesaw.api.DoubleColumn; 11 | import tech.tablesaw.api.StringColumn; 12 | import tech.tablesaw.api.Table; 13 | import tech.tablesaw.columns.Column; 14 | import tech.tablesaw.io.DataFrameReader; 15 | 16 | /** 17 | * Description: 18 | * 19 | * @author kevin 20 | * @date 2022/08/05 21 | */ 22 | public class TablesawTest { 23 | 24 | @Test 25 | public void testColumn() { 26 | double[] numbers = {1, 2, 3, 4}; 27 | DoubleColumn nc = DoubleColumn.create("nc", numbers); 28 | System.out.println(nc.print()); 29 | double three = nc.get(2); 30 | System.out.println(three); 31 | DoubleColumn nc2 = nc.multiply(4); 32 | System.out.println(nc2.print()); 33 | 34 | System.out.println(nc.isLessThan(3)); 35 | } 36 | 37 | @Test 38 | public void testCreateTable() { 39 | String[] animals = {"bear", "cat", "giraffe"}; 40 | double[] cuteness = {90.1, 84.3, 99.7}; 41 | 42 | Table cuteAnimals = 43 | Table.create("Cute Animals") 44 | .addColumns( 45 | StringColumn.create("Animal types", animals), 46 | DoubleColumn.create("rating", cuteness)); 47 | System.out.println(cuteAnimals); 48 | 49 | System.out.println(cuteAnimals.structure()); 50 | 51 | System.out.println(cuteAnimals.first(1)); 52 | System.out.println(cuteAnimals.last(1)); 53 | } 54 | 55 | @Test 56 | public void testDataFrame() { 57 | Map dailyResults = new HashMap<>(); 58 | dailyResults.values(); 59 | //Column 60 | } 61 | 62 | @Test 63 | public void testFields() { 64 | Field[] fields = DailyResult.class.getDeclaredFields(); 65 | 66 | System.out.println("Number of fields = " + fields.length); 67 | 68 | for (Field field : fields) { 69 | System.out.println("Field name = " + field.getName()+", type = " + field.getType()); 70 | if (field.getType()==LocalDate.class) { 71 | System.out.println("date"); 72 | } else if (field.getType()==double.class) { 73 | System.out.println("double"); 74 | } else if (field.getType() == List.class) { 75 | System.out.println("list"); 76 | } else if (field.getType() == int.class) { 77 | System.out.println("int"); 78 | } 79 | } 80 | } 81 | 82 | @Test 83 | public void testCumSum() { 84 | DoubleColumn doubleColumn = DoubleColumn.create("balance", 6).fillWith(0).set(0, 100D); 85 | 86 | DoubleColumn netPnl = DoubleColumn.create("netPnl", 0,1, 2, 3, 4, 5); 87 | DoubleColumn new1=doubleColumn.add(netPnl).cumSum(); 88 | 89 | Iterator iterator = new1.iterator(); 90 | while (iterator.hasNext()) { 91 | System.out.println(iterator.next()); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /tquant-backtester/src/test/java/com/tquant/backtester/TimeSeriesVisualizations.java: -------------------------------------------------------------------------------- 1 | package com.tquant.backtester; 2 | 3 | import tech.tablesaw.api.Table; 4 | import tech.tablesaw.columns.numbers.NumberColumnFormatter; 5 | import tech.tablesaw.plotly.Plot; 6 | import tech.tablesaw.plotly.api.CandlestickPlot; 7 | import tech.tablesaw.plotly.api.LinePlot; 8 | import tech.tablesaw.plotly.api.OHLCPlot; 9 | import tech.tablesaw.plotly.api.TimeSeriesPlot; 10 | import tech.tablesaw.plotly.components.Figure; 11 | import tech.tablesaw.plotly.components.Layout; 12 | import tech.tablesaw.plotly.components.Marker; 13 | import tech.tablesaw.plotly.traces.ScatterTrace; 14 | 15 | /** 16 | * Description: 17 | * 18 | * @author kevin 19 | * @date 2022/08/10 20 | */ 21 | public class TimeSeriesVisualizations { 22 | public static void main(String[] args) throws Exception { 23 | Table bush = Table.read().csv("data/bush.csv"); 24 | Table foxOnly = bush.where(bush.stringColumn("who").equalsIgnoreCase("fox")); 25 | Figure foxPlot = 26 | TimeSeriesPlot.create("George W. Bush approval ratings", foxOnly, "date", "approval"); 27 | Plot.show(foxPlot); 28 | 29 | Plot.show( 30 | TimeSeriesPlot.create("George W. Bush approval ratings", bush, "date", "approval", "who")); 31 | 32 | Table robberies = Table.read().csv("data/boston-robberies.csv"); 33 | Plot.show( 34 | LinePlot.create( 35 | "Boston Robberies by month: Jan 1966-Oct 1975", robberies, "Record", "Robberies")); 36 | 37 | Layout layout = 38 | Layout.builder("Boston Robberies by month: Jan 1966-Oct 1975", "year", "robberies").build(); 39 | 40 | ScatterTrace trace = 41 | ScatterTrace.builder(robberies.numberColumn("Record"), robberies.numberColumn("Robberies")) 42 | .mode(ScatterTrace.Mode.LINE) 43 | .marker(Marker.builder().color("#3D9970").build()) 44 | .fill(ScatterTrace.Fill.TO_NEXT_Y) 45 | .build(); 46 | Plot.show(new Figure(layout, trace)); 47 | 48 | Table priceTable = Table.read().csv("data/ohlcdata.csv"); 49 | priceTable.addColumns( 50 | priceTable.dateColumn("date").atStartOfDay().setName("date time"), 51 | priceTable.dateColumn("date").atStartOfDay().asInstantColumn().setName("instant")); 52 | priceTable.numberColumn("Volume").setPrintFormatter(NumberColumnFormatter.intsWithGrouping()); 53 | 54 | Plot.show(OHLCPlot.create("Prices", priceTable, "date", "open", "high", "low", "close")); 55 | 56 | Plot.show(CandlestickPlot.create("Prices", priceTable, "date", "open", "high", "low", "close")); 57 | 58 | // using a datetime column 59 | Table dateTable = Table.read().csv("data/dateTimeTestFile.csv"); 60 | Plot.show( 61 | TimeSeriesPlot.create( 62 | "Value over time", 63 | "time", 64 | dateTable.dateTimeColumn("Time"), 65 | "values", 66 | dateTable.numberColumn("Value"))); 67 | 68 | // using a datetime column2 69 | Plot.show(TimeSeriesPlot.createDateTimeSeries("Value over time", dateTable, "Time", "Value")); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tquant-bootstrap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | tiger-quant 7 | com.tigerbrokers 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | tquant-bootstrap 13 | 14 | 15 | 16 | com.tigerbrokers 17 | tquant-core 18 | 1.0.0 19 | 20 | 21 | com.tigerbrokers 22 | tquant-gateway 23 | 1.0.0 24 | 25 | 26 | com.tigerbrokers 27 | tquant-algorithm 28 | 1.0.0 29 | 30 | 31 | commons-cli 32 | commons-cli 33 | 34 | 35 | io.github.tigerbrokers 36 | openapi-java-sdk 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-assembly-plugin 45 | ${maven.plugin.version} 46 | 47 | 48 | assembly 49 | package 50 | 51 | single 52 | 53 | 54 | 55 | 56 | true 57 | com.tquant.bootstrap.TigerQuantBootstrap 58 | 59 | 60 | 61 | jar-with-dependencies 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /tquant-bootstrap/src/main/assembly/assembly.xml: -------------------------------------------------------------------------------- 1 | 2 | assembly 3 | 4 | jar 5 | 6 | true 7 | 8 | 9 | false 10 | lib 11 | runtime 12 | ${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension} 13 | 14 | 15 | 16 | 17 | 18 | target/classes 19 | classes 20 | 21 | 22 | -------------------------------------------------------------------------------- /tquant-bootstrap/src/main/java/com/tquant/bootstrap/TigerQuantBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.tquant.bootstrap; 2 | 3 | import com.tquant.bootstrap.command.CliRunner; 4 | import com.tquant.bootstrap.command.CommandExecuteTemplate; 5 | import com.tquant.core.TigerQuantException; 6 | import com.tquant.core.core.MainEngine; 7 | import com.tquant.core.event.EventEngine; 8 | import com.tquant.gateway.tiger.TigerGateway; 9 | import java.util.concurrent.locks.Condition; 10 | import java.util.concurrent.locks.ReentrantLock; 11 | import org.apache.commons.cli.CommandLine; 12 | import org.apache.commons.cli.Options; 13 | 14 | import static com.tquant.core.util.QuantConstants.ALGO_CONFIG_PATH; 15 | import static com.tquant.core.util.QuantConstants.ALGO_CONFIG_PATH_PROP; 16 | import static com.tquant.core.util.QuantConstants.GATEWAY_CONFIG_PATH; 17 | import static com.tquant.core.util.QuantConstants.TIGER_CONFIG_PATH_PROP; 18 | 19 | /** 20 | * Description: bootstrap 21 | * 22 | * @author kevin 23 | * @date 2019/08/15 24 | */ 25 | public class TigerQuantBootstrap implements CliRunner { 26 | 27 | private static final ReentrantLock LOCK = new ReentrantLock(); 28 | private static final Condition STOP = LOCK.newCondition(); 29 | 30 | public static void main(String[] args) { 31 | LOCK.lock(); 32 | try { 33 | CommandExecuteTemplate.execute(args, "-[a][g]", new TigerQuantBootstrap()); 34 | 35 | STOP.await(); 36 | } catch (TigerQuantException e1){ 37 | e1.printStackTrace(); 38 | LOCK.unlock(); 39 | } catch (InterruptedException e) { 40 | throw new RuntimeException(e.getMessage()); 41 | } finally { 42 | LOCK.unlock(); 43 | } 44 | } 45 | 46 | public void startService() { 47 | 48 | EventEngine eventEngine = new EventEngine(); 49 | MainEngine mainEngine = new MainEngine(eventEngine); 50 | mainEngine.addGateway(new TigerGateway(eventEngine)); 51 | mainEngine.start(); 52 | addHook(mainEngine); 53 | } 54 | 55 | private void initProperties(String algoConfigPath, String gatewayConfigPath) { 56 | System.setProperty(ALGO_CONFIG_PATH_PROP, algoConfigPath); 57 | System.setProperty(TIGER_CONFIG_PATH_PROP, gatewayConfigPath); 58 | } 59 | 60 | private static void addHook(MainEngine mainEngine) { 61 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 62 | try { 63 | mainEngine.stop(); 64 | } catch (Exception e) { 65 | throw new RuntimeException("main stop exception ", e); 66 | } 67 | 68 | LOCK.lock(); 69 | try { 70 | STOP.signal(); 71 | } finally { 72 | LOCK.unlock(); 73 | } 74 | }, "main-shutdown-hook")); 75 | } 76 | 77 | @Override 78 | public Options initOptions() { 79 | Options options = new Options(); 80 | options.addOption(ALGO_CONFIG_PATH, true, "Algorithm strategy configuration path, cannot be empty"); 81 | options.addOption(GATEWAY_CONFIG_PATH, true, "Gateway config path, cannot be empty"); 82 | return options; 83 | } 84 | 85 | @Override 86 | public boolean validateOptions(CommandLine cmdLine) { 87 | return (cmdLine.hasOption(ALGO_CONFIG_PATH) && cmdLine.hasOption(GATEWAY_CONFIG_PATH)); 88 | } 89 | 90 | @Override 91 | public void start(CommandLine cmdLine) { 92 | String algoConfigPath = cmdLine.getOptionValue(ALGO_CONFIG_PATH); 93 | String gatewayConfigPath = cmdLine.getOptionValue(GATEWAY_CONFIG_PATH); 94 | initProperties(algoConfigPath, gatewayConfigPath); 95 | startService(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tquant-bootstrap/src/main/java/com/tquant/bootstrap/command/CliRunner.java: -------------------------------------------------------------------------------- 1 | package com.tquant.bootstrap.command; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2022/08/03 8 | */ 9 | import org.apache.commons.cli.CommandLine; 10 | import org.apache.commons.cli.Options; 11 | 12 | public interface CliRunner { 13 | Options initOptions(); 14 | 15 | boolean validateOptions(CommandLine var1); 16 | 17 | void start(CommandLine var1); 18 | } 19 | -------------------------------------------------------------------------------- /tquant-bootstrap/src/main/java/com/tquant/bootstrap/command/CommandExecuteTemplate.java: -------------------------------------------------------------------------------- 1 | package com.tquant.bootstrap.command; 2 | 3 | import org.apache.commons.cli.CommandLine; 4 | import org.apache.commons.cli.CommandLineParser; 5 | import org.apache.commons.cli.DefaultParser; 6 | import org.apache.commons.cli.HelpFormatter; 7 | import org.apache.commons.cli.Options; 8 | import org.apache.commons.cli.ParseException; 9 | 10 | /** 11 | * Description: 12 | * 13 | * @author kevin 14 | * @date 2022/08/03 15 | */ 16 | 17 | public class CommandExecuteTemplate { 18 | 19 | private static HelpFormatter formatter = new HelpFormatter(); 20 | 21 | public CommandExecuteTemplate() { 22 | } 23 | 24 | public static void execute(String[] args, String cmdName, CliRunner runner) { 25 | CommandLineParser parser = new DefaultParser(); 26 | Options options = runner.initOptions(); 27 | 28 | try { 29 | CommandLine cmdLine = parser.parse(options, args); 30 | if (!runner.validateOptions(cmdLine) || cmdLine.hasOption("help")) { 31 | formatter.printHelp(cmdName, options); 32 | return; 33 | } 34 | 35 | runner.start(cmdLine); 36 | } catch (ParseException var6) { 37 | System.out.println("Unexpected exception:" + var6.getMessage()); 38 | formatter.printHelp(cmdName, options); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /tquant-bootstrap/src/main/resources/global_setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "log.enable": true, 3 | "log.level": "info", 4 | "log.console": false, 5 | "log.file": true, 6 | "log.path": "log/", 7 | "storage.enable": true, 8 | "subscribe.enable": false, 9 | "contract.load.enable": false 10 | } -------------------------------------------------------------------------------- /tquant-bootstrap/src/main/resources/jdbc.properties: -------------------------------------------------------------------------------- 1 | # Locally mysql database for contract data and bar data storage and loading 2 | # If without this db config, we will load contract or bar data from interface every time 3 | jdbc.url=jdbc:mysql://localhost/tiger_quant 4 | jdbc.user=root 5 | jdbc.password=123456 -------------------------------------------------------------------------------- /tquant-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | tiger-quant 7 | com.tigerbrokers 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | tquant-core 13 | 14 | 15 | 16 | org.slf4j 17 | slf4j-api 18 | 19 | 20 | ch.qos.logback 21 | logback-classic 22 | 23 | 24 | ch.qos.logback 25 | logback-core 26 | 27 | 28 | org.projectlombok 29 | lombok 30 | 31 | 32 | com.alibaba 33 | fastjson 34 | 35 | 36 | org.ta4j 37 | ta4j-core 38 | 39 | 40 | com.tictactec 41 | ta-lib 42 | 43 | 44 | com.google.guava 45 | guava 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/TigerQuantException.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2019/08/15 8 | */ 9 | public class TigerQuantException extends RuntimeException { 10 | 11 | public TigerQuantException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/config/ConfigLoader.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.tquant.core.TigerQuantException; 5 | import com.tquant.core.model.Config; 6 | import java.io.BufferedInputStream; 7 | import java.io.FileInputStream; 8 | import java.io.IOException; 9 | import java.util.Map; 10 | 11 | /** 12 | * Description: gateway config loader 13 | * 14 | * @author kevin 15 | * @date 2019/08/15 16 | */ 17 | public class ConfigLoader { 18 | 19 | public static final String GLOBAL_FILENAME = "global_setting.json"; 20 | public static final Map GLOBAL_SETTINGS = loadConfig(GLOBAL_FILENAME); 21 | 22 | /** 23 | * @param fileName config fileName 24 | * @param clazz config class 25 | * @param config generic class 26 | */ 27 | public static T loadConfig(String fileName, Class clazz) { 28 | try (FileInputStream inputStream = new FileInputStream(fileName)) { 29 | return JSON.parseObject(inputStream, clazz); 30 | } catch (IOException e) { 31 | throw new TigerQuantException("parse config exception:" + e.getMessage()); 32 | } 33 | } 34 | 35 | /** 36 | * single config 37 | * 38 | * @param fileName config fileName 39 | */ 40 | public static Map loadConfig(String fileName) { 41 | try (BufferedInputStream inputStream = 42 | new BufferedInputStream( 43 | Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName))) { 44 | int available = inputStream.available(); 45 | System.out.println(available); 46 | return JSON.parseObject(inputStream, Map.class); 47 | } catch (IOException e) { 48 | throw new TigerQuantException("parse config exception:" + e.getMessage()); 49 | } 50 | } 51 | 52 | /** 53 | * all algorithm configs 54 | * 55 | * @param fileName algorithm config fileName 56 | */ 57 | public static Map> loadConfigs(String fileName) { 58 | try (FileInputStream inputStream = new FileInputStream(fileName)) { 59 | return JSON.parseObject(inputStream, Map.class); 60 | } catch (IOException e) { 61 | throw new TigerQuantException("parse config exception:" + e.getMessage()); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/core/AlgoTemplate.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.core; 2 | 3 | import com.tquant.core.model.data.Asset; 4 | import com.tquant.core.model.data.Bar; 5 | import com.tquant.core.model.data.Contract; 6 | import com.tquant.core.model.data.Order; 7 | import com.tquant.core.model.data.Position; 8 | import com.tquant.core.model.data.Tick; 9 | import com.tquant.core.model.data.Trade; 10 | import com.tquant.core.model.enums.BacktestingMode; 11 | import com.tquant.core.model.enums.BarType; 12 | import com.tquant.core.model.enums.Direction; 13 | import com.tquant.core.model.enums.OrderType; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | import lombok.Getter; 18 | import lombok.Setter; 19 | 20 | /** 21 | * Description: 22 | * 23 | * @author kevin 24 | * @date 2019/08/19 25 | */ 26 | public abstract class AlgoTemplate { 27 | 28 | @Setter @Getter 29 | protected String algoName; 30 | @Setter @Getter 31 | protected AlgoEngine algoEngine; 32 | @Setter @Getter 33 | protected Map settings; 34 | @Setter @Getter 35 | protected BacktestingMode mode; 36 | 37 | private boolean active = false; 38 | private Map activeOrders = new HashMap<>(); 39 | private Map ticks = new HashMap<>(); 40 | 41 | public AlgoTemplate() { 42 | String strategyName = this.getClass().getSimpleName(); 43 | setAlgoName(strategyName); 44 | } 45 | 46 | public AlgoTemplate(Map settings) { 47 | setAlgoName(this.getClass().getSimpleName()); 48 | this.settings = settings; 49 | } 50 | 51 | public void init() { 52 | if (settings == null) { 53 | settings = algoEngine.getSettings(this.getClass().getSimpleName()); 54 | } 55 | Object mode = settings.get("mode"); 56 | if (mode != null) { 57 | this.mode = (BacktestingMode) mode; 58 | } 59 | } 60 | 61 | public void onBar(Bar bar) { 62 | } 63 | 64 | public void updateTick(Tick tick) { 65 | if (active) { 66 | Tick history = ticks.get(tick.getSymbol()); 67 | if (history != null) { 68 | history.updateTick(tick); 69 | onTick(history); 70 | } else { 71 | ticks.put(tick.getSymbol(), tick); 72 | onTick(tick); 73 | } 74 | } 75 | } 76 | 77 | public void updateOrder(Order order) { 78 | if (active) { 79 | if (order.isActive()) { 80 | activeOrders.put(order.getId(), order); 81 | } 82 | onOrder(order); 83 | } 84 | } 85 | 86 | public void updateTrade(Trade trade) { 87 | if (active) { 88 | onTrade(trade); 89 | } 90 | } 91 | 92 | public void updateTimer() { 93 | if (active) { 94 | onTimer(); 95 | } 96 | } 97 | 98 | public void onStart() { 99 | } 100 | 101 | public void onStop() { 102 | 103 | } 104 | 105 | public void onTimer() { 106 | 107 | } 108 | 109 | public abstract void onTick(Tick tick); 110 | 111 | public abstract void onOrder(Order order); 112 | 113 | public abstract void onTrade(Trade trade); 114 | 115 | public void start() { 116 | active = true; 117 | onStart(); 118 | } 119 | 120 | public void stop() { 121 | active = false; 122 | cancelAll(); 123 | onStop(); 124 | log("algo stop"); 125 | } 126 | 127 | public void subscribe(String symbol) { 128 | if (mode == null) { 129 | algoEngine.subscribe(getAlgoName(), symbol); 130 | } 131 | } 132 | 133 | public void cancelSubscribe(String symbol) { 134 | algoEngine.cancelSubscribe(getAlgoName(), symbol); 135 | } 136 | 137 | public void sendOrder(String symbol, Direction direction, double price, int volume, boolean stop) { 138 | 139 | } 140 | 141 | public long buy(String symbol, Double price, int volume, OrderType orderType) { 142 | log("buy {}|{}|{}", symbol, price, volume); 143 | return algoEngine.sendOrder(getAlgoName(), symbol, Direction.BUY, price, volume, orderType); 144 | } 145 | 146 | public long sell(String symbol, Double price, int volume, OrderType orderType) { 147 | log("sell {}|{}|{}", symbol, price, volume); 148 | return algoEngine.sendOrder(getAlgoName(), symbol, Direction.SELL, price, volume, orderType); 149 | } 150 | 151 | public void cancelOrder(Long orderId) { 152 | algoEngine.cancelOrder(orderId); 153 | } 154 | 155 | public void cancelAll() { 156 | if (activeOrders != null) { 157 | for (Long orderId : activeOrders.keySet()) { 158 | cancelOrder(orderId); 159 | } 160 | } 161 | } 162 | 163 | public List getAllActiveOrders() { 164 | return algoEngine.getAllActiveOrders(); 165 | } 166 | 167 | public Tick getTick(String symbol) { 168 | return algoEngine.getTick(symbol); 169 | } 170 | 171 | public Contract getContract(String symbol) { 172 | return algoEngine.getContract(symbol); 173 | } 174 | 175 | public Asset getAsset() { 176 | return algoEngine.getAsset(); 177 | } 178 | 179 | public Position getPosition(String positionId) { 180 | return algoEngine.getPosition(positionId); 181 | } 182 | 183 | public List getAllPositions() { 184 | return algoEngine.getAllPositions(); 185 | } 186 | 187 | public List getBars(String symbol, BarType barType, int limit) { 188 | return algoEngine.getBars(symbol, barType, limit); 189 | } 190 | 191 | public Map> getBars(List symbols, BarType barType, int limit) { 192 | return algoEngine.getBars(symbols, barType, limit); 193 | } 194 | 195 | public void log(String format, Object... args) { 196 | algoEngine.log(format, args); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/core/Engine.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.core; 2 | 3 | import com.tquant.core.event.EventEngine; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Description: 9 | * 10 | * @author kevin 11 | * @date 2019/08/16 12 | */ 13 | @NoArgsConstructor 14 | @Data 15 | public abstract class Engine { 16 | 17 | protected String engineName; 18 | protected MainEngine mainEngine; 19 | protected EventEngine eventEngine; 20 | 21 | public Engine(String engineName, MainEngine mainEngine, EventEngine eventEngine) { 22 | this.engineName = engineName; 23 | this.mainEngine = mainEngine; 24 | this.eventEngine = eventEngine; 25 | } 26 | 27 | public void start() { 28 | 29 | } 30 | 31 | public void stop() { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/core/Gateway.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.core; 2 | 3 | import com.tquant.core.event.Event; 4 | import com.tquant.core.event.EventEngine; 5 | import com.tquant.core.event.EventType; 6 | import com.tquant.core.model.data.Account; 7 | import com.tquant.core.model.data.Asset; 8 | import com.tquant.core.model.data.Bar; 9 | import com.tquant.core.model.data.Contract; 10 | import com.tquant.core.model.data.Order; 11 | import com.tquant.core.model.data.Position; 12 | import com.tquant.core.model.data.Tick; 13 | import com.tquant.core.model.data.Trade; 14 | import com.tquant.core.model.enums.BarType; 15 | import com.tquant.core.model.request.ModifyRequest; 16 | import com.tquant.core.model.request.OrderRequest; 17 | import com.tquant.core.model.request.SubscribeRequest; 18 | import java.util.List; 19 | import java.util.Map; 20 | import lombok.Data; 21 | import org.slf4j.helpers.FormattingTuple; 22 | import org.slf4j.helpers.MessageFormatter; 23 | 24 | /** 25 | * Description: 26 | * 27 | * @author kevin 28 | * @date 2019/08/15 29 | */ 30 | @Data 31 | public abstract class Gateway { 32 | 33 | private EventEngine eventEngine; 34 | private String gatewayName; 35 | 36 | public Gateway(EventEngine eventEngine, String gatewayName) { 37 | this.eventEngine = eventEngine; 38 | this.gatewayName = gatewayName; 39 | } 40 | 41 | public void onEvent(EventType eventType, Object data) { 42 | eventEngine.put(new Event(eventType, data)); 43 | } 44 | 45 | public void onTick(Tick tick) { 46 | onEvent(EventType.EVENT_TICK, tick); 47 | } 48 | 49 | public void onTrade(Trade trade) { 50 | onEvent(EventType.EVENT_TRADE, trade); 51 | } 52 | 53 | public void onOrder(Order order) { 54 | onEvent(EventType.EVENT_ORDER, order); 55 | } 56 | 57 | public void onPosition(Position position) { 58 | onEvent(EventType.EVENT_POSITION, position); 59 | } 60 | 61 | public void onAccount(Account account) { 62 | onEvent(EventType.EVENT_ACCOUNT, account); 63 | } 64 | 65 | public void onContract(Contract contract) { 66 | onEvent(EventType.EVENT_CONTRACT, contract); 67 | } 68 | 69 | public void onAsset(Asset asset) { 70 | onEvent(EventType.EVENT_ASSET, asset); 71 | } 72 | 73 | public void log(String format, Object... args) { 74 | if (format == null || format.isEmpty()) { 75 | return; 76 | } 77 | FormattingTuple formatting = MessageFormatter.arrayFormat(format, args); 78 | onEvent(EventType.EVENT_LOG, formatting.getMessage()); 79 | } 80 | 81 | public void stop() { 82 | 83 | } 84 | 85 | public abstract void connect(); 86 | 87 | public abstract void subscribe(SubscribeRequest request); 88 | 89 | public abstract void cancelSubscribe(SubscribeRequest request); 90 | 91 | public abstract long sendOrder(OrderRequest request); 92 | 93 | public abstract void cancelOrder(ModifyRequest request); 94 | 95 | public abstract void modifyOrder(ModifyRequest request); 96 | 97 | public abstract Map> getBars(List symbols, BarType barType, int limit); 98 | } 99 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/core/LogEngine.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.core; 2 | 3 | import ch.qos.logback.classic.Level; 4 | import ch.qos.logback.classic.Logger; 5 | import ch.qos.logback.classic.LoggerContext; 6 | import ch.qos.logback.classic.encoder.PatternLayoutEncoder; 7 | import ch.qos.logback.core.ConsoleAppender; 8 | import ch.qos.logback.core.FileAppender; 9 | import com.tquant.core.TigerQuantException; 10 | import com.tquant.core.config.*; 11 | import com.tquant.core.event.EventEngine; 12 | import com.tquant.core.event.EventHandler; 13 | import com.tquant.core.event.EventType; 14 | import java.io.File; 15 | import java.io.IOException; 16 | import java.nio.charset.Charset; 17 | import java.time.LocalDateTime; 18 | import java.time.format.DateTimeFormatter; 19 | import java.util.Map; 20 | import org.slf4j.LoggerFactory; 21 | 22 | /** 23 | * Description: 24 | * 25 | * @author kevin 26 | * @date 2019/08/16 27 | */ 28 | public class LogEngine extends Engine { 29 | 30 | private Logger logger; 31 | private Level logLevel = Level.INFO; 32 | private EventHandler logHandler; 33 | 34 | public LogEngine(MainEngine mainEngine, EventEngine eventEngine) { 35 | this("LogEngine", mainEngine, eventEngine); 36 | } 37 | 38 | public LogEngine(String engineName, MainEngine mainEngine, EventEngine eventEngine) { 39 | super(engineName, mainEngine, eventEngine); 40 | 41 | Map settings = ConfigLoader.GLOBAL_SETTINGS; 42 | if (!(boolean) settings.get("log.enable")) { 43 | return; 44 | } 45 | setLogLevel((String) settings.get("log.level")); 46 | 47 | if ((boolean) settings.get("log.console")) { 48 | addConsoleHandler(); 49 | } 50 | if ((boolean) settings.get("log.file")) { 51 | addFileHandler((String) settings.get("log.path")); 52 | } 53 | registerEvent(); 54 | } 55 | 56 | private void setLogLevel(String level) { 57 | if ("info".equalsIgnoreCase(level)) { 58 | this.logLevel = Level.INFO; 59 | } else if ("debug".equalsIgnoreCase(level)) { 60 | this.logLevel = Level.DEBUG; 61 | } else if ("warn".equalsIgnoreCase(level)) { 62 | this.logLevel = Level.WARN; 63 | } else if ("error".equalsIgnoreCase(level)) { 64 | this.logLevel = Level.ERROR; 65 | } else { 66 | this.logLevel = Level.INFO; 67 | } 68 | } 69 | 70 | public void addConsoleHandler() { 71 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 72 | 73 | PatternLayoutEncoder encoder = new PatternLayoutEncoder(); 74 | encoder.setContext(loggerContext); 75 | encoder.setCharset(Charset.forName("UTF-8")); 76 | encoder.setPattern("%d %level - %msg%n"); 77 | encoder.start(); 78 | 79 | ConsoleAppender appender = new ConsoleAppender(); 80 | appender.setEncoder(encoder); 81 | appender.setContext(loggerContext); 82 | appender.setName("TigerQuant"); 83 | appender.start(); 84 | 85 | logger = loggerContext.getLogger("LogEngine"); 86 | logger.addAppender(appender); 87 | } 88 | 89 | public Logger getLogger() { 90 | return this.logger; 91 | } 92 | 93 | public void addFileHandler(String logPathName) { 94 | String filename = "tiger_quant_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".log"; 95 | if (logPathName == null) { 96 | logPathName = "log/"; 97 | } 98 | if (!logPathName.endsWith("/")) { 99 | logPathName += "/"; 100 | } 101 | 102 | File logPath = new File(logPathName); 103 | if (!logPath.exists()) { 104 | logPath.mkdir(); 105 | } 106 | String filepath = logPathName + filename; 107 | File logFile = new File(filepath); 108 | if (!logPath.exists()) { 109 | try { 110 | logFile.createNewFile(); 111 | } catch (IOException e) { 112 | throw new TigerQuantException("create log error!"); 113 | } 114 | } 115 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 116 | 117 | PatternLayoutEncoder encoder = new PatternLayoutEncoder(); 118 | encoder.setContext(loggerContext); 119 | encoder.setCharset(Charset.forName("UTF-8")); 120 | encoder.setPattern("%d %level - %msg%n"); 121 | encoder.start(); 122 | 123 | FileAppender fileAppender = new FileAppender(); 124 | fileAppender.setName("TigerQuant"); 125 | fileAppender.setContext(loggerContext); 126 | fileAppender.setFile(filepath); 127 | fileAppender.setEncoder(encoder); 128 | fileAppender.start(); 129 | 130 | logger = loggerContext.getLogger("LogEngine"); 131 | logger.addAppender(fileAppender); 132 | 133 | if (this.logLevel == Level.DEBUG) { 134 | this.logHandler = event -> { 135 | if (logger != null) { 136 | this.logger.debug("{}", event.getEventData()); 137 | } 138 | }; 139 | } else if (this.logLevel == Level.INFO) { 140 | this.logHandler = event -> { 141 | if (logger != null) { 142 | this.logger.info("{}", event.getEventData()); 143 | } 144 | }; 145 | } else if (this.logLevel == Level.WARN) { 146 | this.logHandler = event -> { 147 | if (logger != null) { 148 | this.logger.warn("{}", event.getEventData()); 149 | } 150 | }; 151 | } else if (this.logLevel == Level.ERROR) { 152 | this.logHandler = event -> { 153 | if (logger != null) { 154 | this.logger.error("{}", event.getEventData()); 155 | } 156 | }; 157 | } 158 | } 159 | 160 | public void registerEvent() { 161 | eventEngine.register(EventType.EVENT_LOG, logHandler); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/core/MainEngine.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.core; 2 | 3 | import com.tquant.core.TigerQuantException; 4 | import com.tquant.core.core.AlgoEngine; 5 | import com.tquant.core.event.Event; 6 | import com.tquant.core.event.EventEngine; 7 | import com.tquant.core.event.EventType; 8 | import com.tquant.core.core.Gateway; 9 | import com.tquant.core.model.data.Account; 10 | import com.tquant.core.model.data.Asset; 11 | import com.tquant.core.model.data.Bar; 12 | import com.tquant.core.model.data.Contract; 13 | import com.tquant.core.model.data.Order; 14 | import com.tquant.core.model.data.Position; 15 | import com.tquant.core.model.data.Tick; 16 | import com.tquant.core.model.data.Trade; 17 | import com.tquant.core.model.enums.BarType; 18 | import com.tquant.core.model.request.ModifyRequest; 19 | import com.tquant.core.model.request.OrderRequest; 20 | import com.tquant.core.model.request.SubscribeRequest; 21 | import java.time.LocalDate; 22 | import java.time.format.DateTimeFormatter; 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | import org.slf4j.Logger; 28 | import org.slf4j.helpers.FormattingTuple; 29 | import org.slf4j.helpers.MessageFormatter; 30 | 31 | /** 32 | * Description: 33 | * 34 | * @author kevin 35 | * @date 2019/08/16 36 | */ 37 | public class MainEngine { 38 | 39 | private String todayDate; 40 | private Map gateways = new HashMap<>(); 41 | private Map engines = new HashMap<>(); 42 | private EventEngine eventEngine; 43 | private LogEngine logEngine; 44 | 45 | public MainEngine(EventEngine eventEngine) { 46 | this.todayDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); 47 | this.eventEngine = eventEngine; 48 | log("MainEngine init at:" + todayDate); 49 | this.eventEngine.start(); 50 | initEngines(); 51 | } 52 | 53 | private void initEngines() { 54 | logEngine = new LogEngine(this, eventEngine); 55 | addEngine(logEngine); 56 | addEngine(new OrderEngine(this, eventEngine)); 57 | addEngine(new AlgoEngine(this, eventEngine)); 58 | } 59 | 60 | public Logger getLogger() { 61 | return logEngine.getLogger(); 62 | } 63 | 64 | public Engine addEngine(Engine engine) { 65 | return this.engines.putIfAbsent(engine.getEngineName(), engine); 66 | } 67 | 68 | public Engine getEngine(String engineName) { 69 | Engine engine = engines.get(engineName); 70 | if (engine == null) { 71 | log("cannot find engine:" + engineName); 72 | } 73 | return engine; 74 | } 75 | 76 | public List getAllEngines() { 77 | return new ArrayList<>(engines.values()); 78 | } 79 | 80 | public Gateway addGateway(Gateway gateway) { 81 | return this.gateways.putIfAbsent(gateway.getGatewayName(), gateway); 82 | } 83 | 84 | public Gateway getGateway(String gatewayName) { 85 | Gateway gateway = gateways.get(gatewayName); 86 | if (gateway == null) { 87 | log("cannot find gateway:" + gatewayName); 88 | } 89 | return gateway; 90 | } 91 | 92 | public List getAllGatewayNames() { 93 | return new ArrayList<>(gateways.keySet()); 94 | } 95 | 96 | public void subscribe(String gatewayName, SubscribeRequest request) { 97 | Gateway gateway = getGateway(gatewayName); 98 | if (gateway == null) { 99 | throw new TigerQuantException("gateway " + gatewayName + " not found"); 100 | } 101 | gateway.subscribe(request); 102 | } 103 | 104 | public void cancelSubscribe(String gatewayName, SubscribeRequest request) { 105 | Gateway gateway = getGateway(gatewayName); 106 | if (gateway == null) { 107 | throw new TigerQuantException("gateway " + gatewayName + " not found"); 108 | } 109 | gateway.cancelSubscribe(request); 110 | } 111 | 112 | public long sendOrder(String gatewayName, OrderRequest request) { 113 | Gateway gateway = getGateway(gatewayName); 114 | if (gateway == null) { 115 | throw new TigerQuantException("gateway " + gatewayName + " not found"); 116 | } 117 | return gateway.sendOrder(request); 118 | } 119 | 120 | public void cancelOrder(String gatewayName, ModifyRequest request) { 121 | Gateway gateway = getGateway(gatewayName); 122 | if (gateway != null) { 123 | gateway.cancelOrder(request); 124 | } 125 | } 126 | 127 | public Map> getBars(String gatewayName, List symbols, BarType barType, int limit) { 128 | Gateway gateway = getGateway(gatewayName); 129 | if (gateway == null) { 130 | throw new TigerQuantException("gateway " + gatewayName + " not found"); 131 | } 132 | return gateway.getBars(symbols, barType, limit); 133 | } 134 | 135 | public OrderEngine getOrderEngine() { 136 | OrderEngine engine = (OrderEngine) getEngine(OrderEngine.ENGINE_NAME); 137 | if (engine == null) { 138 | throw new TigerQuantException("order engine is null"); 139 | } 140 | return engine; 141 | } 142 | 143 | public Tick getTick(String symbol) { 144 | return getOrderEngine().getTick(symbol); 145 | } 146 | 147 | public Order getOrder(Long orderId) { 148 | return getOrderEngine().getOrder(orderId); 149 | } 150 | 151 | public Trade getTrade(Long orderId) { 152 | return getOrderEngine().getTrade(orderId); 153 | } 154 | 155 | public Position getPosition(String positionId) { 156 | return getOrderEngine().getPosition(positionId); 157 | } 158 | 159 | public Account getAccount(String account) { 160 | return getOrderEngine().getAccount(account); 161 | } 162 | 163 | public Asset getAsset() { 164 | return getOrderEngine().getAsset(); 165 | } 166 | 167 | public Contract getContract(String identifier) { 168 | return getOrderEngine().getContract(identifier); 169 | } 170 | 171 | public List getAllTicks() { 172 | return getOrderEngine().getAllTicks(); 173 | } 174 | 175 | public List getAllOrders() { 176 | return getOrderEngine().getAllOrders(); 177 | } 178 | 179 | public List getAllTrades() { 180 | return getOrderEngine().getAllTrades(); 181 | } 182 | 183 | public List getAllPositions() { 184 | return getOrderEngine().getAllPositions(); 185 | } 186 | 187 | public List getAllAccounts() { 188 | return getOrderEngine().getAllAccounts(); 189 | } 190 | 191 | public List getAllContracts() { 192 | return getOrderEngine().getAllContracts(); 193 | } 194 | 195 | public List getAllActiveOrders(String symbol) { 196 | return getOrderEngine().getAllActiveOrders(symbol); 197 | } 198 | 199 | public void start() { 200 | for (String gatewayName : getAllGatewayNames()) { 201 | Gateway gateway = getGateway(gatewayName); 202 | if (gateway != null) { 203 | gateway.connect(); 204 | } 205 | } 206 | for (Engine engine : getAllEngines()) { 207 | engine.start(); 208 | } 209 | } 210 | 211 | public void stop() { 212 | eventEngine.stop(); 213 | for (Engine engine : engines.values()) { 214 | engine.stop(); 215 | } 216 | 217 | for (Gateway gateway : gateways.values()) { 218 | gateway.stop(); 219 | } 220 | log("main engine stopped."); 221 | } 222 | 223 | public void log(String format, Object... args) { 224 | if (format == null || format.isEmpty()) { 225 | return; 226 | } 227 | FormattingTuple formatting = MessageFormatter.arrayFormat(format, args); 228 | eventEngine.put(new Event(EventType.EVENT_LOG, formatting.getMessage())); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/core/OrderEngine.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.core; 2 | 3 | import com.tquant.core.TigerQuantException; 4 | import com.tquant.core.event.EventEngine; 5 | import com.tquant.core.event.EventHandler; 6 | import com.tquant.core.event.EventType; 7 | import com.tquant.core.model.data.Account; 8 | import com.tquant.core.model.data.Asset; 9 | import com.tquant.core.model.data.Contract; 10 | import com.tquant.core.model.data.Order; 11 | import com.tquant.core.model.data.Position; 12 | import com.tquant.core.model.data.Tick; 13 | import com.tquant.core.model.data.Trade; 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * Description: 21 | * 22 | * @author kevin 23 | * @date 2019/08/16 24 | */ 25 | public class OrderEngine extends Engine { 26 | 27 | public static final String ENGINE_NAME = "OrderEngine"; 28 | 29 | private Map ticks = new HashMap<>(); 30 | private Map orders = new HashMap<>(); 31 | private Map activeOrders = new HashMap<>(); 32 | private Map trades = new HashMap<>(); 33 | private Map positions = new HashMap<>(); 34 | private Asset asset = null; 35 | private Map contracts = new HashMap<>(); 36 | 37 | private EventHandler tickHandler = event -> { 38 | Tick tick = (Tick) event.getEventData(); 39 | ticks.put(tick.getSymbol(), tick); 40 | }; 41 | private EventHandler orderHandler = event -> { 42 | Order order = (Order) event.getEventData(); 43 | orders.put(order.getId(), order); 44 | if (order.isActive()) { 45 | activeOrders.put(order.getId(), order); 46 | } else { 47 | if (activeOrders.containsKey(order.getId())) { 48 | activeOrders.remove(order.getId()); 49 | } 50 | } 51 | }; 52 | private EventHandler tradeHandler = event -> { 53 | Trade trade = (Trade) event.getEventData(); 54 | trades.put(trade.getOrderId(), trade); 55 | }; 56 | private EventHandler positionHandler = event -> { 57 | Position position = (Position) event.getEventData(); 58 | positions.put(position.getIdentifier(), position); 59 | positions.put(position.getSymbol(), position); 60 | }; 61 | private EventHandler assetHandler = event -> { 62 | Asset asset = (Asset) event.getEventData(); 63 | this.asset = asset; 64 | }; 65 | private EventHandler contractHandler = event -> { 66 | Contract contract = (Contract) event.getEventData(); 67 | contracts.put(contract.getIdentifier(), contract); 68 | }; 69 | 70 | public OrderEngine(MainEngine mainEngine, EventEngine eventEngine) { 71 | this(ENGINE_NAME, mainEngine, eventEngine); 72 | } 73 | 74 | public OrderEngine(String engineName, MainEngine mainEngine, EventEngine eventEngine) { 75 | super(engineName, mainEngine, eventEngine); 76 | registerEvent(); 77 | } 78 | 79 | private void registerEvent() { 80 | eventEngine.register(EventType.EVENT_TICK, tickHandler); 81 | eventEngine.register(EventType.EVENT_ORDER, orderHandler); 82 | eventEngine.register(EventType.EVENT_TRADE, tradeHandler); 83 | eventEngine.register(EventType.EVENT_POSITION, positionHandler); 84 | eventEngine.register(EventType.EVENT_ASSET, assetHandler); 85 | eventEngine.register(EventType.EVENT_CONTRACT, contractHandler); 86 | } 87 | 88 | public Tick getTick(String symbol) { 89 | return ticks.get(symbol); 90 | } 91 | 92 | public Order getOrder(Long orderId) { 93 | return orders.get(orderId); 94 | } 95 | 96 | public Trade getTrade(Long orderId) { 97 | return trades.get(orderId); 98 | } 99 | 100 | public Position getPosition(String positionId) { 101 | return positions.get(positionId); 102 | } 103 | 104 | public Account getAccount(String account) { 105 | return null; 106 | } 107 | 108 | public Asset getAsset() { 109 | if (asset == null) { 110 | throw new TigerQuantException("get asset is null"); 111 | } 112 | return asset; 113 | } 114 | 115 | public Contract getContract(String identifier) { 116 | return contracts.get(identifier); 117 | } 118 | 119 | public List getAllTicks() { 120 | return new ArrayList<>(ticks.values()); 121 | } 122 | 123 | public List getAllOrders() { 124 | return new ArrayList<>(orders.values()); 125 | } 126 | 127 | public List getAllTrades() { 128 | return new ArrayList<>(trades.values()); 129 | } 130 | 131 | public List getAllPositions() { 132 | return new ArrayList<>(positions.values()); 133 | } 134 | 135 | public List getAllAccounts() { 136 | return null; 137 | } 138 | 139 | public List getAllContracts() { 140 | return new ArrayList<>(contracts.values()); 141 | } 142 | 143 | public List getAllActiveOrders(String symbol) { 144 | if (symbol == null) { 145 | return new ArrayList<>(activeOrders.values()); 146 | } 147 | List activeOrderList = new ArrayList<>(); 148 | for (Order order : activeOrders.values()) { 149 | if (symbol.equals(order.getSymbol())) { 150 | activeOrderList.add(order); 151 | } 152 | } 153 | return activeOrderList; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/event/Event.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.event; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Description: Event 7 | * 8 | * @author kevin 9 | * @date 2019/08/15 10 | */ 11 | @Data 12 | public class Event { 13 | 14 | /** 15 | * event type 16 | */ 17 | private EventType eventType; 18 | /** 19 | * event data 20 | */ 21 | private Object eventData; 22 | 23 | public Event(EventType eventType) { 24 | this.eventType = eventType; 25 | } 26 | 27 | public Event(EventType eventType, Object eventData) { 28 | this.eventType = eventType; 29 | this.eventData = eventData; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/event/EventEngine.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.event; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.CopyOnWriteArrayList; 7 | import java.util.concurrent.LinkedBlockingQueue; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * Description: 12 | * 13 | * @author kevin 14 | * @date 2019/08/15 15 | */ 16 | public class EventEngine { 17 | 18 | /** 19 | * event queue 20 | */ 21 | private LinkedBlockingQueue queue; 22 | /** 23 | * event engine switch 24 | */ 25 | private boolean active; 26 | 27 | /** 28 | * event process thread 29 | */ 30 | private Thread thread; 31 | /** 32 | * trigger timer event thread 33 | */ 34 | private Thread timer; 35 | /** 36 | * timer working state 37 | */ 38 | private boolean timerActive; 39 | /** 40 | * timer trigger duration,default 1 second 41 | */ 42 | private int timerDuration; 43 | 44 | /** 45 | * event handlers 46 | */ 47 | private Map> handlers; 48 | 49 | /** 50 | * common handlers to process all kinds of event 51 | */ 52 | private List commonHandlers; 53 | 54 | public EventEngine() { 55 | this.queue = new LinkedBlockingQueue<>(8192); 56 | this.active = false; 57 | this.thread = new Thread(() -> run()); 58 | this.timer = new Thread(() -> runTimer()); 59 | this.timerActive = false; 60 | this.timerDuration = 1000; 61 | this.handlers = new ConcurrentHashMap<>(); 62 | this.commonHandlers = new CopyOnWriteArrayList<>(); 63 | } 64 | 65 | private synchronized void run() { 66 | while (active) { 67 | try { 68 | Event event = queue.poll(1000, TimeUnit.MILLISECONDS); 69 | if (event == null) { 70 | continue; 71 | } 72 | process(event); 73 | } catch (Exception e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | } 78 | 79 | private void process(Event event) { 80 | if (handlers.containsKey(event.getEventType())) { 81 | for (EventHandler eventHandler : handlers.get(event.getEventType())) { 82 | eventHandler.process(event); 83 | } 84 | } 85 | for (EventHandler eventHandler : commonHandlers) { 86 | eventHandler.process(event); 87 | } 88 | } 89 | 90 | private void runTimer() { 91 | while (timerActive) { 92 | Event event = new Event(EventType.EVENT_TIMER); 93 | try { 94 | queue.offer(event); 95 | Thread.sleep(timerDuration); 96 | } catch (InterruptedException e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | } 101 | 102 | public void register(EventType eventType, EventHandler handler) { 103 | List handlerList = this.handlers.getOrDefault(eventType, new CopyOnWriteArrayList<>()); 104 | if (!handlerList.contains(handler)) { 105 | handlerList.add(handler); 106 | } 107 | this.handlers.putIfAbsent(eventType, handlerList); 108 | } 109 | 110 | public void unregister(EventType eventType, EventHandler handler) { 111 | List handlerList = this.handlers.get(eventType); 112 | if (handlerList != null) { 113 | if (handlerList.contains(handler)) { 114 | handlerList.remove(handler); 115 | } 116 | } 117 | if (handlerList == null || handlerList.size() == 0) { 118 | this.handlers.remove(eventType); 119 | } 120 | } 121 | 122 | public void registerCommonHandler(EventHandler handler) { 123 | if (this.commonHandlers != null) { 124 | if (!this.commonHandlers.contains(handler)) { 125 | this.commonHandlers.add(handler); 126 | } 127 | } 128 | } 129 | 130 | public void unregisterCommonHandler(EventHandler handler) { 131 | if (this.commonHandlers != null) { 132 | if (this.commonHandlers.contains(handler)) { 133 | this.commonHandlers.remove(handler); 134 | } 135 | } 136 | } 137 | 138 | public void put(Event event) { 139 | this.queue.offer(event); 140 | } 141 | 142 | /** 143 | * start event engine 144 | */ 145 | public void start() { 146 | start(false); 147 | } 148 | 149 | /** 150 | * start engine 151 | * 152 | * @param timer :true start,false not start 153 | */ 154 | public void start(boolean timer) { 155 | this.active = true; 156 | this.thread.start(); 157 | if (timer) { 158 | this.timerActive = true; 159 | this.timer.start(); 160 | } 161 | } 162 | 163 | /** 164 | * stop event engine 165 | */ 166 | public void stop() { 167 | this.active = false; 168 | this.timerActive = false; 169 | try { 170 | this.timer.join(); 171 | } catch (InterruptedException e) { 172 | e.printStackTrace(); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/event/EventHandler.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.event; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2019/08/15 8 | */ 9 | public interface EventHandler { 10 | 11 | void process(Event event); 12 | } 13 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/event/EventType.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.event; 2 | 3 | /** 4 | * Description:Event Type 5 | * 6 | * @author kevin 7 | * @date 2019/08/15 8 | */ 9 | public enum EventType { 10 | /** 11 | * Timer event, sent every 1 second 12 | */ 13 | EVENT_TIMER, 14 | /** 15 | * Log event 16 | */ 17 | EVENT_LOG, 18 | 19 | /** 20 | * TICK market return 21 | */ 22 | EVENT_TICK, 23 | 24 | /** 25 | * Bar event 26 | */ 27 | EVENT_BAR, 28 | 29 | /** 30 | * Trade event 31 | */ 32 | EVENT_TRADE, 33 | /** 34 | * Order return 35 | */ 36 | EVENT_ORDER, 37 | /** 38 | * Assets return 39 | */ 40 | EVENT_ASSET, 41 | /** 42 | * Position return 43 | */ 44 | EVENT_POSITION, 45 | 46 | /** 47 | * Contract return 48 | */ 49 | EVENT_CONTRACT, 50 | 51 | /** 52 | * Account return 53 | */ 54 | EVENT_ACCOUNT, 55 | 56 | /** 57 | * Algo event 58 | */ 59 | EVENT_ALGO, 60 | /** 61 | * Error return 62 | */ 63 | EVENT_ERROR, 64 | 65 | } 66 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/Config.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model; 2 | 3 | /** 4 | * Description: config interface 5 | * 6 | * @author kevin 7 | * @date 2019/08/15 8 | */ 9 | public interface Config { 10 | } 11 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Account.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2019/08/16 8 | */ 9 | public class Account { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Asset.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Description: 7 | * 8 | * @author kevin 9 | * @date 2019/08/15 10 | */ 11 | @Data 12 | public class Asset { 13 | 14 | private String account; 15 | private String capability; 16 | private double netLiquidation; 17 | private double availableFunds; 18 | private double initMarginReq; 19 | private double maintenanceMarginReq; 20 | private double buyingPower; 21 | private double grossPositionValue; 22 | private double realizedPnL; 23 | private double unrealizedPnL; 24 | 25 | public void updateAsset(Asset asset) { 26 | if (asset.getNetLiquidation() > 0) { 27 | this.netLiquidation = asset.getNetLiquidation(); 28 | } 29 | if (asset.getAvailableFunds() > 0) { 30 | this.availableFunds = asset.getAvailableFunds(); 31 | } 32 | if (asset.getInitMarginReq() > 0) { 33 | this.initMarginReq = asset.getInitMarginReq(); 34 | } 35 | if (asset.getMaintenanceMarginReq() > 0) { 36 | this.maintenanceMarginReq = asset.getMaintenanceMarginReq(); 37 | } 38 | if (asset.getBuyingPower() > 0) { 39 | this.buyingPower = asset.getBuyingPower(); 40 | } 41 | if (asset.getGrossPositionValue() > 0) { 42 | this.grossPositionValue = asset.getGrossPositionValue(); 43 | } 44 | if (asset.getRealizedPnL() > 0) { 45 | this.realizedPnL = asset.getRealizedPnL(); 46 | } 47 | if (asset.getUnrealizedPnL() > 0) { 48 | this.unrealizedPnL = asset.getUnrealizedPnL(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Bar.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import com.tquant.core.model.enums.BarType; 4 | import java.time.Duration; 5 | import java.time.LocalDateTime; 6 | import java.time.ZoneId; 7 | import org.ta4j.core.BaseBar; 8 | import org.ta4j.core.num.DecimalNum; 9 | 10 | /** 11 | * Description: 12 | * 13 | * @author kevin 14 | * @date 2019/08/16 15 | */ 16 | public class Bar extends StockData implements BaseData { 17 | 18 | private String symbol; 19 | private String period; 20 | private Duration duration; 21 | private double open; 22 | private double close; 23 | private double high; 24 | private double low; 25 | private LocalDateTime time; 26 | private long volume; 27 | private double amount; 28 | 29 | 30 | public BaseBar toBaseBar() { 31 | return BaseBar.builder(DecimalNum::valueOf, Number.class).timePeriod(duration).endTime(time.atZone(ZoneId.systemDefault())) 32 | .openPrice(open).highPrice(high).lowPrice(low).closePrice(close).volume(volume) 33 | .build(); 34 | } 35 | 36 | public static Duration getDurationByKType(BarType barType) { 37 | if (barType == null) { 38 | return Duration.ofDays(1); 39 | } 40 | switch (barType) { 41 | case day: 42 | return Duration.ofDays(1); 43 | case min1: 44 | return Duration.ofMinutes(1); 45 | case min3: 46 | return Duration.ofMinutes(3); 47 | case min10: 48 | return Duration.ofMinutes(10); 49 | case min45: 50 | return Duration.ofMinutes(45); 51 | case hour2: 52 | return Duration.ofHours(2); 53 | case hour3: 54 | return Duration.ofHours(3); 55 | case hour4: 56 | return Duration.ofHours(4); 57 | case hour6: 58 | return Duration.ofHours(6); 59 | case min5: 60 | return Duration.ofMinutes(5); 61 | case min15: 62 | return Duration.ofMinutes(15); 63 | case min30: 64 | return Duration.ofMinutes(30); 65 | case min60: 66 | return Duration.ofMinutes(60); 67 | case week: 68 | return Duration.ofDays(7); 69 | case month: 70 | return Duration.ofDays(30); 71 | case year: 72 | return Duration.ofDays(365); 73 | default: 74 | return Duration.ofDays(1); 75 | } 76 | } 77 | 78 | public String getSymbol() { 79 | return symbol; 80 | } 81 | 82 | public void setSymbol(String symbol) { 83 | this.symbol = symbol; 84 | } 85 | 86 | public String getPeriod() { 87 | return period; 88 | } 89 | 90 | public void setPeriod(String period) { 91 | this.period = period; 92 | } 93 | 94 | public Duration getDuration() { 95 | return duration; 96 | } 97 | 98 | public void setDuration(Duration duration) { 99 | this.duration = duration; 100 | } 101 | 102 | public double getOpen() { 103 | return open; 104 | } 105 | 106 | public void setOpen(double open) { 107 | this.open = open; 108 | attr("open", this.open); 109 | } 110 | 111 | public double getClose() { 112 | return close; 113 | } 114 | 115 | public void setClose(double close) { 116 | this.close = close; 117 | attr("close", this.close); 118 | } 119 | 120 | public double getHigh() { 121 | return high; 122 | } 123 | 124 | public void setHigh(double high) { 125 | this.high = high; 126 | attr("high", this.high); 127 | } 128 | 129 | public double getLow() { 130 | return low; 131 | } 132 | 133 | public void setLow(double low) { 134 | this.low = low; 135 | attr("low", this.low); 136 | } 137 | 138 | public LocalDateTime getTime() { 139 | return time; 140 | } 141 | 142 | public void setTime(LocalDateTime time) { 143 | this.time = time; 144 | } 145 | 146 | public long getVolume() { 147 | return volume; 148 | } 149 | 150 | public void setVolume(long volume) { 151 | this.volume = volume; 152 | } 153 | 154 | public double getAmount() { 155 | return amount; 156 | } 157 | 158 | public void setAmount(double amount) { 159 | this.amount = amount; 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/BaseData.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2022/08/01 8 | */ 9 | public interface BaseData { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Contract.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import com.tquant.core.model.enums.SecType; 4 | import lombok.Data; 5 | 6 | /** 7 | * Description: 8 | * 9 | * @author kevin 10 | * @date 2019/08/15 11 | */ 12 | @Data 13 | public class Contract { 14 | 15 | protected String identifier; 16 | protected String name; 17 | protected String gatewayName; 18 | private String symbol; 19 | private String secType; 20 | private String currency; 21 | private String exchange; 22 | private String market; 23 | private String expiry; 24 | private String contractMonth; 25 | private Double strike; 26 | private Double multiplier; 27 | private String right; 28 | private Double minTick; 29 | private Integer lotSize; 30 | 31 | public boolean isOption() { 32 | return secType != null && secType.equalsIgnoreCase(SecType.OPT.name()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/HourTrading.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import java.io.Serializable; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Description: 10 | * Created by lijiawen on 2018/12/28. 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class HourTrading implements Serializable { 16 | 17 | /** 18 | * 盘前、盘后 19 | */ 20 | private String tag; 21 | 22 | protected Double latestPrice; 23 | 24 | protected Double preClose; 25 | 26 | private String latestTime; 27 | 28 | private Long volume; 29 | 30 | private Long timestamp; 31 | } 32 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Log.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | /** 7 | * Description: 8 | * 9 | * @author kevin 10 | * @date 2019/08/16 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | public class Log { 15 | 16 | enum LogLevel { 17 | /** 18 | * debug level 19 | */ 20 | DEBUG, 21 | /** 22 | * info level 23 | */ 24 | INFO, 25 | /** 26 | * warn level 27 | */ 28 | WARN, 29 | /** 30 | * error level 31 | */ 32 | ERROR 33 | } 34 | 35 | private LogLevel level = LogLevel.INFO; 36 | private String gatewayName; 37 | private String message; 38 | 39 | public Log(String message) { 40 | this.message = message; 41 | } 42 | 43 | public Log(String gatewayName, String message) { 44 | this.gatewayName = gatewayName; 45 | this.message = message; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/MarketStatus.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Description: 9 | * 10 | * @author kevin 11 | * @date 2022/09/02 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class MarketStatus { 17 | /** 18 | * US:美股,CN:沪深,HK:港股 19 | **/ 20 | private String market; 21 | 22 | /** 23 | * 市场状态,包括:NOT_YET_OPEN:未开盘,PRE_HOUR_TRADING:盘前交易,TRADING:交易中,MIDDLE_CLOSE:午间休市,POST_HOUR_TRADING:盘后交易,CLOSING:已收盘,EARLY_CLOSED:提前休市,MARKET_CLOSED:休市 24 | **/ 25 | private String status; 26 | 27 | /** 28 | * 市场状态描述(未开盘,交易中,休市等) 29 | **/ 30 | private String marketStatus; 31 | 32 | /** 33 | * 最近开盘、交易时间 MM-dd HH:mm:ss z 34 | **/ 35 | private String openTime; 36 | } 37 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Order.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.tquant.core.model.request.ModifyRequest; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | import lombok.Data; 8 | 9 | /** 10 | * Description: 11 | * 12 | * @author kevin 13 | * @date 2019/08/15 14 | */ 15 | @Data 16 | public class Order { 17 | 18 | private long id; 19 | private String gatewayName; 20 | private String name; 21 | private String account; 22 | private String symbol; 23 | private String identifier; 24 | private String direction; 25 | private String orderType; 26 | private double price; 27 | private long volume; 28 | private double averageFilledPrice; 29 | private long filledVolume; 30 | private String status; 31 | private String remark; 32 | private long time; 33 | 34 | private static final Set activeStatus = new HashSet<>(); 35 | 36 | { 37 | activeStatus.add("Initial"); 38 | activeStatus.add("PendingSubmit"); 39 | activeStatus.add("Submitted"); 40 | activeStatus.add("PendingCancel"); 41 | } 42 | 43 | public Order() { 44 | } 45 | 46 | public Order(long id, String symbol, double price, long volume, String direction, String orderType) { 47 | this.id = id; 48 | this.symbol = symbol; 49 | this.price = price; 50 | this.volume = volume; 51 | this.direction = direction; 52 | this.orderType = orderType; 53 | } 54 | 55 | public ModifyRequest createCancelRequest() { 56 | ModifyRequest request = new ModifyRequest(); 57 | request.setOrderId(id); 58 | return request; 59 | } 60 | 61 | public boolean isActive() { 62 | return status != null && activeStatus.contains(status); 63 | } 64 | 65 | public void jsonToOrder(JSONObject jsonObject) { 66 | this.gatewayName = "TigerGateway"; 67 | this.id = jsonObject.getLongValue("id"); 68 | this.identifier = jsonObject.getString("identifier"); 69 | this.name = jsonObject.getString("name"); 70 | this.account = jsonObject.getString("account"); 71 | this.symbol = jsonObject.getString("symbol"); 72 | this.direction = jsonObject.getString("action"); 73 | this.orderType = jsonObject.getString("orderType"); 74 | this.price = jsonObject.getDoubleValue("limitPrice"); 75 | this.volume = jsonObject.getLongValue("totalQuantity"); 76 | this.averageFilledPrice = jsonObject.getDoubleValue("avgFillPrice"); 77 | this.filledVolume = jsonObject.getLongValue("filledQuantity"); 78 | this.remark = jsonObject.getString("remark"); 79 | this.status = jsonObject.getString("status"); 80 | this.time = jsonObject.getLongValue("openTime"); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Position.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import com.tquant.core.model.enums.SecType; 4 | import lombok.Data; 5 | 6 | /** 7 | * Description: 8 | * 9 | * @author kevin 10 | * @date 2019/08/15 11 | */ 12 | @Data 13 | public class Position { 14 | 15 | private Contract contract; 16 | private String account; 17 | private String symbol; 18 | private String secType; 19 | private String right; 20 | private String identifier; 21 | private String exchange; 22 | private String direction; 23 | private int position; 24 | private double averageCost; 25 | private double marketPrice; 26 | private double marketValue; 27 | private double realizedPnl; 28 | private double unrealizedPnl; 29 | 30 | public void updatePosition(Position position) { 31 | if (position.getSecType() != null) { 32 | this.secType = position.getSecType(); 33 | } 34 | if (position.getExchange() != null) { 35 | this.exchange = position.getExchange(); 36 | } 37 | if (position.getDirection() != null) { 38 | this.direction = position.getDirection(); 39 | } 40 | if (position.getPosition() > 0) { 41 | this.position = position.getPosition(); 42 | } 43 | if (position.getAverageCost() > 0) { 44 | this.averageCost = position.getAverageCost(); 45 | } 46 | if (position.getMarketPrice() > 0) { 47 | this.marketPrice = position.getMarketPrice(); 48 | } 49 | if (position.getMarketValue() > 0) { 50 | this.marketValue = position.getMarketValue(); 51 | } 52 | if (position.getRealizedPnl() > 0) { 53 | this.realizedPnl = position.getRealizedPnl(); 54 | } 55 | if (position.getUnrealizedPnl() > 0) { 56 | this.unrealizedPnl = position.getUnrealizedPnl(); 57 | } 58 | } 59 | 60 | public boolean isStock() { 61 | return secType != null && secType.equalsIgnoreCase(SecType.STK.name()); 62 | } 63 | 64 | public boolean isOption() { 65 | return secType != null && secType.equalsIgnoreCase(SecType.OPT.name()); 66 | } 67 | 68 | public boolean isOption(String right) { 69 | return secType != null && secType.equalsIgnoreCase(SecType.OPT.name()) && getRight().equalsIgnoreCase(right); 70 | } 71 | 72 | public boolean isOptionCall() { 73 | return isOption("CALL"); 74 | } 75 | 76 | public boolean isOptionPut() { 77 | return isOption("PUT"); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/RealtimeQuote.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import com.tquant.core.model.enums.StockStatus; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * Description: 11 | * 12 | * @author kevin 13 | * @date 2022/09/02 14 | */ 15 | @Data 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @Builder 19 | public class RealtimeQuote { 20 | 21 | /** 22 | * 股票代码 23 | */ 24 | private String symbol; 25 | 26 | /** 27 | * 开盘价 28 | */ 29 | private Double open; 30 | 31 | /** 32 | * 最高价 33 | */ 34 | private Double high; 35 | 36 | /** 37 | * 最低价 38 | */ 39 | private Double low; 40 | 41 | /** 42 | * 收盘价 43 | */ 44 | private Double close; 45 | 46 | /** 47 | * 昨日收盘价 48 | */ 49 | private Double preClose; 50 | 51 | /** 52 | * 最新价 53 | */ 54 | private Double latestPrice; 55 | 56 | /** 57 | * 最新成交时间 58 | */ 59 | private Long latestTime; 60 | 61 | /** 62 | * 卖盘价 63 | */ 64 | private Double askPrice; 65 | 66 | /** 67 | * 卖盘数量 68 | */ 69 | private Long askSize; 70 | 71 | /** 72 | * 买盘价 73 | */ 74 | private Double bidPrice; 75 | 76 | /** 77 | * 买盘数量 78 | */ 79 | private Long bidSize; 80 | 81 | /** 82 | * 当日成交量 83 | */ 84 | private Long volume; 85 | 86 | /** 87 | * 个股状态: 88 | * 0: 正常 3: 停牌 4: 退市 7: 新股 8: 变更 89 | */ 90 | private StockStatus status; 91 | 92 | private HourTrading hourTrading; 93 | 94 | /** 95 | * 未平仓量 96 | */ 97 | private Integer openInterest; 98 | 99 | } 100 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/StockData.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.google.common.collect.Maps; 5 | import java.util.Date; 6 | import java.util.Map; 7 | 8 | /** 9 | * Description: 10 | * 11 | * @author kevin 12 | * @date 2022/08/05 13 | */ 14 | public class StockData implements Comparable { 15 | 16 | /** 17 | * 股票代码 18 | */ 19 | public String symbol; 20 | 21 | /** 22 | * 股票名称 23 | */ 24 | public String name; 25 | 26 | /** 27 | * 数据时间点 28 | */ 29 | public Date date; 30 | 31 | /** 32 | * 属性值 33 | */ 34 | public Map attribute = Maps.newLinkedHashMap(); 35 | 36 | public StockData() { 37 | } 38 | 39 | public StockData(String symbol) { 40 | this.symbol = symbol; 41 | } 42 | 43 | public void attr(String key, Double val) { 44 | attribute.put(key, val); 45 | } 46 | 47 | public Double attr(String key) { 48 | return attribute.get(key); 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "StockData{" + 54 | "symbol='" + symbol + '\'' + 55 | ", name='" + name + '\'' + 56 | ", date=" + date + 57 | ", stockData=" + JSON.toJSONString(this) + 58 | ", stockAttribute= " + JSON.toJSONString(attribute) + 59 | '}'; 60 | } 61 | 62 | @Override 63 | public int compareTo(StockData stockData) { 64 | int compare = this.symbol.compareTo(stockData.symbol); 65 | if (compare != 0) { 66 | return compare; 67 | } 68 | return this.date.compareTo(stockData.date); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/SymbolName.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Description: 9 | * 10 | * @author kevin 11 | * @date 2022/09/02 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class SymbolName { 17 | 18 | private String symbol; 19 | private String name; 20 | } 21 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Tick.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import java.math.BigDecimal; 5 | import java.time.Instant; 6 | import java.time.LocalDateTime; 7 | import java.time.ZoneId; 8 | import java.util.TimeZone; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | /** 13 | * Description: 14 | * 15 | * @author kevin 16 | * @date 2019/08/16 17 | */ 18 | @Data 19 | @NoArgsConstructor 20 | public class Tick extends StockData implements BaseData { 21 | 22 | private Contract contract; 23 | private String identifier; 24 | private String symbol; 25 | private String name; 26 | private String type; 27 | private long volume; 28 | private double amount; 29 | private double latestPrice; 30 | private double latestVolume; 31 | private LocalDateTime latestTime; 32 | private long time; 33 | private Integer openInterest; 34 | 35 | private double open; 36 | private double high; 37 | private double low; 38 | private double close; 39 | private double preClose; 40 | 41 | private double bidPrice; 42 | private int bidSize; 43 | private double askPrice; 44 | private int askSize; 45 | private double midpoint; 46 | 47 | public Tick(RealtimeQuote realtimeQuote,Contract contract) { 48 | this.symbol = realtimeQuote.getSymbol(); 49 | if (this.contract == null) { 50 | this.contract = contract; 51 | } 52 | if (realtimeQuote.getOpen() != null) { 53 | this.open = realtimeQuote.getOpen(); 54 | } 55 | if (realtimeQuote.getHigh() != null) { 56 | this.high = realtimeQuote.getHigh(); 57 | } 58 | if (realtimeQuote.getLow() != null) { 59 | this.low = realtimeQuote.getLow(); 60 | } 61 | if (realtimeQuote.getClose() != null) { 62 | this.close = realtimeQuote.getClose(); 63 | } 64 | if (realtimeQuote.getPreClose() != null) { 65 | this.preClose = realtimeQuote.getPreClose(); 66 | } 67 | if (realtimeQuote.getLatestPrice() != null) { 68 | this.latestPrice = realtimeQuote.getLatestPrice(); 69 | } 70 | if (realtimeQuote.getLatestTime() != null) { 71 | this.latestTime = Instant.ofEpochMilli(realtimeQuote.getLatestTime()).atZone(ZoneId.systemDefault()).toLocalDateTime(); 72 | } 73 | if (realtimeQuote.getAskPrice() != null) { 74 | this.askPrice = realtimeQuote.getAskPrice(); 75 | } 76 | if (realtimeQuote.getAskSize() != null) { 77 | this.askSize = realtimeQuote.getAskSize().intValue(); 78 | } 79 | if (realtimeQuote.getBidPrice() != null) { 80 | this.bidPrice = realtimeQuote.getBidPrice(); 81 | } 82 | if (realtimeQuote.getBidSize() != null) { 83 | this.bidSize = realtimeQuote.getBidSize().intValue(); 84 | } 85 | if (realtimeQuote.getVolume() != null) { 86 | this.volume = realtimeQuote.getVolume(); 87 | } 88 | if (realtimeQuote.getOpenInterest() != null) { 89 | this.openInterest = realtimeQuote.getOpenInterest(); 90 | } 91 | } 92 | 93 | public void updateTick(Tick tick) { 94 | if (tick.getVolume() > 0) { 95 | this.volume = tick.getVolume(); 96 | } 97 | if (tick.getLatestPrice() > 0) { 98 | this.latestPrice = tick.getLatestPrice(); 99 | } 100 | if (tick.getLatestVolume() > 0) { 101 | this.latestVolume = tick.getLatestVolume(); 102 | } 103 | if (tick.getLatestTime() != null) { 104 | this.latestTime = tick.getLatestTime(); 105 | } 106 | if (tick.getOpen() > 0) { 107 | this.open = tick.getOpen(); 108 | } 109 | if (tick.getHigh() > 0) { 110 | this.high = tick.getHigh(); 111 | } 112 | if (tick.getLow() > 0) { 113 | this.low = tick.getLow(); 114 | } 115 | if (tick.getClose() > 0) { 116 | this.close = tick.getClose(); 117 | } 118 | if (tick.getPreClose() > 0) { 119 | this.preClose = tick.getPreClose(); 120 | } 121 | if (tick.getAskPrice() > 0) { 122 | this.askPrice = tick.getAskPrice(); 123 | } 124 | if (this.getAskSize() > 0) { 125 | this.askSize = tick.getAskSize(); 126 | } 127 | if (tick.getBidPrice() > 0) { 128 | this.bidPrice = tick.getBidPrice(); 129 | } 130 | if (tick.getBidSize() > 0) { 131 | this.bidSize = tick.getBidSize(); 132 | } 133 | if (tick.getAmount() > 0) { 134 | this.amount = tick.getAmount(); 135 | } 136 | } 137 | 138 | public void jsonToTick(JSONObject jsonObject) { 139 | this.symbol = jsonObject.getString("symbol"); 140 | this.identifier = jsonObject.getString("symbol"); 141 | this.volume = jsonObject.getLongValue("volume"); 142 | this.latestPrice = jsonObject.getDoubleValue("latestPrice"); 143 | this.amount = jsonObject.getDoubleValue("amount"); 144 | this.latestVolume = jsonObject.getIntValue("latestVolume"); 145 | long timestamp = jsonObject.getLongValue("timestamp"); 146 | if ( timestamp > 0) { 147 | this.latestTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), 148 | TimeZone.getDefault().toZoneId()); 149 | this.time = timestamp; 150 | } else { 151 | this.latestTime = LocalDateTime.now(); 152 | this.time = System.currentTimeMillis(); 153 | } 154 | 155 | this.open = jsonObject.getDoubleValue("open"); 156 | this.high = jsonObject.getDoubleValue("high"); 157 | this.low = jsonObject.getDoubleValue("low"); 158 | this.close = jsonObject.getDoubleValue("close"); 159 | this.preClose = jsonObject.getDoubleValue("preClose"); 160 | 161 | this.askPrice = jsonObject.getDoubleValue("askPrice"); 162 | this.askSize = jsonObject.getIntValue("askSize"); 163 | this.bidPrice = jsonObject.getDoubleValue("bidPrice"); 164 | this.bidSize = jsonObject.getIntValue("bidSize"); 165 | 166 | String hourTradingTag = jsonObject.getString("hourTradingTag"); 167 | if (hourTradingTag != null && hourTradingTag.equals("盘前")) { 168 | Double latestPrice = jsonObject.getDouble("hourTradingLatestPrice"); 169 | if (latestPrice != null) { 170 | this.latestPrice = latestPrice; 171 | } 172 | Double preClose = jsonObject.getDouble("hourTradingPreClose"); 173 | if (preClose != null) { 174 | this.preClose = preClose; 175 | } 176 | Long volume = jsonObject.getLong("hourTradingVolume"); 177 | if (volume != null) { 178 | this.volume = volume; 179 | } 180 | } 181 | } 182 | 183 | public double getMidpoint() { 184 | if (this.askPrice > 0 && this.bidPrice > 0) { 185 | this.midpoint = 186 | new BigDecimal((this.askPrice + this.bidPrice) / 2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); 187 | } 188 | return this.midpoint; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/TimelinePoint.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import java.io.Serializable; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * Description: 11 | * Created by lijiawen on 2018/12/25. 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Builder 17 | public class TimelinePoint implements Serializable { 18 | 19 | private Double price; 20 | 21 | private Double avgPrice; 22 | 23 | private Long time; 24 | 25 | private Long volume; 26 | } 27 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/TimelineQuote.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Description: 10 | * Created by lijiawen on 2018/12/25. 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @Builder 16 | public class TimelineQuote { 17 | 18 | /** 19 | * 股票代号 20 | */ 21 | private String symbol; 22 | 23 | /** 24 | * 分时 day ; 5日分时 5day 25 | */ 26 | private String period; 27 | 28 | /** 29 | * 昨日收盘价 30 | */ 31 | private Double preClose; 32 | 33 | /** 34 | * 分时数据,当天分时数据没有endTime 35 | */ 36 | private TimelineRange intraday; 37 | 38 | /** 39 | * 盘前交易分时数据(仅美股) 40 | */ 41 | private TimelineRange preMarket; 42 | 43 | /** 44 | * 盘后交易分时数据(仅美股) 45 | */ 46 | private TimelineRange afterHours; 47 | } 48 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/TimelineRange.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * Description: 12 | * Created by lijiawen on 2018/12/25. 13 | */ 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Builder 18 | public class TimelineRange implements Serializable { 19 | 20 | /** 21 | * 分时数据数组 22 | */ 23 | private List items; 24 | 25 | private Long beginTime; 26 | 27 | private Long endTime; 28 | } 29 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/Trade.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import java.time.LocalDateTime; 4 | import lombok.Data; 5 | 6 | /** 7 | * Description:Trade detail contains information of a fill of an order. 8 | * One order can have several trade fills. 9 | * 10 | * @author kevin 11 | * @date 2019/08/16 12 | */ 13 | @Data 14 | public class Trade { 15 | 16 | private String id; 17 | private Long orderId; 18 | private String account; 19 | private String symbol; 20 | private String exchange; 21 | private String direction; 22 | private String orderType; 23 | private double price; 24 | private long volume; 25 | private String status; 26 | private LocalDateTime time; 27 | } 28 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/data/TradeCalendar.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.data; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Description: 9 | * 10 | * @author kevin 11 | * @date 2022/09/02 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class TradeCalendar { 17 | /** 18 | * trading day date,yyyy-MM-dd 19 | */ 20 | private String date; 21 | 22 | /** 23 | * trading day type:NORMAL/EARLY_CLOSE 24 | */ 25 | private String type; 26 | } 27 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/enums/BacktestingMode.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.enums; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2022/08/01 8 | */ 9 | public enum BacktestingMode { 10 | BAR, TICK 11 | } 12 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/enums/BarPeriod.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.enums; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2022/08/02 8 | */ 9 | public enum BarPeriod { 10 | day("day"), week("week"), month("month"), year("year"), min1("1min"), min5("5min"), 11 | min15("15min"), min30("30min"), min60("60min"); 12 | 13 | private String value; 14 | 15 | BarPeriod(String value) { 16 | this.value = value; 17 | } 18 | 19 | public String getValue() { 20 | return value; 21 | } 22 | 23 | public void setValue(String value) { 24 | this.value = value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/enums/BarType.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.enums; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * Description: 8 | * 9 | * @author kevin 10 | * @date 2019/09/05 11 | */ 12 | public enum BarType { 13 | min1("1min"), min3("3min"), min5("5min"), min10("10min"), 14 | min15("15min"), min30("30min"), min45("45min"), min60("60min"), 15 | hour2("2hour"), hour3("3hour"), hour4("4hour"), hour6("6hour"), 16 | day("day"), week("week"), month("month"), year("year"); 17 | 18 | private static Set stockBarType = new HashSet<>(); 19 | 20 | static { 21 | stockBarType.add(min1); 22 | stockBarType.add(min5); 23 | stockBarType.add(min15); 24 | stockBarType.add(min30); 25 | stockBarType.add(min60); 26 | stockBarType.add(day); 27 | stockBarType.add(week); 28 | stockBarType.add(month); 29 | stockBarType.add(year); 30 | } 31 | 32 | private String value; 33 | 34 | BarType(String value) { 35 | this.value = value; 36 | } 37 | 38 | public String getValue() { 39 | return value; 40 | } 41 | 42 | public boolean isStockBarType() { 43 | return stockBarType.contains(this); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/enums/Direction.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.enums; 2 | 3 | /** 4 | * Order direction 5 | */ 6 | public enum Direction { 7 | /** 8 | * buy direction 9 | */ 10 | BUY, 11 | /** 12 | * sell direction 13 | */ 14 | SELL 15 | } 16 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/enums/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.enums; 2 | 3 | /** 4 | * Order status 5 | */ 6 | public enum OrderStatus { 7 | Initial, 8 | PendingSubmit, 9 | Inactive, 10 | Cancelled, 11 | Filled, 12 | Invalid 13 | } 14 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/enums/OrderType.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.enums; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2019/09/05 8 | */ 9 | public enum OrderType { 10 | /** 11 | * market order 12 | */ 13 | MKT("MKT", "市价"), 14 | /** 15 | * limit order 16 | */ 17 | LMT("LMT", "限价"); 18 | 19 | private String type; 20 | private String desc; 21 | 22 | OrderType(String type, String desc) { 23 | this.type = type; 24 | this.desc = desc; 25 | } 26 | 27 | public String getType() { 28 | return type; 29 | } 30 | 31 | public String getDesc() { 32 | return desc; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/enums/SecType.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.enums; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2022/08/05 8 | */ 9 | public enum SecType { 10 | STK, OPT, WAR, IOPT, FUT; 11 | } 12 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/enums/StockStatus.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.enums; 2 | 3 | /** 4 | * Description: 5 | * Created by lijiawen on 2018/12/27. 6 | */ 7 | public enum StockStatus { 8 | 9 | UNKNOWN(-1D, "未知"), NORMAL(0D, "正常"), HALTED(3.0D, "停牌"), DELIST(4.0D, "退市"), NEW(7.0D, "新股"), ALTER(8.0D, "变更"); 10 | 11 | private Double value; 12 | 13 | private String desc; 14 | 15 | StockStatus(Double value, String desc) { 16 | this.value = value; 17 | this.desc = desc; 18 | } 19 | 20 | public Double getValue() { 21 | return value; 22 | } 23 | 24 | public String getDesc() { 25 | return desc; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/request/ModifyRequest.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.request; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Description: 7 | * 8 | * @author kevin 9 | * @date 2019/08/16 10 | */ 11 | @Data 12 | public class ModifyRequest { 13 | 14 | private long orderId; 15 | private String symbol; 16 | private Double limitPrice; 17 | private Integer quantity; 18 | private String exchange; 19 | } 20 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/request/OptionChainFilter.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.request; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | /** 7 | * Description: 8 | * 9 | * @author kevin 10 | * @date 2022/08/30 11 | */ 12 | @Data 13 | @Builder 14 | public class OptionChainFilter { 15 | 16 | private Boolean inTheMoney; 17 | private Double minImpliedVolatility; 18 | private Double maxImpliedVolatility; 19 | private Integer minOpenInterest; 20 | private Integer maxOpenInterest; 21 | private Double minDelta; 22 | private Double maxDelta; 23 | private Double minGamma; 24 | private Double maxGamma; 25 | private Double minVega; 26 | private Double maxVega; 27 | private Double minTheta; 28 | private Double maxTheta; 29 | private Double minRho; 30 | private Double maxRho; 31 | } 32 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/request/OrderRequest.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.request; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | /** 7 | * Description: 8 | * 9 | * @author kevin 10 | * @date 2019/08/16 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | public class OrderRequest { 15 | 16 | private String symbol; 17 | private String exchange; 18 | private String direction; 19 | private String orderType; 20 | private int quantity; 21 | private Double price; 22 | } 23 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/model/request/SubscribeRequest.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.model.request; 2 | 3 | import java.util.Set; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | 7 | /** 8 | * Description: 9 | * 10 | * @author kevin 11 | * @date 2019/08/16 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | public class SubscribeRequest { 16 | 17 | private Set symbols; 18 | } 19 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/util/BarGenerator.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.util; 2 | 3 | import com.tquant.core.TigerQuantException; 4 | import com.tquant.core.model.data.Bar; 5 | import com.tquant.core.model.data.Tick; 6 | import java.time.Duration; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Description: 13 | * 1. generating 1 minute bar data from tick data 14 | * 2. generating x minute bar/x hour bar data from 1 minute data 15 | * Notice: 16 | * 1. for x minute bar, x must be able to divide 60: 2, 3, 5, 6, 10, 15, 20, 30 17 | * 2. for x hour bar, x can be any number 18 | * 19 | * @author kevin 20 | * @date 2019/08/16 21 | */ 22 | public class BarGenerator { 23 | 24 | class BarData { 25 | 26 | private Bar bar; 27 | private Bar xminBar; 28 | private int xmin = 0; 29 | private double baseAmount = 0; 30 | private long baseVolume = 0; 31 | } 32 | 33 | private Map symbolBars = new HashMap<>(); 34 | 35 | private BarHandler barHandler; 36 | 37 | public BarGenerator(BarHandler barHandler) { 38 | this.barHandler = barHandler; 39 | } 40 | 41 | public BarGenerator(List symbols, int xmin, BarHandler barHandler) { 42 | for (String symbol : symbols) { 43 | BarData barData = symbolBars.get(symbol); 44 | if (barData == null) { 45 | barData = new BarData(); 46 | symbolBars.put(symbol, barData); 47 | } 48 | barData.xmin = xmin; 49 | } 50 | this.barHandler = barHandler; 51 | } 52 | 53 | public void updateTick(Tick tick) { 54 | boolean newMinute = false; 55 | 56 | if (tick.getLatestPrice() < 0) { 57 | return; 58 | } 59 | 60 | BarData barData = symbolBars.get(tick.getSymbol()); 61 | if (barData == null) { 62 | barData = new BarData(); 63 | symbolBars.put(tick.getSymbol(), barData); 64 | } 65 | Bar bar = barData.bar; 66 | double baseAmount = barData.baseAmount; 67 | long baseVolume = barData.baseVolume; 68 | 69 | if (bar == null) { 70 | newMinute = true; 71 | } else if (bar.getTime() != null && bar.getTime().getMinute() != tick.getLatestTime().getMinute()) { 72 | bar.setTime(bar.getTime().withSecond(0).withNano(0)); 73 | barHandler.onBar(bar); 74 | newMinute = true; 75 | } 76 | if (newMinute) { 77 | bar = new Bar(); 78 | bar.setSymbol(tick.getSymbol()); 79 | bar.setDuration(Duration.ofSeconds(60)); 80 | bar.setOpen(tick.getLatestPrice()); 81 | bar.setClose(tick.getLatestPrice()); 82 | bar.setHigh(tick.getLatestPrice()); 83 | bar.setLow(tick.getLatestPrice()); 84 | if (tick.getAmount() > 0) { 85 | baseAmount = tick.getAmount(); 86 | barData.baseAmount = baseAmount; 87 | } 88 | if (tick.getVolume() > 0) { 89 | baseVolume = tick.getVolume(); 90 | barData.baseVolume = baseVolume; 91 | } 92 | bar.setAmount(0); 93 | bar.setVolume(0); 94 | barData.bar = bar; 95 | } else { 96 | if (tick.getLatestPrice() > 0) { 97 | bar.setHigh(Math.max(bar.getHigh(), tick.getLatestPrice())); 98 | if (bar.getLow() == 0) { 99 | bar.setLow(tick.getLatestPrice()); 100 | } else { 101 | bar.setLow(Math.min(bar.getLow(), tick.getLatestPrice())); 102 | } 103 | bar.setClose(tick.getLatestPrice()); 104 | if (bar.getOpen() == 0) { 105 | bar.setOpen(tick.getLatestPrice()); 106 | } 107 | } 108 | if (tick.getAmount() > 0) { 109 | if (baseAmount == 0) { 110 | baseAmount = tick.getAmount(); 111 | barData.baseAmount = baseAmount; 112 | } else { 113 | bar.setAmount(tick.getAmount() - baseAmount); 114 | } 115 | } 116 | if (tick.getVolume() > 0) { 117 | if (baseVolume == 0) { 118 | baseVolume = tick.getVolume(); 119 | barData.baseVolume = baseVolume; 120 | } else { 121 | bar.setVolume(tick.getVolume() - baseVolume); 122 | } 123 | } 124 | bar.setTime(tick.getLatestTime()); 125 | } 126 | } 127 | 128 | public void updateBar(Bar bar) { 129 | BarData barData = symbolBars.get(bar.getSymbol()); 130 | int xmin = barData.xmin; 131 | Bar xminBar = barData.xminBar; 132 | if (xmin <= 0) { 133 | throw new TigerQuantException("xmin must be greater than zero"); 134 | } 135 | if (xminBar == null) { 136 | xminBar = new Bar(); 137 | xminBar.setSymbol(bar.getSymbol()); 138 | xminBar.setTime(bar.getTime()); 139 | xminBar.setOpen(bar.getOpen()); 140 | xminBar.setHigh(bar.getHigh()); 141 | xminBar.setLow(bar.getLow()); 142 | barData.xminBar = xminBar; 143 | } else { 144 | xminBar.setHigh(Math.max(xminBar.getHigh(), bar.getHigh())); 145 | xminBar.setLow(Math.min(xminBar.getLow(), bar.getLow())); 146 | } 147 | xminBar.setClose(bar.getClose()); 148 | xminBar.setVolume(xminBar.getVolume() + bar.getVolume()); 149 | xminBar.setAmount(xminBar.getAmount() + bar.getAmount()); 150 | if ((bar.getTime().getMinute() + 1) % xmin == 0) { 151 | xminBar.setDuration(Duration.ofSeconds(60 * xmin)); 152 | xminBar.setTime(xminBar.getTime().withSecond(0).withNano(0)); 153 | barHandler.onBar(xminBar); 154 | barData.xminBar = null; 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/util/BarHandler.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.util; 2 | 3 | import com.tquant.core.model.data.Bar; 4 | 5 | /** 6 | * Description: 7 | * 8 | * @author kevin 9 | * @date 2019/08/27 10 | */ 11 | @FunctionalInterface 12 | public interface BarHandler { 13 | 14 | /** 15 | * on bar event 16 | * @param bar 17 | */ 18 | void onBar(Bar bar); 19 | } 20 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/util/QuantConstants.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.util; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2023/03/03 8 | */ 9 | public class QuantConstants { 10 | public static final String ALGO_CONFIG_PATH_PROP = "algo_config_path"; 11 | public static final String TIGER_CONFIG_PATH_PROP = "tiger_config_path"; 12 | 13 | public static final String GATEWAY_CONFIG_PATH = "g"; 14 | public static final String ALGO_CONFIG_PATH = "a"; 15 | } 16 | -------------------------------------------------------------------------------- /tquant-core/src/main/java/com/tquant/core/util/QuantUtils.java: -------------------------------------------------------------------------------- 1 | package com.tquant.core.util; 2 | 3 | import com.tquant.core.model.data.StockData; 4 | import java.math.BigDecimal; 5 | import java.util.List; 6 | import java.util.regex.Pattern; 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * Description: 11 | * 12 | * @author kevin 13 | * @date 2019/09/05 14 | */ 15 | public class QuantUtils { 16 | 17 | 18 | private static final Pattern FUTURE_SYMBOL_PATTERN = Pattern.compile("[A-Z]{2,4}([0-9]{4}|main){1}"); 19 | 20 | public static boolean isFuturesSymbol(String symbol) { 21 | if (symbol == null || symbol.isEmpty()) { 22 | return false; 23 | } 24 | if (FUTURE_SYMBOL_PATTERN.matcher(symbol).matches()) { 25 | return true; 26 | } 27 | return false; 28 | } 29 | 30 | /** 31 | * 将StockDatas的一列值转化成double[] 32 | * @param stockDatas stock data list 33 | * @param column column 34 | * @return return 35 | */ 36 | public static double[] toDoubleArray(List stockDatas,String column){ 37 | List values = stockDatas.parallelStream().map(stockData -> stockData.attr(column)).collect(Collectors.toList()); 38 | double[] doubles = QuantUtils.toDoubleArray(values); 39 | return doubles; 40 | } 41 | 42 | public static boolean addDoubleArrayToList(double[] values,List stockDatas,String column){ 43 | if(values.length == stockDatas.size()){ 44 | for(int i =0 ;i values){ 54 | double[] array = new double[values.size()]; 55 | for(int i=0;i 2 | 5 | 6 | tiger-quant 7 | com.tigerbrokers 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | tquant-gateway 13 | 14 | 15 | 16 | com.tigerbrokers 17 | tquant-core 18 | 1.0.0 19 | 20 | 21 | com.tigerbrokers 22 | tquant-storage 23 | 1.0.0 24 | 25 | 26 | io.github.tigerbrokers 27 | openapi-java-sdk 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/api/OptionApi.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.api; 2 | 3 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionBriefItem; 4 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionExpirationItem; 5 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionKlineItem; 6 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionRealTimeQuoteGroup; 7 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionTradeTickItem; 8 | import com.tigerbrokers.stock.openapi.client.struct.enums.Right; 9 | import com.tquant.core.model.request.OptionChainFilter; 10 | import java.time.LocalDate; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * Description: option api 16 | * 17 | * @author kevin 18 | * @date 2019/08/15 19 | */ 20 | public interface OptionApi { 21 | 22 | OptionExpirationItem getOptionExpiration(String symbol) ; 23 | 24 | List getOptionBrief(String symbol, Right right, String strike, String expiry); 25 | 26 | List getOptionChain(String symbol, String expiry, OptionChainFilter optionChainFilter); 27 | 28 | List getOptionKline(String symbol, Right right, String strike, String expiry, LocalDate beginDate, 29 | LocalDate endDate); 30 | 31 | List getOptionTradeTick(String symbol, Right right, String strike, String expiry); 32 | } 33 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/api/QuoteApi.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.api; 2 | 3 | import com.tquant.core.model.data.Bar; 4 | import com.tquant.core.model.data.MarketStatus; 5 | import com.tquant.core.model.data.RealtimeQuote; 6 | import com.tquant.core.model.data.SymbolName; 7 | import com.tquant.core.model.data.Tick; 8 | import com.tquant.core.model.data.TimelineQuote; 9 | import com.tquant.core.model.data.TradeCalendar; 10 | import com.tquant.core.model.enums.BarType; 11 | import com.tigerbrokers.stock.openapi.client.https.domain.financial.item.CorporateDividendItem; 12 | import com.tigerbrokers.stock.openapi.client.https.domain.financial.item.CorporateSplitItem; 13 | import com.tigerbrokers.stock.openapi.client.https.domain.financial.item.FinancialDailyItem; 14 | import com.tigerbrokers.stock.openapi.client.https.domain.financial.item.FinancialReportItem; 15 | import com.tigerbrokers.stock.openapi.client.struct.enums.FinancialPeriodType; 16 | import com.tigerbrokers.stock.openapi.client.struct.enums.Market; 17 | import com.tigerbrokers.stock.openapi.client.struct.enums.RightOption; 18 | import java.time.LocalDate; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * Description: quote api 24 | * 25 | * @author kevin 26 | * @date 2019/08/15 27 | */ 28 | public interface QuoteApi { 29 | 30 | String grabQuotePermission(); 31 | 32 | List getMarketState(Market market); 33 | 34 | List getTradingCalendar(Market market, String beginDate, String endDate); 35 | 36 | List getSymbols(Market market); 37 | 38 | List getSymbolNames(Market market); 39 | 40 | Map> getBars(List symbols, BarType barType, RightOption rightOption, int limit); 41 | 42 | Map> getBars(List symbols, BarType barType, LocalDate start, LocalDate end, 43 | RightOption rightOption); 44 | 45 | Map> getFuturesBars(List symbols, BarType barType, int limit); 46 | 47 | Map> getRealTimeQuotes(List symbols); 48 | 49 | Map> getTradeTicks(List symbols); 50 | 51 | Map> getTimeShareQuotes(List symbols, Long beginTime); 52 | 53 | Map> getFinancialDaily(List symbols, List fields, 54 | LocalDate beginDate, 55 | LocalDate endDate); 56 | 57 | Map> getFinancialReport(List symbols, List fields, 58 | FinancialPeriodType periodType); 59 | 60 | Map> getCorporateSplit(List symbols, LocalDate beginDate, LocalDate endDate); 61 | 62 | Map> getCorporateDividend(List symbols, LocalDate beginDate, 63 | LocalDate endDate); 64 | } 65 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/api/TradeApi.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.api; 2 | 3 | import com.tquant.core.model.data.Asset; 4 | import com.tquant.core.model.data.Contract; 5 | import com.tquant.core.model.data.Order; 6 | import com.tquant.core.model.data.Position; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * Description: trade api 12 | * 13 | * @author kevin 14 | * @date 2019/08/15 15 | */ 16 | public interface TradeApi { 17 | 18 | void setMock(boolean isMock); 19 | 20 | int getOrderId(); 21 | 22 | long placeLimitOrder(Contract contract, String direction, Double price, int quantity); 23 | 24 | long placeMarketOrder(Contract contract, String direction, int quantity); 25 | 26 | String cancelOrder(long id); 27 | 28 | String modifyOrder(long id, Double limitPrice, int quantity); 29 | 30 | Order getOrder(long id); 31 | 32 | List getOpenOrders(); 33 | 34 | List getOpenOrders(String secType); 35 | 36 | List getFilledOrders(String secType); 37 | 38 | List getCancelledOrders(String secType); 39 | 40 | List getOrders(String secType); 41 | 42 | List getAccounts(); 43 | 44 | Asset getAsset(); 45 | 46 | Asset getAsset(String secType); 47 | 48 | Map> getPositions(); 49 | 50 | List getContracts(String secType); 51 | 52 | List getContracts(List symbols, String secType); 53 | } 54 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/tiger/TigerClient.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.tiger; 2 | 3 | import com.tigerbrokers.stock.openapi.client.config.ClientConfig; 4 | import com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient; 5 | 6 | /** 7 | * Description: 8 | * 9 | * @author kevin 10 | * @date 2022/08/03 11 | */ 12 | public class TigerClient { 13 | 14 | public static TigerHttpClient getInstance() { 15 | TigerConfig config = TigerConfigLoader.loadTigerConfig(); 16 | ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG; 17 | clientConfig.tigerId = config.getTigerId(); 18 | clientConfig.privateKey = config.getPrivateKey(); 19 | clientConfig.defaultAccount = config.getAccount(); 20 | return TigerHttpClient.getInstance().clientConfig(clientConfig); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/tiger/TigerConfig.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.tiger; 2 | 3 | import com.tquant.core.model.Config; 4 | import lombok.Data; 5 | 6 | /** 7 | * Description: tiger config 8 | * 9 | * @author kevin 10 | * @date 2019/08/16 11 | */ 12 | @Data 13 | public class TigerConfig implements Config { 14 | 15 | private String tigerId; 16 | private String account; 17 | private boolean apiLogEnable; 18 | private String apiLogPath; 19 | private String privateKey; 20 | private String serverUrl; 21 | private String socketUrl; 22 | private String apiCallbackClassName; 23 | private Class apiCallbackClass; 24 | } 25 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/tiger/TigerConfigLoader.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.tiger; 2 | 3 | import com.tquant.core.TigerQuantException; 4 | import com.tquant.core.config.*; 5 | 6 | import static com.tquant.core.util.QuantConstants.TIGER_CONFIG_PATH_PROP; 7 | 8 | /** 9 | * Description: 10 | * 11 | * @author kevin 12 | * @date 2022/08/05 13 | */ 14 | public class TigerConfigLoader { 15 | 16 | public static TigerConfig loadTigerConfig() { 17 | String gatewayConfigPath = System.getProperty(TIGER_CONFIG_PATH_PROP); 18 | if (gatewayConfigPath == null) { 19 | throw new TigerQuantException("tiger gateway config path cannot be empty"); 20 | } 21 | TigerConfig config = ConfigLoader.loadConfig(gatewayConfigPath, TigerConfig.class); 22 | 23 | if (config.getTigerId() == null) { 24 | throw new TigerQuantException("tigerId is null"); 25 | } 26 | if (config.getAccount() == null) { 27 | throw new TigerQuantException("account is null"); 28 | } 29 | if (config.getPrivateKey() == null) { 30 | throw new TigerQuantException("privateKey is null"); 31 | } 32 | return config; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/tiger/TigerGateway.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.tiger; 2 | 3 | import com.tquant.core.TigerQuantException; 4 | import com.tquant.core.config.ConfigLoader; 5 | import com.tquant.gateway.api.OptionApi; 6 | import com.tquant.gateway.api.QuoteApi; 7 | import com.tquant.gateway.api.TradeApi; 8 | import com.tquant.core.event.EventEngine; 9 | import com.tquant.core.core.Gateway; 10 | import com.tquant.core.model.data.Asset; 11 | import com.tquant.core.model.data.Bar; 12 | import com.tquant.core.model.data.Contract; 13 | import com.tquant.core.model.data.Order; 14 | import com.tquant.core.model.data.Position; 15 | import com.tquant.core.model.enums.BarType; 16 | import com.tquant.core.model.request.ModifyRequest; 17 | import com.tquant.core.model.request.OrderRequest; 18 | import com.tquant.core.model.request.SubscribeRequest; 19 | import com.tquant.storage.dao.ContractDAO; 20 | import com.tquant.core.util.QuantUtils; 21 | import com.tigerbrokers.stock.openapi.client.config.ClientConfig; 22 | import com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient; 23 | import com.tigerbrokers.stock.openapi.client.socket.WebSocketClient; 24 | import com.tigerbrokers.stock.openapi.client.struct.enums.RightOption; 25 | import com.tigerbrokers.stock.openapi.client.struct.enums.SecType; 26 | import com.tigerbrokers.stock.openapi.client.struct.enums.Subject; 27 | import com.tigerbrokers.stock.openapi.client.util.ApiLogger; 28 | import java.util.ArrayList; 29 | import java.util.HashMap; 30 | import java.util.List; 31 | import java.util.Map; 32 | 33 | /** 34 | * Description: 35 | * 36 | * @author kevin 37 | * @date 2019/08/16 38 | */ 39 | public class TigerGateway extends Gateway { 40 | 41 | public static final String GATEWAY_NAME = "TigerGateway"; 42 | 43 | private TigerHttpClient serverClient; 44 | private WebSocketClient socketClient; 45 | private TigerConfig config; 46 | private static Boolean contractLoadEnabled = (Boolean) ConfigLoader.GLOBAL_SETTINGS.get("contract.load.enable"); 47 | private static Boolean subscribeEnabled = (Boolean) ConfigLoader.GLOBAL_SETTINGS.get("subscribe.enable"); 48 | private TradeApi tradeApi; 49 | private QuoteApi quoteApi; 50 | private OptionApi optionApi; 51 | private TigerSubscribeApi subscribeApi; 52 | 53 | private Map contractDict = new HashMap<>(); 54 | private Map orderDict = new HashMap<>(); 55 | private Map openOrderDict = new HashMap<>(); 56 | private Map assetDict = new HashMap<>(); 57 | 58 | private ContractDAO contractDAO = new ContractDAO(); 59 | 60 | public TigerGateway(EventEngine eventEngine) { 61 | super(eventEngine, GATEWAY_NAME); 62 | init(); 63 | } 64 | 65 | private void init() { 66 | this.config = TigerConfigLoader.loadTigerConfig(); 67 | if (this.config == null) { 68 | throw new TigerQuantException("config is null"); 69 | } 70 | ApiLogger.setEnabled(config.isApiLogEnable(), config.getApiLogPath()); 71 | ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG; 72 | clientConfig.privateKey = config.getPrivateKey(); 73 | clientConfig.tigerId = config.getTigerId(); 74 | clientConfig.defaultAccount = config.getAccount(); 75 | serverClient = TigerHttpClient.getInstance().clientConfig(clientConfig); 76 | try { 77 | subscribeApi = new TigerSubscribeApi(this); 78 | socketClient = WebSocketClient.getInstance().clientConfig(clientConfig).apiComposeCallback(subscribeApi); 79 | } catch (Exception e) { 80 | throw new TigerQuantException("build socket client exception:" + e.getMessage()); 81 | } 82 | tradeApi = new TigerTradeApi(serverClient); 83 | quoteApi = new TigerQuoteApi(serverClient); 84 | optionApi = new TigerOptionApi(serverClient); 85 | } 86 | 87 | public TradeApi getTradeApi() { 88 | return tradeApi; 89 | } 90 | 91 | public QuoteApi getQuoteApi() { 92 | return quoteApi; 93 | } 94 | 95 | public OptionApi getOptionApi() { 96 | return optionApi; 97 | } 98 | 99 | @Override 100 | public void connect() { 101 | queryContract(); 102 | quoteApi.grabQuotePermission(); 103 | 104 | if (isSubscribeEnabled() && !socketClient.isConnected()) { 105 | socketClient.connect(); 106 | } 107 | } 108 | 109 | @Override 110 | public void stop() { 111 | cancelAllActiveOrders(); 112 | if (socketClient != null) { 113 | try { 114 | socketClient.disconnect(); 115 | } catch (Exception e) { 116 | } 117 | } 118 | } 119 | 120 | private boolean isSubscribeEnabled() { 121 | return subscribeEnabled == null ? false : subscribeEnabled; 122 | } 123 | 124 | private void cancelAllActiveOrders() { 125 | List activeOrders = tradeApi.getOpenOrders(SecType.FUT.name()); 126 | if (activeOrders != null) { 127 | activeOrders.forEach(order -> { 128 | log("cancel active order: {}", order); 129 | tradeApi.cancelOrder(order.getId()); 130 | }); 131 | } 132 | } 133 | 134 | private void queryOrder(SecType secType) { 135 | List orders = tradeApi.getOrders(secType.name()); 136 | if (orders == null) { 137 | return; 138 | } 139 | for (Order order : orders) { 140 | this.orderDict.put(order.getId(), order); 141 | if (order.isActive()) { 142 | this.openOrderDict.put(order.getId(), order); 143 | } 144 | onOrder(order); 145 | } 146 | } 147 | 148 | private void queryAccount() { 149 | } 150 | 151 | private void queryAsset(SecType secType) { 152 | Asset asset = tradeApi.getAsset(); 153 | this.assetDict.put(asset.getAccount(), asset); 154 | onAsset(asset); 155 | } 156 | 157 | private void queryContract() { 158 | if (contractLoadEnabled == null || !contractLoadEnabled) { 159 | return; 160 | } 161 | log("contract loading......"); 162 | List contracts = contractDAO.queryContracts(); 163 | for (Contract contract : contracts) { 164 | contractDict.put(contract.getIdentifier(), contract); 165 | onContract(contract); 166 | } 167 | log("contract loaded......"); 168 | } 169 | 170 | @Override 171 | public void subscribe(SubscribeRequest request) { 172 | if (!isSubscribeEnabled()) { 173 | return; 174 | } 175 | socketClient.subscribe(Subject.OrderStatus); 176 | socketClient.subscribe(Subject.Asset); 177 | socketClient.subscribe(Subject.Position); 178 | socketClient.subscribeQuote(request.getSymbols()); 179 | } 180 | 181 | @Override 182 | public void cancelSubscribe(SubscribeRequest request) { 183 | socketClient.cancelSubscribeQuote(request.getSymbols()); 184 | } 185 | 186 | @Override 187 | public long sendOrder(OrderRequest request) { 188 | Contract contract = contractDict.get(request.getSymbol()); 189 | if (request.getPrice() == null) { 190 | return tradeApi.placeMarketOrder(contract, request.getDirection(), request.getQuantity()); 191 | } else { 192 | return tradeApi.placeLimitOrder(contract, request.getDirection(), request.getPrice(), request.getQuantity()); 193 | } 194 | } 195 | 196 | @Override 197 | public void cancelOrder(ModifyRequest request) { 198 | if (request == null) { 199 | throw new TigerQuantException("cancel order request is null"); 200 | } 201 | if (request.getOrderId() <= 0) { 202 | throw new TigerQuantException("cancel order request orderId <= 0"); 203 | } 204 | tradeApi.cancelOrder(request.getOrderId()); 205 | } 206 | 207 | @Override 208 | public void modifyOrder(ModifyRequest request) { 209 | if (request == null) { 210 | throw new TigerQuantException("modify order request is null"); 211 | } 212 | if (request.getLimitPrice() == null || request.getLimitPrice() <= 0) { 213 | throw new TigerQuantException("modify param error,price " + request.getLimitPrice() + " illegal"); 214 | } 215 | if (request.getQuantity() == null || request.getQuantity() <= 0) { 216 | throw new TigerQuantException("modify param error, quantity " + request.getQuantity() + " illegal"); 217 | } 218 | tradeApi.modifyOrder(request.getOrderId(), request.getLimitPrice(), request.getQuantity()); 219 | } 220 | 221 | @Override 222 | public Map> getBars(List symbols, BarType barType, int limit) { 223 | if (symbols == null || symbols.isEmpty()) { 224 | throw new TigerQuantException("get bars symbols is null"); 225 | } 226 | if (barType == null) { 227 | throw new TigerQuantException("get bars barType is null"); 228 | } 229 | if (limit <= 0) { 230 | throw new TigerQuantException("get bars limit <= 0"); 231 | } 232 | List stockSymbols = new ArrayList<>(); 233 | List futureSymbols = new ArrayList<>(); 234 | for (String symbol : symbols) { 235 | if (QuantUtils.isFuturesSymbol(symbol)) { 236 | futureSymbols.add(symbol); 237 | } else { 238 | stockSymbols.add(symbol); 239 | } 240 | } 241 | Map> bars = new HashMap<>(); 242 | if (!stockSymbols.isEmpty()) { 243 | bars.putAll(quoteApi.getBars(stockSymbols, barType, RightOption.br, limit)); 244 | } 245 | if (!futureSymbols.isEmpty()) { 246 | bars.putAll(quoteApi.getFuturesBars(futureSymbols, barType, limit)); 247 | } 248 | return bars; 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/tiger/TigerOptionApi.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.tiger; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionRealTimeQuoteGroup; 5 | import com.tigerbrokers.stock.openapi.client.https.domain.option.model.OptionChainFilterModel; 6 | import com.tigerbrokers.stock.openapi.client.https.request.option.OptionChainQueryV3Request; 7 | import com.tquant.core.TigerQuantException; 8 | import com.tquant.core.model.request.OptionChainFilter; 9 | import com.tquant.gateway.api.OptionApi; 10 | import com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient; 11 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionBriefItem; 12 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionChainItem; 13 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionExpirationItem; 14 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionKlineItem; 15 | import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionTradeTickItem; 16 | import com.tigerbrokers.stock.openapi.client.https.domain.option.model.OptionChainModel; 17 | import com.tigerbrokers.stock.openapi.client.https.domain.option.model.OptionCommonModel; 18 | import com.tigerbrokers.stock.openapi.client.https.domain.option.model.OptionKlineModel; 19 | import com.tigerbrokers.stock.openapi.client.https.request.option.OptionBriefQueryRequest; 20 | import com.tigerbrokers.stock.openapi.client.https.request.option.OptionChainQueryRequest; 21 | import com.tigerbrokers.stock.openapi.client.https.request.option.OptionExpirationQueryRequest; 22 | import com.tigerbrokers.stock.openapi.client.https.request.option.OptionKlineQueryRequest; 23 | import com.tigerbrokers.stock.openapi.client.https.request.option.OptionTradeTickQueryRequest; 24 | import com.tigerbrokers.stock.openapi.client.https.response.option.OptionBriefResponse; 25 | import com.tigerbrokers.stock.openapi.client.https.response.option.OptionChainResponse; 26 | import com.tigerbrokers.stock.openapi.client.https.response.option.OptionExpirationResponse; 27 | import com.tigerbrokers.stock.openapi.client.https.response.option.OptionKlineResponse; 28 | import com.tigerbrokers.stock.openapi.client.https.response.option.OptionTradeTickResponse; 29 | import com.tigerbrokers.stock.openapi.client.struct.enums.Right; 30 | import java.time.LocalDate; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.stream.Collectors; 34 | 35 | /** 36 | * Description: 37 | * 38 | * @author kevin 39 | * @date 2019/08/16 40 | */ 41 | public class TigerOptionApi implements OptionApi { 42 | 43 | private TigerHttpClient client; 44 | 45 | public TigerOptionApi(TigerHttpClient client) { 46 | this.client = client; 47 | } 48 | 49 | @Override 50 | public OptionExpirationItem getOptionExpiration(String symbol) { 51 | OptionExpirationResponse response = client.execute(OptionExpirationQueryRequest.of(Lists.newArrayList(symbol))); 52 | if (!response.isSuccess()) { 53 | throw new TigerQuantException("get option expiration error:" + response.getMessage()); 54 | } 55 | return response.getOptionExpirationItems().get(0); 56 | } 57 | 58 | @Override 59 | public List getOptionBrief(String symbol, Right right, String strike, String expiry) { 60 | OptionCommonModel optionCommonModel = new OptionCommonModel(); 61 | optionCommonModel.setSymbol(symbol); 62 | optionCommonModel.setRight(right.name()); 63 | optionCommonModel.setStrike(strike); 64 | if (expiry != null && !expiry.contains("-")) { 65 | optionCommonModel.setExpiry(expiry.substring(0, 4) + "-" + expiry.substring(4, 6) + "-" + expiry.substring(6)); 66 | } else { 67 | optionCommonModel.setExpiry(expiry); 68 | } 69 | OptionBriefResponse response = client.execute(OptionBriefQueryRequest.of(optionCommonModel)); 70 | if (!response.isSuccess()) { 71 | throw new TigerQuantException("get option brief error:" + response.getMessage()); 72 | } 73 | return response.getOptionBriefItems(); 74 | } 75 | 76 | @Override 77 | public List getOptionChain(String symbol, String expiry, OptionChainFilter filter) { 78 | OptionChainModel basicModel = new OptionChainModel(symbol, expiry); 79 | OptionChainFilterModel filterModel = null; 80 | if (filter != null) { 81 | filterModel = new OptionChainFilterModel().inTheMoney(filter.getInTheMoney()) 82 | .impliedVolatility(filter.getMinImpliedVolatility(), filter.getMaxImpliedVolatility()) 83 | .openInterest(filter.getMinOpenInterest(), filter.getMaxOpenInterest()) 84 | .greeks(new OptionChainFilterModel.Greeks() 85 | .delta(filter.getMinDelta(), filter.getMaxDelta()) 86 | .gamma(filter.getMinGamma(), filter.getMaxGamma()) 87 | .vega(filter.getMinVega(), filter.getMaxVega()) 88 | .theta(filter.getMinTheta(), filter.getMaxTheta()) 89 | .rho(filter.getMinRho(), filter.getMaxRho())); 90 | } 91 | 92 | OptionChainResponse response = client.execute(OptionChainQueryV3Request.of(basicModel, filterModel)); 93 | if (!response.isSuccess()) { 94 | throw new TigerQuantException("get option chain error:" + response.getMessage()); 95 | } 96 | List optionChainItems = response.getOptionChainItems(); 97 | if (optionChainItems == null || optionChainItems.isEmpty()) { 98 | throw new TigerQuantException("get option chain result empty"); 99 | } 100 | return optionChainItems.get(0).getItems(); 101 | } 102 | 103 | @Override 104 | public List getOptionKline(String symbol, Right right, String strike, String expiry, 105 | LocalDate beginDate, LocalDate endDate) { 106 | OptionKlineModel model = new OptionKlineModel(); 107 | model.setSymbol(symbol); 108 | model.setRight(right.name()); 109 | model.setStrike(strike); 110 | model.setExpiry(expiry); 111 | model.setBeginTime(beginDate.toEpochDay()); 112 | model.setEndTime(endDate.toEpochDay()); 113 | OptionKlineResponse response = client.execute(OptionKlineQueryRequest.of(model)); 114 | if (!response.isSuccess()) { 115 | throw new TigerQuantException("get option kline error:" + response.getMessage()); 116 | } 117 | return response.getKlineItems(); 118 | } 119 | 120 | @Override 121 | public List getOptionTradeTick(String symbol, Right right, String strike, 122 | String expiry) { 123 | OptionTradeTickResponse response = client.execute( 124 | OptionTradeTickQueryRequest.of(new OptionCommonModel(symbol, right.name(), strike, Long.parseLong(expiry)))); 125 | if (!response.isSuccess()) { 126 | throw new TigerQuantException("get option trade tick error:" + response.getMessage()); 127 | } 128 | return response.getOptionTradeTickItems(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /tquant-gateway/src/main/java/com/tquant/gateway/tiger/TigerSubscribeApi.java: -------------------------------------------------------------------------------- 1 | package com.tquant.gateway.tiger; 2 | 3 | import com.tigerbrokers.stock.openapi.client.socket.data.TradeTick; 4 | import com.tigerbrokers.stock.openapi.client.socket.data.pb.AssetData; 5 | import com.tigerbrokers.stock.openapi.client.socket.data.pb.OrderStatusData; 6 | import com.tigerbrokers.stock.openapi.client.socket.data.pb.OrderTransactionData; 7 | import com.tigerbrokers.stock.openapi.client.socket.data.pb.PositionData; 8 | import com.tigerbrokers.stock.openapi.client.socket.data.pb.QuoteBBOData; 9 | import com.tigerbrokers.stock.openapi.client.socket.data.pb.QuoteBasicData; 10 | import com.tigerbrokers.stock.openapi.client.socket.data.pb.QuoteDepthData; 11 | import com.tquant.core.core.Gateway; 12 | import com.tquant.core.model.data.Order; 13 | import com.tquant.core.model.data.Tick; 14 | import com.tigerbrokers.stock.openapi.client.socket.ApiComposeCallback; 15 | import com.tigerbrokers.stock.openapi.client.struct.SubscribedSymbol; 16 | 17 | /** 18 | * Description: 19 | * 20 | * @author kevin 21 | * @date 2019/08/20 22 | */ 23 | public class TigerSubscribeApi implements ApiComposeCallback { 24 | 25 | private Gateway gateway; 26 | 27 | public TigerSubscribeApi(Gateway gateway) { 28 | this.gateway = gateway; 29 | } 30 | 31 | @Override 32 | public void error(String errorMsg) { 33 | gateway.log("{} subscribeError {}", gateway.getGatewayName(), errorMsg); 34 | } 35 | 36 | @Override 37 | public void error(int id, int errorCode, String errorMsg) { 38 | gateway.log("{} subscribeError {}", gateway.getGatewayName(), errorMsg); 39 | } 40 | 41 | @Override 42 | public void connectionClosed() { 43 | gateway.log("{} connectionClosed ", gateway.getGatewayName()); 44 | } 45 | 46 | 47 | @Override 48 | public void connectionAck() { 49 | gateway.log("{} connectionAck ", gateway.getGatewayName()); 50 | } 51 | 52 | @Override 53 | public void connectionAck(int serverSendInterval, int serverReceiveInterval) { 54 | gateway.log("{} connectionAck sendInterval:{},receiveInterval:{} ", gateway.getGatewayName(), serverSendInterval, 55 | serverReceiveInterval); 56 | } 57 | 58 | @Override 59 | public void hearBeat(String heartBeatContent) { 60 | gateway.log("{} connectionHeartBeat {}", gateway.getGatewayName(), heartBeatContent); 61 | } 62 | 63 | @Override 64 | public void serverHeartBeatTimeOut(String channelIdAsLongText) { 65 | gateway.log("{} ConnectionHeartBeatTimeout {}", gateway.getGatewayName(), channelIdAsLongText); 66 | } 67 | 68 | @Override 69 | public void getSubscribedSymbolEnd(SubscribedSymbol subscribedSymbol) { 70 | gateway.log("{} subscribeSymbols {}", gateway.getGatewayName(), subscribedSymbol); 71 | } 72 | 73 | @Override 74 | public void connectionKickout(int errorCode, String errorMsg) { 75 | gateway.log("{} connectionKickoff {} {}", gateway.getGatewayName(), errorCode, errorMsg); 76 | } 77 | 78 | @Override 79 | public void orderStatusChange(OrderStatusData data) { 80 | gateway.log("orderChange {}",data); 81 | Order order = new Order(); 82 | order.setAccount(data.getAccount()); 83 | order.setAverageFilledPrice(data.getAvgFillPrice()); 84 | order.setDirection(data.getAction()); 85 | order.setId(data.getId()); 86 | order.setOrderType(data.getOrderType()); 87 | order.setSymbol(data.getSymbol()); 88 | order.setVolume(data.getTotalQuantity()); 89 | order.setFilledVolume(data.getFilledQuantity()); 90 | order.setStatus(data.getStatus()); 91 | gateway.onOrder(order); 92 | } 93 | 94 | @Override 95 | public void orderTransactionChange(OrderTransactionData data) { 96 | 97 | } 98 | 99 | @Override 100 | public void positionChange(PositionData data) { 101 | 102 | } 103 | 104 | @Override 105 | public void assetChange(AssetData data) { 106 | 107 | } 108 | 109 | @Override 110 | public void tradeTickChange(TradeTick data) { 111 | 112 | } 113 | 114 | @Override 115 | public void quoteChange(QuoteBasicData data) { 116 | gateway.log("quoteChange {}",data); 117 | Tick tick = new Tick(); 118 | tick.setSymbol(data.getSymbol()); 119 | tick.setIdentifier(data.getIdentifier()); 120 | tick.setVolume(data.getVolume()); 121 | tick.setLatestPrice(data.getLatestPrice()); 122 | tick.setAmount(data.getAmount()); 123 | tick.setOpen(data.getOpen()); 124 | tick.setHigh(data.getHigh()); 125 | tick.setLow(data.getLow()); 126 | //close 127 | //tick.setClose(data.get); 128 | tick.setPreClose(data.getPreClose()); 129 | 130 | gateway.onTick(tick); 131 | } 132 | 133 | @Override 134 | public void quoteAskBidChange(QuoteBBOData data) { 135 | 136 | } 137 | 138 | @Override 139 | public void optionChange(QuoteBasicData data) { 140 | 141 | } 142 | 143 | @Override 144 | public void optionAskBidChange(QuoteBBOData data) { 145 | 146 | } 147 | 148 | @Override 149 | public void futureChange(QuoteBasicData data) { 150 | 151 | } 152 | 153 | @Override 154 | public void futureAskBidChange(QuoteBBOData data) { 155 | 156 | } 157 | 158 | @Override 159 | public void depthQuoteChange(QuoteDepthData data) { 160 | gateway.log("{} depthQuote change {}", gateway.getGatewayName(), data); 161 | } 162 | 163 | @Override 164 | public void subscribeEnd(int id, String subject, String result) { 165 | gateway.log("{} subscribeEnd {} {} {}", gateway.getGatewayName(), id, subject, result); 166 | } 167 | 168 | @Override 169 | public void cancelSubscribeEnd(int id, String subject, String result) { 170 | gateway.log("{} cancelSubscribeEnd {} {} {}", gateway.getGatewayName(), id, subject, result); 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /tquant-loader/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | tiger-quant 7 | com.tigerbrokers 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | tquant-loader 13 | 14 | 15 | 16 | com.tigerbrokers 17 | tquant-core 18 | 1.0.0 19 | 20 | 21 | com.tigerbrokers 22 | tquant-storage 23 | 1.0.0 24 | 25 | 26 | com.tigerbrokers 27 | tquant-gateway 28 | 1.0.0 29 | 30 | 31 | commons-cli 32 | commons-cli 33 | 34 | 35 | io.github.tigerbrokers 36 | openapi-java-sdk 37 | 38 | 39 | -------------------------------------------------------------------------------- /tquant-loader/src/main/java/com/tquant/loader/BarLoader.java: -------------------------------------------------------------------------------- 1 | package com.tquant.loader; 2 | 3 | 4 | import com.tquant.core.model.data.Bar; 5 | import com.tquant.core.model.enums.BarType; 6 | 7 | import com.tquant.gateway.api.QuoteApi; 8 | import com.tquant.gateway.tiger.TigerClient; 9 | import com.tquant.gateway.tiger.TigerQuoteApi; 10 | import com.tquant.loader.command.CliRunner; 11 | import com.tquant.loader.command.CommandExecuteTemplate; 12 | import com.tquant.storage.dao.BarDAO; 13 | import java.time.LocalDate; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | import org.apache.commons.cli.CommandLine; 18 | import org.apache.commons.cli.Options; 19 | 20 | /** 21 | * Description: 22 | * 23 | * @author kevin 24 | * @date 2022/08/02 25 | */ 26 | public class BarLoader implements CliRunner { 27 | 28 | private static final String SYMBOL = "m"; 29 | private static final String START = "s"; 30 | private static final String END = "e"; 31 | private static final String PERIOD = "p"; 32 | 33 | private static BarDAO barDAO = new BarDAO(); 34 | 35 | public static void main(String[] args) { 36 | CommandExecuteTemplate.execute(args, "-[m][s][e][p]", new BarLoader()); 37 | } 38 | 39 | @Override 40 | public Options initOptions() { 41 | Options options = new Options(); 42 | options.addOption(SYMBOL, true, "股票代码"); 43 | options.addOption(START, true, "开始时间,格式:yyyyMMdd"); 44 | options.addOption(END, true, "结束时间,格式:yyyyMMdd"); 45 | options.addOption(PERIOD, true, "K线类型,day/min1/min30/min60"); 46 | return options; 47 | } 48 | 49 | @Override 50 | public boolean validateOptions(CommandLine cmdLine) { 51 | return (cmdLine.hasOption(SYMBOL) && cmdLine.hasOption(START) && cmdLine.hasOption(END)) ; 52 | } 53 | 54 | @Override 55 | public void start(CommandLine cmdLine) { 56 | String symbol; 57 | String start; 58 | String end; 59 | String period; 60 | if (cmdLine.hasOption(SYMBOL) && cmdLine.hasOption(START) && cmdLine.hasOption(END)) { 61 | symbol = cmdLine.getOptionValue(SYMBOL); 62 | start = cmdLine.getOptionValue(START); 63 | end = cmdLine.getOptionValue(END); 64 | period = cmdLine.getOptionValue(PERIOD); 65 | } else { 66 | throw new RuntimeException("symbol, start_date, end_date must not be null"); 67 | } 68 | 69 | BarLoader barLoader = new BarLoader(); 70 | List symbols = new ArrayList<>(); 71 | symbols.add(symbol); 72 | 73 | barLoader.queryAndSaveBar(symbols, BarType.valueOf(period), parseLocalDate(start), parseLocalDate(end)); 74 | } 75 | 76 | public void queryAndSaveBar(List symbols, BarType barType, LocalDate startDate, LocalDate endDate) { 77 | QuoteApi quoteApi = new TigerQuoteApi(TigerClient.getInstance()); 78 | Map> symbolBars = quoteApi.getBars(symbols, barType, startDate, endDate, null); 79 | for (List bars : symbolBars.values()) { 80 | for (Bar bar : bars) { 81 | barDAO.saveBar(bar); 82 | } 83 | } 84 | } 85 | 86 | private static LocalDate parseLocalDate(String date) { 87 | return LocalDate.of(Integer.parseInt(date.substring(0, 4)), Integer.parseInt(date.substring(4, 6)), 88 | Integer.parseInt(date.substring(6, 8))); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tquant-loader/src/main/java/com/tquant/loader/ContractLoader.java: -------------------------------------------------------------------------------- 1 | package com.tquant.loader; 2 | 3 | import com.tquant.gateway.api.TradeApi; 4 | import com.tquant.gateway.tiger.TigerClient; 5 | import com.tquant.gateway.tiger.TigerTradeApi; 6 | import com.tquant.loader.command.CommandExecuteTemplate; 7 | import com.tquant.loader.command.CliRunner; 8 | import com.tquant.core.model.data.Contract; 9 | 10 | import com.tigerbrokers.stock.openapi.client.struct.enums.SecType; 11 | import com.tquant.storage.dao.ContractDAO; 12 | import java.util.List; 13 | import org.apache.commons.cli.CommandLine; 14 | import org.apache.commons.cli.Options; 15 | 16 | /** 17 | * Description: update contract once per day 18 | * 19 | * @author kevin 20 | * @date 2022/03/04 21 | */ 22 | public class ContractLoader implements CliRunner { 23 | private static final String SEC_TYPE = "t"; 24 | 25 | private static ContractDAO contractDAO = new ContractDAO(); 26 | 27 | public static void main(String[] args) { 28 | CommandExecuteTemplate.execute(args, "-[t]", new ContractLoader()); 29 | } 30 | 31 | @Override 32 | public Options initOptions() { 33 | Options options = new Options(); 34 | options.addOption(SEC_TYPE, true, "交易品种,包括 SEC/FUT"); 35 | return options; 36 | } 37 | 38 | @Override 39 | public boolean validateOptions(CommandLine cmdLine) { 40 | return cmdLine.hasOption(SEC_TYPE); 41 | } 42 | 43 | @Override 44 | public void start(CommandLine cmdLine) { 45 | queryAndSaveContracts(SecType.valueOf(cmdLine.getOptionValue(SEC_TYPE))); 46 | } 47 | 48 | private static void queryAndSaveContracts(SecType secType) { 49 | TradeApi tradeApi = new TigerTradeApi(TigerClient.getInstance()); 50 | List contracts = tradeApi.getContracts(secType.name()); 51 | for (Contract contract : contracts) { 52 | contractDAO.saveContract(contract); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tquant-loader/src/main/java/com/tquant/loader/TickLoader.java: -------------------------------------------------------------------------------- 1 | package com.tquant.loader; 2 | 3 | import com.tquant.gateway.api.QuoteApi; 4 | import com.tquant.gateway.tiger.TigerClient; 5 | import com.tquant.gateway.tiger.TigerQuoteApi; 6 | import com.tquant.loader.command.CliRunner; 7 | import com.tquant.loader.command.CommandExecuteTemplate; 8 | import com.tquant.core.model.data.Tick; 9 | import com.tquant.storage.dao.TickDAO; 10 | import java.time.LocalDate; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | import org.apache.commons.cli.CommandLine; 15 | import org.apache.commons.cli.Options; 16 | 17 | /** 18 | * Description: 19 | * 20 | * @author kevin 21 | * @date 2022/08/04 22 | */ 23 | public class TickLoader implements CliRunner { 24 | 25 | private static final String SYMBOL = "m"; 26 | private static final String START = "s"; 27 | private static final String END = "e"; 28 | 29 | private static TickDAO tickDAO = new TickDAO(); 30 | 31 | public static void main(String[] args) { 32 | CommandExecuteTemplate.execute(args, "-[m][s][e]", new TickLoader()); 33 | } 34 | 35 | @Override 36 | public Options initOptions() { 37 | Options options = new Options(); 38 | options.addOption(SYMBOL, true, "股票代码"); 39 | options.addOption(START, true, "开始时间,格式:yyyyMMdd"); 40 | options.addOption(END, true, "结束时间,格式:yyyyMMdd"); 41 | return options; 42 | } 43 | 44 | @Override 45 | public boolean validateOptions(CommandLine cmdLine) { 46 | return (cmdLine.hasOption(SYMBOL) && cmdLine.hasOption(START) && cmdLine.hasOption(END)) ; 47 | } 48 | 49 | @Override 50 | public void start(CommandLine cmdLine) { 51 | String symbol; 52 | String start; 53 | String end; 54 | if (cmdLine.hasOption(SYMBOL) && cmdLine.hasOption(START) && cmdLine.hasOption(END)) { 55 | symbol = cmdLine.getOptionValue(SYMBOL); 56 | start = cmdLine.getOptionValue(START); 57 | end = cmdLine.getOptionValue(END); 58 | } else { 59 | throw new RuntimeException("symbol, start_date, end_date must not be null"); 60 | } 61 | 62 | List symbols = new ArrayList<>(); 63 | symbols.add(symbol); 64 | 65 | queryAndSaveTick(symbols, parseLocalDate(start), parseLocalDate(end)); 66 | } 67 | 68 | public void queryAndSaveTick(List symbols, LocalDate startDate, LocalDate endDate) { 69 | QuoteApi quoteApi = new TigerQuoteApi(TigerClient.getInstance()); 70 | Map> tradeTicks = quoteApi.getTradeTicks(symbols); 71 | for (List ticks : tradeTicks.values()) { 72 | ticks.stream().forEach(tick -> tickDAO.saveTick(tick)); 73 | } 74 | } 75 | 76 | private static LocalDate parseLocalDate(String date) { 77 | return LocalDate.of(Integer.parseInt(date.substring(0, 4)), Integer.parseInt(date.substring(4, 6)), 78 | Integer.parseInt(date.substring(6, 8))); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tquant-loader/src/main/java/com/tquant/loader/command/CliRunner.java: -------------------------------------------------------------------------------- 1 | package com.tquant.loader.command; 2 | 3 | /** 4 | * Description: 5 | * 6 | * @author kevin 7 | * @date 2022/08/03 8 | */ 9 | import org.apache.commons.cli.CommandLine; 10 | import org.apache.commons.cli.Options; 11 | 12 | public interface CliRunner { 13 | Options initOptions(); 14 | 15 | boolean validateOptions(CommandLine var1); 16 | 17 | void start(CommandLine var1); 18 | } 19 | -------------------------------------------------------------------------------- /tquant-loader/src/main/java/com/tquant/loader/command/CommandExecuteTemplate.java: -------------------------------------------------------------------------------- 1 | package com.tquant.loader.command; 2 | 3 | import org.apache.commons.cli.CommandLine; 4 | import org.apache.commons.cli.CommandLineParser; 5 | import org.apache.commons.cli.DefaultParser; 6 | import org.apache.commons.cli.HelpFormatter; 7 | import org.apache.commons.cli.Options; 8 | import org.apache.commons.cli.ParseException; 9 | 10 | /** 11 | * Description: 12 | * 13 | * @author kevin 14 | * @date 2022/08/03 15 | */ 16 | 17 | public class CommandExecuteTemplate { 18 | 19 | private static HelpFormatter formatter = new HelpFormatter(); 20 | 21 | public CommandExecuteTemplate() { 22 | } 23 | 24 | public static void execute(String[] args, String cmdName, CliRunner runner) { 25 | CommandLineParser parser = new DefaultParser(); 26 | Options options = runner.initOptions(); 27 | 28 | try { 29 | CommandLine cmdLine = parser.parse(options, args); 30 | if (!runner.validateOptions(cmdLine) || cmdLine.hasOption("help")) { 31 | formatter.printHelp(cmdName, options); 32 | return; 33 | } 34 | 35 | runner.start(cmdLine); 36 | } catch (ParseException var6) { 37 | System.out.println("Unexpected exception:" + var6.getMessage()); 38 | formatter.printHelp(cmdName, options); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /tquant-storage/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | tiger-quant 7 | com.tigerbrokers 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | tquant-storage 13 | 14 | 15 | 16 | com.tigerbrokers 17 | tquant-core 18 | 1.0.0 19 | 20 | 21 | org.mybatis 22 | mybatis 23 | 24 | 25 | mysql 26 | mysql-connector-java 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tquant-storage/src/main/java/com/tquant/storage/dao/BarDAO.java: -------------------------------------------------------------------------------- 1 | package com.tquant.storage.dao; 2 | 3 | import com.tquant.core.model.data.Bar; 4 | import com.tquant.storage.mapper.BarMapper; 5 | import java.time.LocalDateTime; 6 | import java.util.List; 7 | import org.apache.ibatis.session.SqlSession; 8 | 9 | /** 10 | * Description: 11 | * todo 优化SqlSession,保证线程安全 12 | * 13 | * @author kevin 14 | * @date 2019/09/02 15 | */ 16 | public class BarDAO extends BaseDAO { 17 | 18 | private static SqlSession sqlSession = new BaseDAO().openSession(); 19 | 20 | public void saveBar(Bar bar) { 21 | sqlSession.getMapper(BarMapper.class).saveBar(bar); 22 | sqlSession.commit(); 23 | } 24 | 25 | public List queryBar(String symbol, int limit) { 26 | return sqlSession.getMapper(BarMapper.class).queryBars(symbol, limit); 27 | } 28 | 29 | public List queryBar(String symbol, String period, LocalDateTime beginDate, LocalDateTime endDate) { 30 | return sqlSession.getMapper(BarMapper.class).queryBarsByDate(symbol, period, beginDate, endDate); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tquant-storage/src/main/java/com/tquant/storage/dao/BaseDAO.java: -------------------------------------------------------------------------------- 1 | package com.tquant.storage.dao; 2 | 3 | 4 | import com.tquant.core.config.*; 5 | import com.tquant.core.TigerQuantException; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import org.apache.ibatis.io.Resources; 9 | import org.apache.ibatis.session.SqlSession; 10 | import org.apache.ibatis.session.SqlSessionFactory; 11 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 12 | 13 | /** 14 | * Description: 15 | * 16 | * @author kevin 17 | * @date 2019/09/02 18 | */ 19 | public class BaseDAO { 20 | 21 | private static SqlSessionFactory sqlSessionFactory; 22 | private static Boolean storageEnabled = (Boolean) ConfigLoader.GLOBAL_SETTINGS.get("storage.enable"); 23 | static { 24 | if (storageEnabled != null && storageEnabled) { 25 | String resource = "datasource/mybatis-config.xml"; 26 | InputStream inputStream; 27 | try { 28 | inputStream = Resources.getResourceAsStream(resource); 29 | sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 30 | } catch (IOException e) { 31 | throw new TigerQuantException("load mybatis config error:" + e.getMessage()); 32 | } 33 | } 34 | } 35 | 36 | private static SqlSessionFactory getSqlSessionFactory() { 37 | if (sqlSessionFactory == null) { 38 | throw new TigerQuantException("[storage.enable] switch is not configured in global_setting.json"); 39 | } 40 | return sqlSessionFactory; 41 | } 42 | 43 | public SqlSession openSession() { 44 | return getSqlSessionFactory().openSession(); 45 | } 46 | 47 | public void closeSession(SqlSession sqlSession) { 48 | if (sqlSession != null) { 49 | try { 50 | sqlSession.close(); 51 | } catch (Exception e) { 52 | System.err.println("failed to close sqlSession:" + e.getMessage()); 53 | throw e; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tquant-storage/src/main/java/com/tquant/storage/dao/ContractDAO.java: -------------------------------------------------------------------------------- 1 | package com.tquant.storage.dao; 2 | 3 | import com.tquant.core.model.data.Contract; 4 | import com.tquant.storage.mapper.ContractMapper; 5 | import java.util.List; 6 | import org.apache.ibatis.session.SqlSession; 7 | 8 | /** 9 | * Description: 10 | * 11 | * @author kevin 12 | * @date 2022/03/04 13 | */ 14 | public class ContractDAO extends BaseDAO { 15 | 16 | private static SqlSession sqlSession = new BaseDAO().openSession(); 17 | 18 | public void saveContract(Contract contract) { 19 | sqlSession.getMapper(ContractMapper.class).saveContract(contract); 20 | sqlSession.commit(); 21 | } 22 | 23 | public List queryContracts() { 24 | return sqlSession.getMapper(ContractMapper.class).queryContracts(); 25 | } 26 | 27 | public Contract queryContract(String identifier) { 28 | return sqlSession.getMapper(ContractMapper.class).queryContract(identifier); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tquant-storage/src/main/java/com/tquant/storage/dao/TickDAO.java: -------------------------------------------------------------------------------- 1 | package com.tquant.storage.dao; 2 | 3 | import com.tquant.core.model.data.Tick; 4 | import com.tquant.storage.mapper.TickMapper; 5 | import java.time.LocalDateTime; 6 | import java.util.List; 7 | import org.apache.ibatis.session.SqlSession; 8 | 9 | /** 10 | * Description: 11 | * todo 实现tick查询 12 | * 13 | * @author kevin 14 | * @date 2022/08/02 15 | */ 16 | public class TickDAO extends BaseDAO { 17 | private static SqlSession sqlSession = new BaseDAO().openSession(); 18 | 19 | public void saveTick(Tick tick) { 20 | sqlSession.getMapper(TickMapper.class).saveTick(tick); 21 | sqlSession.commit(); 22 | } 23 | 24 | public List queryTicks(String symbol, LocalDateTime start, LocalDateTime end) { 25 | return sqlSession.getMapper(TickMapper.class).queryTicksByDate(symbol, start, end); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tquant-storage/src/main/java/com/tquant/storage/mapper/BarMapper.java: -------------------------------------------------------------------------------- 1 | package com.tquant.storage.mapper; 2 | 3 | import com.tquant.core.model.data.Bar; 4 | import java.time.LocalDateTime; 5 | import java.util.List; 6 | import org.apache.ibatis.annotations.Insert; 7 | import org.apache.ibatis.annotations.Mapper; 8 | import org.apache.ibatis.annotations.Param; 9 | import org.apache.ibatis.annotations.Select; 10 | 11 | /** 12 | * Description: 13 | * 14 | * @author kevin 15 | * @date 2019/09/02 16 | */ 17 | @Mapper 18 | public interface BarMapper { 19 | 20 | String ALL_COLUMN = "symbol,period,open,high,low,close,volume,duration,amount,time,create_time"; 21 | 22 | @Insert("insert into bar("+ALL_COLUMN+") value (#{symbol},#{period},#{open},#{high},#{low},#{close},#{volume},#{duration},#{amount},#{time},now())") 23 | void saveBar(Bar bar); 24 | 25 | @Select("select " + ALL_COLUMN + " from bar where symbol=#{symbol} limit #{limit}") 26 | List queryBars(@Param("symbol") String symbol, @Param("limit") int limit); 27 | 28 | @Select("select " 29 | + ALL_COLUMN 30 | + " from bar where symbol=#{symbol} and period=#{period} and time>=#{beginDate} and time<=#{endDate}") 31 | List queryBarsByDate(@Param("symbol") String symbol,@Param("period") String period, @Param("beginDate") LocalDateTime beginDate, 32 | @Param("endDate") LocalDateTime endDate); 33 | } 34 | -------------------------------------------------------------------------------- /tquant-storage/src/main/java/com/tquant/storage/mapper/ContractMapper.java: -------------------------------------------------------------------------------- 1 | package com.tquant.storage.mapper; 2 | 3 | import com.tquant.core.model.data.Contract; 4 | import java.util.List; 5 | import org.apache.ibatis.annotations.Insert; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Param; 8 | import org.apache.ibatis.annotations.Select; 9 | 10 | /** 11 | * Description: 12 | * 13 | * @author kevin 14 | * @date 2022/03/04 15 | */ 16 | @Mapper 17 | public interface ContractMapper { 18 | 19 | String SAVE_CONTRACT_SQL = "insert into contract(identifier,symbol,sec_type,currency,exchange,market,expiry," 20 | + "contract_month,strike,multiplier,`right`,min_tick,lot_size,create_time) " 21 | + "values (#{identifier},#{symbol},#{secType},#{currency},#{exchange},#{market},#{expiry}," 22 | + "#{contractMonth},#{strike},#{multiplier},#{right},#{minTick},#{lotSize},now())" 23 | + "ON DUPLICATE KEY UPDATE " 24 | + " identifier=VALUES(identifier),symbol=VALUES(symbol)"; 25 | 26 | String QUERY_CONTRACTS_SQL = 27 | "select identifier,symbol,sec_type,currency,exchange,market,expiry,contract_month,strike," 28 | + "multiplier,`right`,min_tick,lot_size,create_time from contract"; 29 | 30 | String QUERY_CONTRACT_SQL = "select identifier,symbol,sec_type,currency,exchange,market,expiry,contract_month,strike," 31 | + "multiplier,`right`,min_tick,lot_size,create_time from contract where identifier=#{identifier} limit 1"; 32 | 33 | @Insert(SAVE_CONTRACT_SQL) 34 | void saveContract(Contract bar); 35 | 36 | @Select(QUERY_CONTRACTS_SQL) 37 | List queryContracts(); 38 | 39 | @Select(QUERY_CONTRACT_SQL) 40 | Contract queryContract(@Param("identifier") String identifier); 41 | } 42 | -------------------------------------------------------------------------------- /tquant-storage/src/main/java/com/tquant/storage/mapper/TickMapper.java: -------------------------------------------------------------------------------- 1 | package com.tquant.storage.mapper; 2 | 3 | import com.tquant.core.model.data.Tick; 4 | import java.time.LocalDateTime; 5 | import java.util.List; 6 | import org.apache.ibatis.annotations.Insert; 7 | import org.apache.ibatis.annotations.Mapper; 8 | import org.apache.ibatis.annotations.Param; 9 | import org.apache.ibatis.annotations.Select; 10 | 11 | /** 12 | * Description: 13 | * 14 | * @author kevin 15 | * @date 2022/08/02 16 | */ 17 | @Mapper 18 | public interface TickMapper { 19 | 20 | String ALL_COLUMN = "symbol,volume,amount,latest_price,latest_volume,latest_time,time,type,create_time"; 21 | 22 | @Insert("insert into tick(" 23 | + ALL_COLUMN 24 | + ") value (#{symbol},#{volume},#{amount},#{latestPrice},#{latestVolume},#{latestTime},#{time},#{type},now())") 25 | void saveTick(Tick tick); 26 | 27 | @Select("select " 28 | + ALL_COLUMN 29 | + " from tick where symbol=#{symbol} and latest_time>=#{beginDate} and time<=#{endDate}") 30 | List queryTicksByDate(@Param("symbol") String symbol, @Param("beginDate") LocalDateTime beginDate, 31 | @Param("endDate") LocalDateTime endDate); 32 | } 33 | -------------------------------------------------------------------------------- /tquant-storage/src/main/java/com/tquant/storage/typehandler/DurationTypeHandler.java: -------------------------------------------------------------------------------- 1 | package com.tquant.storage.typehandler; 2 | 3 | import java.sql.CallableStatement; 4 | import java.sql.PreparedStatement; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | import java.time.Duration; 8 | import org.apache.ibatis.type.BaseTypeHandler; 9 | import org.apache.ibatis.type.JdbcType; 10 | import org.apache.ibatis.type.MappedJdbcTypes; 11 | 12 | /** 13 | * Description: 14 | * 15 | * @author kevin 16 | * @date 2019/09/03 17 | */ 18 | @MappedJdbcTypes(JdbcType.BIGINT) 19 | public class DurationTypeHandler extends BaseTypeHandler { 20 | 21 | @Override 22 | public void setNonNullParameter(PreparedStatement ps, int i, Duration parameter, JdbcType jdbcType) 23 | throws SQLException { 24 | ps.setLong(i, parameter.getSeconds()); 25 | } 26 | 27 | @Override 28 | public Duration getNullableResult(ResultSet rs, String columnName) throws SQLException { 29 | long seconds = rs.getLong(columnName); 30 | if (rs.wasNull()) { 31 | return null; 32 | } else { 33 | return Duration.ofSeconds(seconds); 34 | } 35 | } 36 | 37 | @Override 38 | public Duration getNullableResult(ResultSet rs, int columnIndex) throws SQLException { 39 | long seconds = rs.getLong(columnIndex); 40 | if (rs.wasNull()) { 41 | return null; 42 | } else { 43 | return Duration.ofSeconds(seconds); 44 | } 45 | } 46 | 47 | @Override 48 | public Duration getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { 49 | long seconds = cs.getLong(columnIndex); 50 | if (cs.wasNull()) { 51 | return null; 52 | } else { 53 | return Duration.ofSeconds(seconds); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tquant-storage/src/main/resources/datasource/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tquant-storage/src/main/resources/datasource/mysql_table_create.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE tiger_quant; 2 | USE tiger_quant; 3 | 4 | CREATE TABLE `bar` ( 5 | `id` int NOT NULL AUTO_INCREMENT, 6 | `symbol` varchar(20) NOT NULL DEFAULT '0', 7 | `duration` bigint NOT NULL DEFAULT '0', 8 | `period` varchar(10) DEFAULT NULL, 9 | `open` double(15,4) NOT NULL DEFAULT '0.0000', 10 | `high` double(15,4) NOT NULL DEFAULT '0.0000', 11 | `low` double(15,4) NOT NULL DEFAULT '0.0000', 12 | `close` double(15,4) NOT NULL DEFAULT '0.0000', 13 | `volume` int NOT NULL DEFAULT '0', 14 | `amount` double(15,4) NOT NULL DEFAULT '0.0000', 15 | `time` timestamp NULL DEFAULT NULL, 16 | `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 17 | PRIMARY KEY (`id`), 18 | KEY `idx_create_time` (`create_time`) 19 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 20 | 21 | CREATE TABLE `contract` ( 22 | `id` int NOT NULL AUTO_INCREMENT, 23 | `identifier` varchar(40) NOT NULL, 24 | `name` varchar(50) DEFAULT NULL, 25 | `symbol` varchar(20) NOT NULL DEFAULT '', 26 | `sec_type` varchar(20) DEFAULT NULL, 27 | `currency` varchar(10) DEFAULT NULL, 28 | `exchange` varchar(10) DEFAULT NULL, 29 | `market` varchar(10) DEFAULT NULL, 30 | `expiry` varchar(10) DEFAULT NULL, 31 | `contract_month` varchar(10) DEFAULT NULL, 32 | `strike` double DEFAULT NULL, 33 | `multiplier` double DEFAULT NULL, 34 | `right` varchar(10) DEFAULT NULL, 35 | `min_tick` double DEFAULT NULL, 36 | `lot_size` int DEFAULT '0', 37 | `create_time` timestamp NOT NULL DEFAULT '2018-01-01 10:00:00', 38 | `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 39 | PRIMARY KEY (`id`), 40 | UNIQUE KEY `uniq_idx_identifier` (`identifier`), 41 | KEY `idx_symbol` (`symbol`), 42 | KEY `idx_create_time` (`create_time`) 43 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 44 | 45 | CREATE TABLE `tick` ( 46 | `id` bigint NOT NULL AUTO_INCREMENT, 47 | `seq_no` bigint DEFAULT NULL, 48 | `symbol` varchar(20) NOT NULL DEFAULT '0', 49 | `volume` int NOT NULL DEFAULT '0', 50 | `amount` double(15,4) DEFAULT '0.0000', 51 | `type` varchar(10) DEFAULT NULL, 52 | `latest_price` double DEFAULT NULL, 53 | `latest_volume` double DEFAULT NULL, 54 | `latest_time` datetime(3) DEFAULT NULL, 55 | `time` bigint DEFAULT NULL, 56 | `bid_price` double DEFAULT NULL, 57 | `bid_size` bigint DEFAULT NULL, 58 | `ask_price` double DEFAULT NULL, 59 | `ask_size` bigint DEFAULT NULL, 60 | `open` double(15,4) DEFAULT '0.0000', 61 | `high` double(15,4) DEFAULT '0.0000', 62 | `low` double(15,4) DEFAULT '0.0000', 63 | `close` double(15,4) DEFAULT '0.0000', 64 | `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 65 | PRIMARY KEY (`id`), 66 | KEY `idx_symbol_time` (`symbol`) 67 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 --------------------------------------------------------------------------------