├── .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 extends StockData> 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 extends StockData> 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
--------------------------------------------------------------------------------