├── .gitignore
├── README.md
├── bin
├── cli.py
└── trader.py
├── pom.xml
└── src
├── main
├── java
│ └── net
│ │ └── jquant
│ │ ├── Indicators.java
│ │ ├── Main.java
│ │ ├── Quants.java
│ │ ├── common
│ │ ├── Constants.java
│ │ ├── Context.java
│ │ ├── DateRange.java
│ │ ├── Pair.java
│ │ ├── ParallelProcesser.java
│ │ ├── StockConstants.java
│ │ ├── StockDataParseException.java
│ │ └── Utils.java
│ │ ├── downloader
│ │ ├── AjaxDownloader.java
│ │ ├── BasicDownloader.java
│ │ ├── Downloader.java
│ │ ├── JsonpParser.java
│ │ └── THSJSDownloader.java
│ │ ├── model
│ │ ├── ArticleType.java
│ │ ├── Bar.java
│ │ ├── BoardType.java
│ │ ├── PeriodType.java
│ │ ├── StockBlock.java
│ │ ├── StockData.java
│ │ ├── StockMarketType.java
│ │ ├── StockSlice.java
│ │ ├── Symbol.java
│ │ └── Tick.java
│ │ ├── provider
│ │ ├── DailyDataProvider.java
│ │ ├── FinanceDataProvider.java
│ │ ├── MinuteDataProvider.java
│ │ ├── MoneyFlowDataProvider.java
│ │ ├── Provider.java
│ │ ├── RealTimeDataProvider.java
│ │ ├── ReferenceDataProvider.java
│ │ ├── ReportDataProvider.java
│ │ ├── StockIndexDataProvider.java
│ │ ├── TickDataProvider.java
│ │ └── TopListDataProvider.java
│ │ ├── strategy
│ │ ├── KellyFormula.java
│ │ ├── Strategy.java
│ │ ├── StrategyUtils.java
│ │ ├── TDXFunction.java
│ │ └── TStragegy.java
│ │ ├── tools
│ │ ├── Analyzer.java
│ │ ├── Conditions.java
│ │ ├── SharpeRatio.java
│ │ ├── Sleeper.java
│ │ ├── SortinoRatio.java
│ │ ├── StockCategory.java
│ │ ├── StockList.java
│ │ ├── StockPool.java
│ │ ├── Suggest.java
│ │ └── tableformat
│ │ │ ├── AbstractTableFormatter.java
│ │ │ ├── SimpleTableFormatter.java
│ │ │ └── TableFormatter.java
│ │ └── trade
│ │ ├── BackTester.java
│ │ ├── Commission.java
│ │ ├── FixedSlippage.java
│ │ ├── Order.java
│ │ ├── OrderStatus.java
│ │ ├── OrderStyle.java
│ │ ├── OrderType.java
│ │ ├── Portfolio.java
│ │ ├── Position.java
│ │ ├── PriceRelatedSlippage.java
│ │ ├── Record.java
│ │ ├── RiskAnalysis.java
│ │ ├── Slippage.java
│ │ ├── Trade.java
│ │ └── TradingSystem.java
└── resources
│ └── log4j.properties
└── test
└── java
└── net
└── jquant
├── common
└── UtilsTest.java
├── model
└── StockDataTest.java
├── provider
├── FinanceDataProviderTest.java
└── ProviderTest.java
└── tools
└── StockListTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Java template
3 | *.class
4 |
5 | # Mobile Tools for Java (J2ME)
6 | .mtj.tmp/
7 |
8 | # Package Files #
9 | *.jar
10 | *.war
11 | *.ear
12 |
13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
14 | hs_err_pid*
15 |
16 | *.iml
17 | .idea
18 | *.log
19 | *.json
20 | target
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JQuant
2 |
3 | # 项目迁移
4 |
5 | JQuant已经停止更新,请使用全新的SQuant项目,新项目是JQuant的升级版本,包含JQuant所有功能,并在JQuant基础上增加了回测和交易等功能,使用scala开发与java兼容
6 |
7 | 新项目地址:https://github.com/eryk/squant
8 |
9 | # [changelog](https://github.com/eryk/JQuant/wiki#changelog)
10 |
11 | # Maven依赖
12 |
13 | ```xml
14 |
15 |
16 | net.jquant
17 | JQuant
18 | 0.1
19 |
20 |
21 |
22 |
23 |
24 | oss
25 | https://oss.sonatype.org/content/groups/public/
26 |
27 |
28 | ```
29 |
30 | # 示例
31 |
32 | ```java
33 | package quant.fans;
34 |
35 | import quant.fans.model.StockData;
36 |
37 | import java.util.List;
38 |
39 | public class Main {
40 |
41 | public static void main(String[] args) {
42 | Quants quants = new Quants();
43 | //获取股票列表
44 | List list = quants.data.stockList();
45 |
46 | for (String stock : list) {
47 | //StockData代表一个时间片的数据,例如日线级别,每个StockData为一天收盘后的股票数据
48 | List stockDatas = quants.data.dailyData(stock);
49 | //剔除交易数据小于60天的股票
50 | if (stockDatas == null || stockDatas.size() < 60) {
51 | continue;
52 | }
53 | //indicator包含常用指标的计算
54 | quants.indicator.macd(stockDatas);
55 | //获取5、10、20、30、40、60均线,也可以通过sma(stockDatas,ma)获取指定时间间隔的均线
56 | quants.indicator.sma(stockDatas);
57 | quants.indicator.boll(stockDatas);
58 | quants.indicator.kdj(stockDatas);
59 | //strategy包含简单的策略计算,例如macd金叉
60 | quants.strategy.macdCross(stockDatas);
61 | quants.strategy.kdjCross(stockDatas);
62 | quants.strategy.goldenSpider(stockDatas);
63 | quants.strategy.bollThroat(stockDatas);
64 | for (StockData stockData : stockDatas) {
65 | System.out.println(stockData);
66 | }
67 | }
68 | }
69 | }
70 | ```
71 |
72 | # API
73 |
74 | ## 初始化
75 |
76 | > Quants quants = new Quants();
77 |
78 | `StockData`表示股票的一个时间片的数据,继承自LinkedHashMap,存储属性名称和double类型数值。
79 |
80 | ```java
81 | StockData stockData = Provider.realtimeData("000001");
82 | System.out.println("股票名称:" + stockData.name);
83 | System.out.println("股票代码:" + stockData.symbol);
84 | for(Map.Entry data : stockData.entrySet()){
85 | System.out.println(data.getKey() + "=" + data.getValue());
86 | }
87 | ```
88 |
89 | 不同Provider返回的StockData的数据项不同,具体信息查询[字段说明](https://github.com/eryk/JQuant/wiki/StockData%E5%AD%97%E6%AE%B5%E8%AF%B4%E6%98%8E)
90 |
91 | ## 股票数据 quants.data
92 |
93 | net.jquant.provider包提供了股票相关的数据获取类
94 |
95 | ### Provider列表如下:
96 |
97 | * DailyDataProvider:日线级别数据
98 | * MinuteDataProvider:分钟级别股票数据,可获得5、15、30、60分钟级别股票数据
99 | * RealTimeDataProvider:实时股票数据
100 | * StockIndexDataProvider:指数实时行情数据
101 | * TickDataProvider:股票逐笔数据
102 | * TopListDataProvider:龙虎榜数据
103 | * ReportDataProvider:研报数据
104 | * ReferenceDataProvider:分红数据
105 | * MoneyFlowDataProvider:股票资金流数据
106 | * FinanceDataProvider:个股财务报表数据
107 |
108 | > net.jquant.provider.Provider类里的static方法汇总了全部provider方法,一般情况,使用Provider类就可以满足数据查询需求。
109 |
110 | ## 股票列表 quants.stocks
111 |
112 | `StockList` 用于获取股票列表,并提供一些过滤和处理接口对股票进行筛选。
113 |
114 | ## 指标计算 quants.indicator
115 |
116 | `net.jquant。Indicators` 是对[Ta-lib](http://ta-lib.org/function.html)库的封装,提供常用指标计算,返回StockData list对象。同时也提供了一些Ta-lib没有的指标计算。
117 |
118 | 支持的指标包括:
119 |
120 | * sma:简单移动平均线
121 | * ema:指数移动平均线
122 | * dma:平均线差
123 | * macd:指数平滑异同平均线
124 | * boll:布林线
125 | * kdj:随机指标
126 | * rsi:强弱指标
127 | * sar:抛物线指标或停损转向操作点指标
128 | * adx:平均趋向指数
129 | * adxr:趋向指标
130 | * cci:顺势指标
131 | * mfi:资金流量指标
132 | * obv:能量潮又称为平衡交易量
133 | * roc:变动率指标
134 | * rocP:Rate of change Percentage: (price-prevPrice)/prevPrice
135 | * trix:三重指数平滑平均线
136 | * willR:威廉指标
137 | * ad:收集派发摆荡指标
138 | * aroon:阿隆指标
139 | * aroonOsc:Aroon Oscillator
140 | * bop:均势指标
141 | * kama:适应性移动平均线
142 | * trima:三角移动平均线
143 |
144 | ## 策略计算 quants.strategy
145 |
146 | * `StragegyUtils` 常用策略
147 | * `TDXFunction` 通达信常用指标
148 |
--------------------------------------------------------------------------------
/bin/cli.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import anyjson as json
4 | import click
5 | import dill
6 |
7 | import easytrader
8 |
9 | ACCOUNT_OBJECT_FILE = 'account.session'
10 |
11 |
12 | @click.command()
13 | @click.option('--use', help='指定券商 [ht, yjb, yh]')
14 | @click.option('--prepare', type=click.Path(exists=True), help='指定登录账户文件路径')
15 | @click.option('--get', help='调用 easytrader 中对应的变量')
16 | @click.option('--do', help='调用 easytrader 中对应的函数名')
17 | @click.option('--debug', default=False, help='是否输出 easytrader 的 debug 日志')
18 | @click.argument('params', nargs=-1)
19 | def main(prepare, use, do, get, params, debug):
20 | if get is not None:
21 | do = get
22 | if prepare is not None and use in ['ht', 'yjb', 'yh', 'gf', 'xq']:
23 | user = easytrader.use(use, debug)
24 | user.prepare(prepare)
25 | with open(ACCOUNT_OBJECT_FILE, 'wb') as f:
26 | dill.dump(user, f)
27 | if do is not None:
28 | with open(ACCOUNT_OBJECT_FILE, 'rb') as f:
29 | user = dill.load(f)
30 |
31 | if len(params) > 0:
32 | result = getattr(user, do)(*params)
33 | else:
34 | result = getattr(user, do)
35 |
36 | json_result = json.dumps(result)
37 | click.echo(json_result)
38 |
39 |
40 | if __name__ == '__main__':
41 | main()
--------------------------------------------------------------------------------
/bin/trader.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | #coding:utf-8
3 |
4 | import easytrader
5 |
6 | user = easytrader.use('yjb',debug=False)
7 | user.prepare('yjb.json')
8 |
9 |
10 | def balance():
11 | # 获取资金状况
12 | b = user.balance
13 | return b[0]
14 |
15 | def position():
16 | # 获取持仓
17 | p = user.position
18 | return p[0]
19 |
20 | def entrust():
21 | # 获取今日委托单
22 | e = user.entrust
23 | return e[0]
24 |
25 | def buy(code,price,amount):
26 | # 买入
27 | ret = user.buy(code, price=price, amount=amount)
28 | return ret[0]
29 |
30 | def sell(code,price,amount):
31 | # 卖出
32 | ret = user.sell(code, price=price, amount=amount)
33 | return ret[0]
34 |
35 | def cancel_entrust(entrust_no,stock_code):
36 | # 撤单
37 | ret = user.cancel_entrust(entrust_no,stock_code)
38 | return ret[0]
39 |
40 | def deal():
41 | # 查询当日成交
42 | ret = user.current_deal
43 | return ret[0]
44 |
45 | def ipo_limit(stock_code):
46 | # 查询新股申购额度申购上限
47 | ret = user.get_ipo_limit(stock_code)
48 | return ret
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | net.jquant
8 | JQuant
9 | 0.2
10 |
11 | JQuant
12 | quant tool
13 | https://github.com/eryk/JQuant
14 |
15 |
16 |
17 | The Apache License, Version 2.0
18 | http://www.apache.org/licenses/LICENSE-2.0.txt
19 |
20 |
21 |
22 |
23 |
24 | xuqi
25 | xuqi86@gmail.com
26 |
27 |
28 |
29 |
30 | https://github.com/eryk/JQuant
31 | git@github.com:eryk/JQuant.git
32 | git@github.com:eryk/JQuant.git
33 |
34 |
35 |
36 | GitHub Issues
37 | https://github.com/eryk/JQuant/issues
38 |
39 |
40 |
41 |
42 | ossrh
43 | https://oss.sonatype.org/content/repositories/snapshots
44 |
45 |
46 | ossrh
47 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
48 |
49 |
50 |
51 |
52 | 1.8
53 | 1.8
54 | 1.8
55 | 1.8
56 | true
57 | true
58 |
59 |
60 |
61 |
62 | junit
63 | junit
64 | 4.12
65 | test
66 |
67 |
68 | org.mockito
69 | mockito-core
70 | 1.10.19
71 | test
72 |
73 |
74 | com.google.guava
75 | guava
76 | 19.0
77 |
78 |
79 |
80 | org.slf4j
81 | slf4j-api
82 | 1.7.21
83 |
84 |
85 |
86 | org.slf4j
87 | slf4j-log4j12
88 | 1.7.21
89 |
90 |
91 |
92 | org.jsoup
93 | jsoup
94 | 1.8.3
95 |
96 |
97 | joda-time
98 | joda-time
99 | 2.8.1
100 |
101 |
102 | com.tictactec
103 | ta-lib
104 | 0.4.0
105 |
106 |
107 | com.mashape.unirest
108 | unirest-java
109 | 1.4.6
110 |
111 |
112 | com.google.code.gson
113 | gson
114 | 2.7
115 |
116 |
117 | org.seleniumhq.selenium
118 | selenium-java
119 | 2.47.1
120 |
121 |
122 | commons-io
123 | commons-io
124 | 2.5
125 |
126 |
127 | org.yaml
128 | snakeyaml
129 | 1.17
130 |
131 |
132 | org.apache.commons
133 | commons-math3
134 | 3.5
135 |
136 |
137 | org.xerial.snappy
138 | snappy-java
139 | 1.1.2.6
140 |
141 |
142 |
143 |
144 |
145 |
146 | org.apache.maven.plugins
147 | maven-gpg-plugin
148 | 1.5
149 |
150 |
151 | sign-artifacts
152 | verify
153 |
154 | sign-and-deploy-file
155 |
156 |
157 |
158 |
159 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
160 | ossrh
161 | pom.xml
162 | target/JQuant-0.1-javadoc.jar
163 | target/JQuant-0.1.jar
164 | target/JQuant-0.1-sources.jar
165 |
166 |
167 |
168 | org.apache.maven.plugins
169 | maven-source-plugin
170 | 2.2.1
171 |
172 |
173 | attach-sources
174 |
175 | jar-no-fork
176 |
177 |
178 |
179 |
180 |
181 | org.apache.maven.plugins
182 | maven-javadoc-plugin
183 | 2.9.1
184 |
185 |
186 | attach-javadocs
187 |
188 | jar
189 |
190 |
191 |
192 |
193 |
194 | org.apache.maven.plugins
195 | maven-compiler-plugin
196 | 3.0
197 |
198 | 1.8
199 | 1.8
200 |
201 |
202 |
203 |
204 |
205 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/Main.java:
--------------------------------------------------------------------------------
1 | package net.jquant;
2 |
3 | import net.jquant.common.StockDataParseException;
4 | import net.jquant.model.StockData;
5 |
6 | import java.util.List;
7 |
8 | public class Main {
9 |
10 | public static void main(String[] args) throws StockDataParseException {
11 | Quants quants = new Quants();
12 | //获取股票列表
13 | List list = quants.data.stockList();
14 |
15 | for (String stock : list) {
16 | //StockData代表一个时间片的数据,例如日线级别,每个StockData为一天收盘后的股票数据
17 | List stockDatas = quants.data.dailyData(stock);
18 | //剔除交易数据小于60天的股票
19 | if (stockDatas == null || stockDatas.size() < 60) {
20 | continue;
21 | }
22 | //indicator包含常用指标的计算
23 | quants.indicator.macd(stockDatas);
24 | //获取5、10、20、30、40、60均线,也可以通过sma(stockDatas,ma)获取指定时间间隔的均线
25 | quants.indicator.sma(stockDatas);
26 | quants.indicator.boll(stockDatas);
27 | quants.indicator.kdj(stockDatas);
28 | //strategy包含简单的策略计算,例如macd金叉
29 | quants.strategy.macdCross(stockDatas);
30 | quants.strategy.kdjCross(stockDatas);
31 | quants.strategy.goldenSpider(stockDatas);
32 | quants.strategy.bollThroat(stockDatas);
33 | for (StockData stockData : stockDatas) {
34 | System.out.println(stockData);
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/main/java/net/jquant/Quants.java:
--------------------------------------------------------------------------------
1 | package net.jquant;
2 |
3 | import net.jquant.provider.Provider;
4 | import net.jquant.tools.StockList;
5 | import net.jquant.strategy.StrategyUtils;
6 |
7 | import java.util.Map;
8 |
9 | public class Quants {
10 |
11 | /**
12 | * 获取股票数据接口
13 | */
14 | public final Provider data = new Provider();
15 |
16 | /**
17 | * 指标计算接口
18 | */
19 | public final Indicators indicator = new Indicators();
20 |
21 | /**
22 | * 策略计算接口
23 | */
24 | public final StrategyUtils strategy = new StrategyUtils();
25 |
26 | public final StockList stocks = new StockList();
27 | /**
28 | * 使用外部存储,支持:
29 | * 文件系统(csv)
30 | * redis
31 | * hbase
32 | *
33 | * @param config config
34 | * @return quants instance
35 | */
36 | public Quants db(Map config) {
37 | //TODO
38 | return this;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/common/Constants.java:
--------------------------------------------------------------------------------
1 | package net.jquant.common;
2 |
3 | import java.nio.charset.Charset;
4 |
5 | /**
6 | * Created by eryk on 2015/7/20.
7 | */
8 | public class Constants {
9 | public static final Charset UTF8 = Charset.forName("UTF-8");
10 |
11 | public static final String DATABASE_POOL_SIZE = "database.pool.size";
12 | public static final String SCHEDULE_POOL_SIZE = "schedule.pool.size";
13 | public static final String THREAD_POOL_SIZE = "thread.pool.size";
14 |
15 | public static final String BROKER_POOL_SIZE = "broker.pool.size";
16 | public static final String BROKER_CHECK_INTERVAL = "broker.check.interval";
17 |
18 | public static final String TRADER_ACCOUNT_ID = "trader.account.id";
19 | public static final String TRADER_TRADING_DAY_COUNT = "trader.trading.day.count";
20 | public static final String TRADER_POOL_SIZE = "trader.pool.size";
21 | public static final String TRADER_EXCEL_BASE_DIR = "trader.execl.base.dir";
22 |
23 | public static String NETEASE_DATE_STYLE = "yyyy-MM-dd";
24 |
25 | public static String IFENG_DATE_STYLE = "yyyy-MM-dd";
26 |
27 | public static String BISNESS_DATA_FORMAT = "yyyyMMdd";
28 |
29 | public static final String MINUTE_ROWKEY_DATA_FORMAT = "yyyyMMddHHmm";
30 |
31 | public static final String SECOND_ROWKEY_DATA_FORMAT = "yyyyMMddHHmmss";
32 |
33 | public static final String MARKET_START_DATE = "19901219";
34 |
35 | public static final String SCHEDULER_JOBS = "jobs";
36 |
37 | public static final String SCHEDULER_JOB_CLASSNAME = "classname";
38 |
39 | public static final String SCHEDULER_JOB_CRON = "cron";
40 |
41 | public static int SECOND = 1 * 1000;
42 | public static int MINUTE = 60 * SECOND;
43 | public static int MINUTES_5 = 5 * MINUTE;
44 | public static int MINUTES_15 = 15 * MINUTE;
45 | public static int MINUTES_30 = 30 * MINUTE;
46 | public static int MINUTES_60 = 60 * MINUTE;
47 | public static int DAY = 1440 * MINUTE;
48 | public static int WEEK = 7 * 1440 * MINUTE;
49 |
50 | /**
51 | * table definition
52 | */
53 | public static final String TABLE_STOCK_5_MINUTES = "stocks_data_5mins";
54 |
55 | public static final String TABLE_STOCK_15_MINUTES = "stocks_data_15mins";
56 |
57 | public static final String TABLE_STOCK_30_MINUTES = "stocks_data_30mins";
58 |
59 | public static final String TABLE_STOCK_60_MINUTES = "stocks_data_60mins";
60 |
61 | public static final String TABLE_STOCK_DAILY = "stocks_data_daily";
62 |
63 | public static final String TABLE_STOCK_WEEK = "stocks_data_week";
64 |
65 | public static final String TABLE_STOCK_MONTH = "stocks_data_month";
66 |
67 | public static final String TABLE_STOCK_TICK = "stocks_tick_data";
68 |
69 | public static final byte[] TABLE_CF_DATA = "d".getBytes();
70 |
71 | public static final String TABLE_STOCK_INFO = "stocks_info";
72 |
73 | public static final byte[] TABLE_CF_INFO = "i".getBytes();
74 |
75 | public static final String TABLE_ARTICLE = "stocks_article";
76 |
77 | public static final byte[] TABLE_CF_ARTICLE = "a".getBytes();
78 |
79 | public static final String[] ACCOUNT_HEADER = {"交易时间","起始资产","期末资产","交易盈亏","收益率","夏普比率","索提诺比率","操作正确率","最大单笔盈利","最大单笔亏损","平均每笔盈利","基准收益额","基准收益率","最大资产","最小资产","最大回撤","交易次数","盈利次数","亏损次数","持仓总时间","平均持仓时间"};
80 |
81 | public static final String[] ACCOUNT_COLUMN = {"date","start","end","pnl","pnlRate","sharpe","sortino","accuracy","maxEarnPerOp","maxLossPerOp","meanEarnPerOp","benchmarkBenfit","benchmarkBenfitPercent","max","min","drawdown","totalOperate","earnOperate","lossOperate","totalPositionDays","avgPositionDays"};
82 |
83 | public static final String[] POSITION_HEADER = {"代码","买入日期","卖出日期","成交量","成交额","买入价","卖出价","盈亏额"};
84 |
85 | public static final String[] POSITION_COLUMN = {"symbol","buyDate","sellDate","volume","amount","price","sellPrice","pnl"};
86 |
87 | /**
88 | * stock column
89 | */
90 | public static final byte[] CLOSE = "close".getBytes();
91 | public static final byte[] HIGH = "high".getBytes();
92 | public static final byte[] LOW = "low".getBytes();
93 | public static final byte[] OPEN = "open".getBytes();
94 | public static final byte[] LAST_CLOSE = "lastClose".getBytes();
95 | public static final byte[] CHANGE_AMOUNT = "changeAmount".getBytes();
96 | public static final byte[] CHANGE = "change".getBytes();
97 | public static final byte[] TURNOVER_RATE = "turnoverRate".getBytes();
98 | public static final byte[] VOLUME = "volume".getBytes();
99 | public static final byte[] AMOUNT = "amount".getBytes();
100 | public static final byte[] TOTAL_VALUE = "totalValue".getBytes();
101 | public static final byte[] MARKET_VALUE = "marketValue".getBytes();
102 | public static final byte[] AMPLITUDE = "amplitude".getBytes();
103 | public static final byte[] NAME = "name".getBytes();
104 | public static final byte[] STATUS = "status".getBytes();
105 |
106 | public static final byte[] AVG_COST = "avgCost".getBytes();
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/common/Context.java:
--------------------------------------------------------------------------------
1 | package net.jquant.common;
2 |
3 | import net.jquant.tools.StockList;
4 |
5 | public class Context {
6 | private StockList stockList;
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/common/DateRange.java:
--------------------------------------------------------------------------------
1 | package net.jquant.common;
2 |
3 | import com.google.common.collect.Lists;
4 | import org.joda.time.DateTime;
5 |
6 | import java.util.Date;
7 | import java.util.List;
8 |
9 | /**
10 | * start:今天往前的n个交易日,步长为天
11 | * stop:明天,因为hbase的stoprow是开区间
12 | */
13 | public class DateRange {
14 |
15 | private int dayCount;
16 |
17 | private DateRange() {
18 | }
19 |
20 | private DateRange(int dayCount) {
21 | this.dayCount = dayCount;
22 | }
23 |
24 | public static DateRange getRange(int dayCount) {
25 | return new DateRange(dayCount);
26 | }
27 |
28 | public static DateRange getRange(){
29 | return new DateRange(0);
30 | }
31 |
32 | public String start() {
33 | return start("yyyyMMdd");
34 | }
35 |
36 | public String start(String format) {
37 | DateTime dt = new DateTime();
38 | dt = addDays(dt, dayCount); //[)
39 | return dt.toString(format);
40 | }
41 |
42 | public Date startDate(String format){
43 | return Utils.str2Date(start(),format);
44 | }
45 |
46 | public Date startDate(){
47 | return startDate("yyyyMMdd");
48 | }
49 |
50 | private DateTime addDays(DateTime dateTime, int days) {
51 | DateTime dt = dateTime;
52 | for (int i = 0; i < days; i++) {
53 | dt = dt.plusDays(-1);
54 | if (dt.getDayOfWeek() == 7) {
55 | dt = dt.plusDays(-2);
56 | }
57 | }
58 | return dt;
59 | }
60 |
61 | public String stop() {
62 | return stop("yyyyMMdd");
63 | }
64 |
65 | public String stop(String format) {
66 | DateTime dt = new DateTime();
67 | dt = dt.plusDays(1);
68 | return dt.toString(format);
69 | }
70 |
71 | public Date stopDate(String format){
72 | return Utils.str2Date(stop(),format);
73 | }
74 |
75 | public Date stopDate(){
76 | return stopDate("yyyyMMdd");
77 | }
78 |
79 | /**
80 | * 获取从开始时间到结束时间的交易日期
81 | * @param format format style
82 | * @return date list
83 | */
84 | public List getDateList(String format){
85 | List dateList = Lists.newArrayListWithCapacity(dayCount+1);
86 | DateTime tmpDate = new DateTime(startDate());
87 | DateTime stop = new DateTime(stopDate());
88 | while(tmpDate.toDate().getTime() <=stop.toDate().getTime()){
89 | //只添加周一到周五
90 | if(tmpDate.getDayOfWeek()>=1 && tmpDate.getDayOfWeek()<=5){
91 | dateList.add(tmpDate.toString(format));
92 | }
93 | tmpDate = tmpDate.plusDays(1);
94 | }
95 | return dateList;
96 | }
97 |
98 | public List getDateList(){
99 | return getDateList("yyyyMMdd");
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/common/Pair.java:
--------------------------------------------------------------------------------
1 | package net.jquant.common;
2 |
3 | public class Pair {
4 | private K key;
5 | private V val;
6 |
7 | public Pair(K key, V val) {
8 | this.key = key;
9 | this.val = val;
10 | }
11 |
12 | public K getKey() {
13 | return key;
14 | }
15 |
16 | public V getVal() {
17 | return val;
18 | }
19 |
20 | @Override
21 | public String toString() {
22 | return "Pair{" +
23 | "key=" + key +
24 | ", val=" + val +
25 | '}';
26 | }
27 |
28 | @Override
29 | public boolean equals(Object o) {
30 | if (this == o) return true;
31 | if (!(o instanceof Pair)) return false;
32 |
33 | Pair pair = (Pair) o;
34 |
35 | if (!key.equals(pair.key)) return false;
36 | if (!val.equals(pair.val)) return false;
37 |
38 | return true;
39 | }
40 |
41 | @Override
42 | public int hashCode() {
43 | int result = key.hashCode();
44 | result = 31 * result + val.hashCode();
45 | return result;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/common/ParallelProcesser.java:
--------------------------------------------------------------------------------
1 | package net.jquant.common;
2 |
3 | import com.google.common.util.concurrent.MoreExecutors;
4 |
5 | import java.util.concurrent.*;
6 |
7 | public class ParallelProcesser {
8 |
9 | static volatile boolean isInit = false;
10 |
11 | static ScheduledExecutorService executorService;
12 |
13 | static ExecutorService threadPool;
14 |
15 | private static int executor_pool_size = 1;
16 | private static int schedule_pool_size = 1;
17 |
18 | public static synchronized void init(int scheduledPoolSize,int threadPoolSize) {
19 | if (executorService == null) {
20 | executorService = Executors.newScheduledThreadPool(scheduledPoolSize);
21 | }
22 | if(threadPool == null){
23 | threadPool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(threadPoolSize));
24 | }
25 | isInit = true;
26 | }
27 |
28 | public static void close() {
29 | Utils.closeThreadPool(executorService);
30 | Utils.closeThreadPool(threadPool);
31 | isInit = false;
32 | }
33 |
34 | public static void process(Runnable task){
35 | if(threadPool == null){
36 | init(schedule_pool_size,executor_pool_size);
37 | }
38 | threadPool.execute(task);
39 | }
40 |
41 | public static void schedule(Runnable runnable, int start, int period){
42 | if(executorService == null){
43 | init(schedule_pool_size,executor_pool_size);
44 | }
45 | executorService.scheduleAtFixedRate(runnable,start,period, TimeUnit.MINUTES);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/common/StockConstants.java:
--------------------------------------------------------------------------------
1 | package net.jquant.common;
2 |
3 | import com.google.common.collect.Lists;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * author: eryk
9 | * mail: xuqi86@gmail.com
10 | * date: 15-8-30.
11 | */
12 | public class StockConstants {
13 | public static String CLOSE = "close"; //最新价,收盘价
14 | public static String HIGH = "high"; //最高价
15 | public static String LOW = "low"; //最低价
16 | public static String OPEN = "open"; //开盘价
17 | public static String LAST_CLOSE = "lastClose"; //昨日收盘价
18 | public static String CHANGE_AMOUNT = "changeAmount"; //涨跌额 今天收盘价-昨天收盘价
19 | public static String CHANGE = "change"; //涨跌幅
20 | public static String TURNOVER_RATE = "turnoverRate"; //换手率
21 | public static String VOLUME = "volume"; //成交量,单位:手
22 | public static String AMOUNT = "amount"; //成交额,单位:万
23 | public static String TOTAL_VALUE = "totalValue"; //总市值,单位:亿
24 | public static String MARKET_VALUE = "marketValue"; //流通市值,单位:亿
25 | public static String AMPLITUDE = "amplitude"; //振幅
26 | public static String FACTOR = "factor"; //复权因子
27 |
28 | public static String PE = "PE"; //动态市盈率
29 |
30 | public static String POSITION="position";
31 | //macd指标
32 | public static String DIF = "dif";
33 | public static String DEA = "dea";
34 | public static String MACD = "macd";
35 | public static String MACD_CROSS = "macd_cross";
36 | //布林线指标
37 | public static String UPPER = "upper";
38 | public static String MID = "mid";
39 | public static String LOWER = "lower";
40 | public static String BOLL_SHRINK = "boll_shrink";
41 | //均线指标
42 | public static String CLOSE_MA5 = "close_ma5";
43 | public static String CLOSE_MA10 = "close_ma10";
44 | public static String CLOSE_MA13 = "close_ma13";
45 | public static String CLOSE_MA20 = "close_ma20";
46 | public static String CLOSE_MA30 = "close_ma30";
47 | public static String CLOSE_MA34 = "close_ma34";
48 | public static String CLOSE_MA40 = "close_ma40";
49 | public static String CLOSE_MA55 = "close_ma55";
50 | public static String CLOSE_MA60 = "close_ma60";
51 | public static String CLOSE_MA120 = "close_ma120";
52 |
53 | public static String MAX = "max";
54 | public static String MIN = "min";
55 |
56 | public static String AVERAGE_BOND = "average_bond";
57 |
58 | public static String GOLDEN_SPIDER = "golden_spider";
59 | public static String GOLDEN_SPIDER_RANGE = "golden_spider_range";
60 |
61 | public static String DATE_OF_DECLARATION = "dateOfDeclaration"; //公告日期
62 | public static String ANNUAL = "annual"; //分红年度
63 | public static String DATE_OF_RECORD = "dateOfRecord"; //股权登记日
64 | public static String DATE_OF_EX_DIVIDEND = "dateOfExDividend"; //除权除息日
65 |
66 | public static String STOCK_SHARES = "stockShares"; //送股
67 | public static String STOCK_TRANSFERRED = "stockTransferred"; //转增
68 | public static String STOCK_DIVIDEND = "stockDividend"; //派息
69 |
70 | public static String TYPE = "type";
71 |
72 | public static List DAILY = Lists.newArrayList(
73 | "close", //最新价,收盘价
74 | "high", //最高价
75 | "low", //最低价
76 | "open", //开盘价
77 | "lastClose", //昨日收盘价
78 | "changeAmount", //涨跌额
79 | "change", //涨跌幅
80 | "turnoverRate", //换手率
81 | "volume", //成交量,单位:手
82 | "amount", //成交额,单位:万
83 | "totalValue", //总市值,单位:亿
84 | "marketValue", //流通市值,单位:亿
85 | "amplitude" //振幅
86 | );
87 |
88 | public static int DAILY_SIZE = 15;
89 |
90 |
91 | public static List REALTIME = Lists.newArrayList(
92 | "marketType", //0 市场类型,沪市:1,深市:2
93 | "code", //1 证券代码
94 | "name", //2 证券名称
95 | "buy1", //3 买一
96 | "buy2", //4 买二
97 | "buy3", //5 买三
98 | "buy4", //6 买四
99 | "buy5", //7 买五
100 | "sell1", //8 卖一
101 | "sell2", //9 卖二
102 | "sell3", //10 卖三
103 | "sell4", //11 卖四
104 | "sell5", //12 卖五
105 | "buy1Volume", //13 买一手数
106 | "buy2Volume", //14 买二手数
107 | "buy3Volume", //15 买三手数
108 | "buy4Volume", //16 买四手数
109 | "buy5Volume", //17 买五手数
110 | "sell1Volume", //18 卖一手数
111 | "sell2Volume", //19 卖二手数
112 | "sell3Volume", //20 卖三手数
113 | "sell4Volume", //21 卖四手数
114 | "sell5Volume", //22 卖五手数
115 | "limitUp", //23 涨停价
116 | "limitDown", //24 跌停价
117 | "close", //25 最新价,收盘价
118 | "avgCost", //26 均价
119 | "changeAmount", //27 涨跌额
120 | "open", //28 开盘价
121 | "change", //29 涨跌幅
122 | "high", //30 最高价
123 | "volume", //31 成交量,单位:手
124 | "low", //32 最低价
125 | "", //33 未知
126 | "lastClose", //34 昨日收盘价
127 | "amount", //35 成交额,单位:万
128 | "quantityRelative", //36 量比
129 | "turnoverRate", //37 换手率
130 | "PE", //38 市盈率
131 | "outerDisc", //39 外盘,主动买,单位:手
132 | "innerDisc", //40 内盘,主动卖,单位:手
133 | "committeeThan", //41 委比,百分比
134 | "committeeSent", //42 委差
135 | "PB", //43 市净率
136 | "", //44 未知
137 | "marketValue", //45 流通市值,单位:亿
138 | "totalValue", //46 总市值,单位:亿
139 | "", //47 未知
140 | "", //48 未知
141 | "date", //49 时间
142 | "", //50 未知
143 | "", //51 未知
144 | "" //52 未知
145 | );
146 |
147 | public static List MONEYFLOW = Lists.newArrayList(
148 | "今日主力净流入", //0 今日主力净流入
149 | "主力净比", //1 主力净比
150 | "今日超大单净流入", //2 今日超大单净流入
151 | "超大单净比", //3 超大单净比
152 | "今日大单净流入", //4 今日大单净流入
153 | "大单净比", //5 大单净比
154 | "今日中单净流入", //6 今日中单净流入
155 | "中单净比", //7 中单净比
156 | "今日小单净流入", //8 今日小单净流入
157 | "小单净比", //9 小单净比
158 | "未知1", //10 未知
159 | "未知2", //11 未知
160 | "超大单:流入", //12 超大单:流入
161 | "超大单:流出", //13 超大单:流出
162 | "大单:流入", //14 大单:流入
163 | "大单:流出", //15 大单:流出
164 | "中单:流入", //16 中单:流入
165 | "中单:流出", //17 中单:流出
166 | "小单:流入", //17 小单:流入
167 | "小单:流出", //18 小单:流出
168 | "未知3", //19 未知
169 | "未知4" //20 未知
170 |
171 | );
172 |
173 | public static List MONEYFLOW_HIS = Lists.newArrayList(
174 | "date", //0 日期
175 | "close", //1 收盘价
176 | "change", //2 涨跌幅
177 | "主力净流入-净额", //3 主力净流入-净额
178 | "主力净流入-净占比", //4 主力净流入-净占比
179 | "超大单净流入-净额", //5 超大单净流入-净额
180 | "超大单净流入-净占比", //6 超大单净流入-净占比
181 | "大单净流入-净额", //7 大单净流入-净额
182 | "大单净流入-净占比", //8 大单净流入-净占比
183 | "中单净流入-净额", //9 中单净流入-净额
184 | "中单净流入-净占比", //10 中单净流入-净占比
185 | "小单净流入-净额", //11 小单净流入-净额
186 | "小单净流入-净占比" //12 小单净流入-净占比
187 | );
188 |
189 | public static List DAPAN_MONEYFLOW_HIS = Lists.newArrayList(
190 | "date", //0 日期
191 | "sh-close", //1 上证-收盘价
192 | "sh-change", //2 上证-涨跌幅
193 | "sz-close", //3 深证-收盘价
194 | "sz-change", //4 深证-涨跌幅
195 | "主力净流入-净额", //5 主力净流入-净额
196 | "主力净流入-净占比", //6 主力净流入-净占比
197 | "超大单净流入-净额", //7 超大单净流入-净额
198 | "超大单净流入-净占比", //8 超大单净流入-净占比
199 | "大单净流入-净额", //9 大单净流入-净额
200 | "大单净流入-净占比", //10 大单净流入-净占比
201 | "中单净流入-净额", //11 中单净流入-净额
202 | "中单净流入-净占比", //12 中单净流入-净占比
203 | "小单净流入-净额", //13 小单净流入-净额
204 | "小单净流入-净占比" //14 小单净流入-净占比
205 | );
206 |
207 | public static List INDUSTRY_MONEYFLOW = Lists.newArrayList(
208 | "num", //0 序号
209 | "symbol", //1 日期
210 | "name", //2 名称
211 | "change", //3 涨跌幅
212 | "主力净流入-净额", //4 主力净流入-净额
213 | "主力净流入-净占比", //5 主力净流入-净占比
214 | "超大单净流入-净额", //6 超大单净流入-净额
215 | "超大单净流入-净占比", //7 超大单净流入-净占比
216 | "大单净流入-净额", //8 大单净流入-净额
217 | "大单净流入-净占比", //9 大单净流入-净占比
218 | "中单净流入-净额", //10 中单净流入-净额
219 | "中单净流入-净占比", //11 中单净流入-净占比
220 | "小单净流入-净额", //12 小单净流入-净额
221 | "小单净流入-净占比" //13 小单净流入-净占比
222 | );
223 | }
224 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/common/StockDataParseException.java:
--------------------------------------------------------------------------------
1 | package net.jquant.common;
2 |
3 | public class StockDataParseException extends Exception {
4 | public StockDataParseException() {
5 | }
6 |
7 | public StockDataParseException(String message) {
8 | super(message);
9 | }
10 |
11 | public StockDataParseException(String message, Throwable cause) {
12 | super(message, cause);
13 | }
14 |
15 | public StockDataParseException(Throwable cause) {
16 | super(cause);
17 | }
18 |
19 | public StockDataParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
20 | super(message, cause, enableSuppression, writableStackTrace);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/downloader/AjaxDownloader.java:
--------------------------------------------------------------------------------
1 | package net.jquant.downloader;
2 |
3 | import org.openqa.selenium.WebDriver;
4 | import org.openqa.selenium.firefox.FirefoxDriver;
5 |
6 | /**
7 | * author: eryk
8 | * mail: xuqi86@gmail.com
9 | * date: 15-8-31.
10 | */
11 | public class AjaxDownloader{
12 |
13 | public static String download(String url) {
14 | WebDriver driver = new FirefoxDriver();
15 | driver.get(url);
16 | String source = driver.getPageSource();
17 | driver.close();
18 | return source;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/downloader/BasicDownloader.java:
--------------------------------------------------------------------------------
1 | package net.jquant.downloader;
2 |
3 | import com.google.common.base.Joiner;
4 | import com.google.common.collect.Maps;
5 | import com.mashape.unirest.http.HttpResponse;
6 | import com.mashape.unirest.http.Unirest;
7 | import com.mashape.unirest.http.exceptions.UnirestException;
8 | import org.apache.commons.io.IOUtils;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import java.io.BufferedReader;
13 | import java.io.IOException;
14 | import java.io.InputStream;
15 | import java.io.InputStreamReader;
16 | import java.net.URL;
17 | import java.net.URLConnection;
18 | import java.nio.charset.Charset;
19 | import java.util.List;
20 | import java.util.Map;
21 |
22 | /**
23 | * author: eryk
24 | * mail: xuqi86@gmail.com
25 | * date: 15-8-15.
26 | */
27 | public class BasicDownloader {
28 | private static final Logger LOG = LoggerFactory.getLogger(BasicDownloader.class);
29 |
30 | private static int RETRY_COUNT = 1;
31 |
32 | public static String download(String url,Map header){
33 | int retry = 0;
34 | while(retry<=RETRY_COUNT){
35 | try {
36 | HttpResponse response = null;
37 | if(header.size() > 0){
38 | response = Unirest.get(url).headers(header).asString();
39 | }else{
40 | response = Unirest.get(url).asString();
41 | }
42 | if(response.getStatus()==200){
43 | return response.getBody();
44 | }else{
45 | LOG.warn("status="+response.getStatus()+",url="+url);
46 | }
47 | retry++;
48 | } catch (UnirestException e) {
49 | retry++;
50 | LOG.error("retry="+retry+",fail to download from " + url);
51 | try {
52 | Thread.sleep(1000);
53 | } catch (InterruptedException e1) {
54 | LOG.error("fail to download from" + url);
55 | }
56 | }
57 | }
58 | return "";
59 | }
60 |
61 | public static String download(String url) {
62 | return download(url, Maps.newHashMap());
63 | }
64 |
65 | public static InputStream downloadStream(String url) {
66 | int retry = 0;
67 | while(retry<=RETRY_COUNT){
68 | try {
69 | HttpResponse response = Unirest.get(url).asString();
70 | if(response.getStatus()==200){
71 | return response.getRawBody();
72 | }else{
73 | LOG.warn("status="+response.getStatus()+",url="+url);
74 | }
75 | retry++;
76 | } catch (UnirestException e) {
77 | retry++;
78 | LOG.error("fail to download from " + url);
79 | }
80 | }
81 | return null;
82 | }
83 |
84 | public static String download(String url,String encoding){
85 | InputStream inputStream = downloadStream(url);
86 | if(inputStream!=null){
87 | try {
88 | List strings = IOUtils.readLines(inputStream, encoding);
89 | return Joiner.on("\n").join(strings);
90 | } catch (IOException e) {
91 | e.printStackTrace();
92 | }
93 | }
94 | return "";
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/downloader/Downloader.java:
--------------------------------------------------------------------------------
1 | package net.jquant.downloader;
2 |
3 | /**
4 | * author: eryk
5 | * mail: xuqi86@gmail.com
6 | * date: 15-8-15.
7 | */
8 | public class Downloader {
9 |
10 | public static String download(String url){
11 | return BasicDownloader.download(url);
12 | }
13 |
14 | public static String download(String url,String encoding){
15 | return BasicDownloader.download(url,encoding);
16 | }
17 |
18 | public static String downloadAjaxData(String url){
19 | return AjaxDownloader.download(url);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/downloader/JsonpParser.java:
--------------------------------------------------------------------------------
1 | package net.jquant.downloader;
2 |
3 | import com.google.common.base.Strings;
4 | import com.google.gson.Gson;
5 |
6 | import java.util.Map;
7 |
8 | public class JsonpParser {
9 |
10 | public static Map parser(String jsonStr) {
11 | if (Strings.isNullOrEmpty(jsonStr)) {
12 | return null;
13 | }
14 | String json = jsonStr.substring(jsonStr.indexOf("(") + 1 , jsonStr.length() -1);
15 | Gson gson = new Gson();
16 | return gson.fromJson(json, Map.class);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/downloader/THSJSDownloader.java:
--------------------------------------------------------------------------------
1 | package net.jquant.downloader;
2 |
3 | import com.google.common.collect.Maps;
4 |
5 | import java.util.Map;
6 |
7 | public class THSJSDownloader {
8 |
9 | public static Map download(String urlBase, String symbol){
10 | return download(String.format(urlBase,symbol));
11 | }
12 |
13 | public static Map download(String url){
14 | Map header = Maps.newHashMap();
15 | header.put("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36");
16 | String value = BasicDownloader.download(url,header);
17 | Map ret = JsonpParser.parser(value);
18 | if (ret.size() == 0) {
19 | return null;
20 | }
21 | return ret;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/model/ArticleType.java:
--------------------------------------------------------------------------------
1 | package net.jquant.model;
2 |
3 |
4 | import net.jquant.common.Utils;
5 |
6 | /**
7 | * author: eryk
8 | * mail: xuqi86@gmail.com
9 | * date: 15-8-19.
10 | */
11 | public enum ArticleType {
12 | FINANCIAL_STATEMENTS(0), //财务报表
13 | RESEARCH_REPORT(1), //研究报告
14 | NOTICE(2), //公告
15 | COMMENT(3), //评论
16 | NEWS(4); //新闻
17 |
18 | private byte[] type;
19 |
20 | ArticleType(int type){
21 | this.type = Utils.toBytes(type);
22 | }
23 |
24 | public byte[] getType(){
25 | return this.type;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/model/Bar.java:
--------------------------------------------------------------------------------
1 | package net.jquant.model;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * author: eryk
7 | * mail: xuqi86@gmail.com
8 | * date: 15-11-17.
9 | */
10 | public class Bar {
11 | private long id;
12 | private Date date; //close time
13 | private double open;
14 | private double high;
15 | private double low;
16 | private double close;
17 | private int volume;
18 | private double amount;
19 |
20 | public Bar(long id,Tick tick){
21 | this.id = id;
22 | this.date = tick.date;
23 | this.open = tick.price;
24 | this.high = tick.price;
25 | this.low = tick.price;
26 | this.close = tick.price;
27 | this.volume = tick.volume;
28 | this.amount = tick.amount;
29 | }
30 |
31 | public void addTick(Tick tick){
32 | this.date = tick.date;
33 | if(this.high tick.price){
37 | this.low = tick.price;
38 | }
39 | this.close = tick.price;
40 | this.volume += tick.volume;
41 | this.amount += tick.amount;
42 | }
43 |
44 | public void appendBar(Bar bar){
45 |
46 | }
47 |
48 | @Override
49 | public String toString() {
50 | return "Bar{" +
51 | "id=" + id +
52 | ", date=" + date +
53 | ", open=" + open +
54 | ", high=" + high +
55 | ", low=" + low +
56 | ", close=" + close +
57 | ", volume=" + volume +
58 | ", amount=" + amount +
59 | '}';
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/model/BoardType.java:
--------------------------------------------------------------------------------
1 | package net.jquant.model;
2 |
3 | /**
4 | * author: eryk
5 | * mail: xuqi86@gmail.com
6 | * date: 15-8-12.
7 | */
8 | public enum BoardType {
9 | MAIN, //主板
10 | SME, //中小板
11 | GEM, //创业板
12 | UNKNOW;
13 |
14 | public static BoardType getType(String symbol){
15 | if(symbol.startsWith("6") || symbol.startsWith("000")){
16 | return MAIN;
17 | }else if(symbol.startsWith("002")){
18 | return SME;
19 | }else if(symbol.startsWith("3")){
20 | return GEM;
21 | }else{
22 | return UNKNOW;
23 | }
24 | }
25 |
26 | public boolean isMatchType(String symbol){
27 | if(this.equals(MAIN)){
28 | return symbol.startsWith("6") || symbol.startsWith("000");
29 | }else if(this.equals(SME)){
30 | return symbol.startsWith("002");
31 | }else if(this.equals(GEM)){
32 | return symbol.startsWith("3");
33 | }
34 | return false;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/model/PeriodType.java:
--------------------------------------------------------------------------------
1 | package net.jquant.model;
2 |
3 |
4 | import net.jquant.common.Constants;
5 |
6 | /**
7 | * author: eryk
8 | * mail: xuqi86@gmail.com
9 | * date: 15-8-9.
10 | */
11 | public enum PeriodType {
12 | ONE_MIN("1"),
13 | FIVE_MIN("5"),
14 | FIFTEEN_MIN("15"),
15 | THIRTY_MIN("30"),
16 | SIXTY_MIN("60"),
17 | DAY("daily"),
18 | WEEK("week"),
19 | MONTH("month"),
20 | YEAR("year");
21 |
22 | private String type;
23 |
24 | private PeriodType(String type){
25 | this.type = type;
26 | }
27 |
28 | public String getType() {
29 | return type;
30 | }
31 |
32 | public int getIntValue(){
33 | int val = 0;
34 | switch (this) {
35 | case ONE_MIN:
36 | val = Constants.MINUTE;
37 | break;
38 | case FIVE_MIN:
39 | val = Constants.MINUTES_5;
40 | break;
41 | case FIFTEEN_MIN:
42 | val = Constants.MINUTES_15;
43 | break;
44 | case THIRTY_MIN:
45 | val = Constants.MINUTES_30;
46 | break;
47 | case SIXTY_MIN:
48 | val = Constants.MINUTES_60;
49 | break;
50 | case DAY:
51 | val = Constants.DAY;
52 | break;
53 | case WEEK:
54 | val = Constants.WEEK;
55 | break;
56 | }
57 | return val;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/model/StockBlock.java:
--------------------------------------------------------------------------------
1 | package net.jquant.model;
2 |
3 | import com.google.common.collect.Lists;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * 股票版块
9 | */
10 | public class StockBlock {
11 |
12 | //http://quote.eastmoney.com/center/list.html#28002458_0_2, id=28002458
13 | public String id;
14 |
15 | public String name;
16 |
17 | public String type; //版块类型:概念,地域,行业
18 |
19 | public String url;
20 |
21 | public List symbolList = Lists.newLinkedList();
22 |
23 | public void add(String symbol){
24 | symbolList.add(symbol);
25 | }
26 |
27 | @Override
28 | public String toString() {
29 | return "StockBlock{" +
30 | "url='" + url + '\'' +
31 | ", name='" + name + '\'' +
32 | ", id='" + id + '\'' +
33 | '}';
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/model/StockData.java:
--------------------------------------------------------------------------------
1 | package net.jquant.model;
2 |
3 | import com.google.common.collect.Maps;
4 | import net.jquant.common.Utils;
5 |
6 | import java.util.Date;
7 | import java.util.LinkedHashMap;
8 | import java.util.Map;
9 |
10 | /**
11 | * author: eryk
12 | * mail: xuqi86@gmail.com
13 | * date: 15-8-15.
14 | */
15 | public class StockData extends LinkedHashMap implements Comparable {
16 |
17 | /**
18 | * 股票代码
19 | */
20 | public String symbol;
21 |
22 | /**
23 | * 股票名称
24 | */
25 | public String name;
26 |
27 | /**
28 | * 数据时间点
29 | */
30 | public Date date;
31 |
32 | /**
33 | * 版块信息:主版,中小板,创业板
34 | */
35 | public BoardType boardType;
36 |
37 | /**
38 | * 市场:深市,沪市
39 | */
40 | public StockMarketType stockMarketType;
41 |
42 | /**
43 | * 属性值
44 | */
45 | public Map attribute = Maps.newHashMap();
46 |
47 | public StockData() {
48 | }
49 |
50 | public StockData(String symbol) {
51 | this.symbol = symbol;
52 | this.stockMarketType = StockMarketType.getType(symbol);
53 | this.boardType = BoardType.getType(symbol);
54 | }
55 |
56 | public StockData(Map map) {
57 | this.putAll(map);
58 | }
59 |
60 | public void attr(String key, String val) {
61 | attribute.put(key, val);
62 | }
63 |
64 | public String attr(String key) {
65 | return attribute.get(key);
66 | }
67 |
68 | @Override
69 | public String toString() {
70 | return "StockData{" +
71 | "symbol='" + symbol + '\'' +
72 | ", name='" + name + '\'' +
73 | ", date=" + Utils.formatDate(date, "yyyy-MM-dd HH:mm:ss") +
74 | ", boardType=" + boardType +
75 | ", stockMarketType=" + stockMarketType +
76 | ", stockData=" + Utils.map2Json(this) +
77 | ", stockAttribute= " + Utils.map2Json(attribute) +
78 | '}';
79 | }
80 |
81 | @Override
82 | public int compareTo(StockData stockData) {
83 | int compare = this.symbol.compareTo(stockData.symbol);
84 | if (compare != 0) {
85 | return compare;
86 | }
87 | return this.date.compareTo(stockData.date);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/model/StockMarketType.java:
--------------------------------------------------------------------------------
1 | package net.jquant.model;
2 |
3 | /**
4 | * author: eryk
5 | * mail: xuqi86@gmail.com
6 | * date: 15-8-12.
7 | */
8 | public enum StockMarketType {
9 | SH,SZ,UNKNOW;
10 |
11 | public static StockMarketType getType(String symbol){
12 | if(symbol.startsWith("6")){
13 | return SH;
14 | }else if(symbol.startsWith("0") || symbol.startsWith("3")){
15 | return SZ;
16 | }else{
17 | return UNKNOW;
18 | }
19 | }
20 |
21 | public boolean isMatchType(String symbol){
22 | if(this.equals(SH)){
23 | return symbol.startsWith("6");
24 | }
25 | if(this.equals(SZ)){
26 | return symbol.startsWith("0") || symbol.startsWith("3");
27 | }
28 | return false;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/model/StockSlice.java:
--------------------------------------------------------------------------------
1 | package net.jquant.model;
2 |
3 | import com.google.common.collect.Lists;
4 | import com.google.common.collect.Maps;
5 | import net.jquant.common.Constants;
6 | import net.jquant.common.Utils;
7 |
8 | import java.util.List;
9 | import java.util.Map;
10 |
11 | /**
12 | * author: eryk
13 | * mail: xuqi86@gmail.com
14 | * date: 15-8-9.
15 | */
16 | public class StockSlice {
17 | public String symbol;
18 | public String startDate;
19 | public String stopDate;
20 | public PeriodType type;
21 |
22 | public List stocks;
23 |
24 |
25 | public Map> points;
26 |
27 | public static StockSlice getSlice(String symbol,List stocks, String start, String stop) {
28 | return new StockSlice(symbol,stocks, start, stop);
29 | }
30 |
31 | private StockSlice(String symbol,List stocks, String start, String stop) {
32 | this.symbol = symbol;
33 | this.startDate = start;
34 | this.stopDate = stop;
35 | this.stocks = stocks;
36 | points = Maps.newHashMapWithExpectedSize(stocks.size());
37 | setClosePrice();
38 | setVolumes();
39 | }
40 |
41 | public List getStocks(){
42 | return stocks;
43 | }
44 |
45 | public void setClosePrice() {
46 | List closes = points.get(Utils.toString(Constants.CLOSE));
47 | if(closes==null){
48 | closes = Lists.newLinkedList();
49 | }
50 | for (StockData stock : stocks) {
51 | closes.add(stock.get("close"));
52 | }
53 | points.put(Utils.toString(Constants.CLOSE), closes);
54 | }
55 |
56 | public double[] getClose(){
57 | return getValues(Utils.toString(Constants.CLOSE));
58 | }
59 |
60 | public void setVolumes(){
61 | List volumes = points.get(Utils.toString(Constants.VOLUME));
62 | if(volumes==null){
63 | volumes = Lists.newLinkedList();
64 | }
65 | for (StockData stock : stocks) {
66 | volumes.add(stock.get("volume"));
67 | }
68 | points.put(Utils.toString(Constants.VOLUME), volumes);
69 | }
70 |
71 | public double[] getVolumes(){
72 | return getValues(Utils.toString(Constants.VOLUME));
73 | }
74 |
75 | public double[] getValues(String property) {
76 | List values = points.get(property);
77 | if (values == null) {
78 | values = Lists.newArrayList();
79 | }
80 | double[] result = new double[values.size()];
81 | for(int i=0;i getDailyDataWithURL(String symbol,String url){
41 | List stocks = Lists.newLinkedList();
42 | try {
43 | String data = Downloader.download(url, "gb2312");
44 | String[] lines = data.split("\n");
45 |
46 | for (int i = 1; i < lines.length; i++) { //第一行是标题,跳过
47 | String[] line = lines[i].split(",");
48 | //股票数据15列,指数数据13列,少最后两列
49 | if ((line.length == 15 || line.length == 13) && !lines[i].contains("None")) {
50 | try {
51 | StockData stock = new StockData(line[1].replace("'", ""));
52 |
53 | stock.date = Utils.str2Date(line[0], Constants.NETEASE_DATE_STYLE);
54 | stock.name = line[2];
55 | for (int j = 0; j < StockConstants.DAILY.size() - 1; j++) {
56 | if(line.length>j+3){
57 | stock.put(StockConstants.DAILY.get(j), Utils.str2Double(line[j + 3]));
58 | }
59 | }
60 | stock.put("amplitude", Utils.formatDouble((stock.get("high") - stock.get("low")) / stock.get("lastClose")));
61 | changeUnit(stock);
62 | stocks.add(stock);
63 | } catch (Exception e) {
64 | LOG.warn(String.format("stock %s convert error %s", symbol,lines[i]),e);
65 | }
66 | }
67 | }
68 |
69 | } catch (Exception e) {
70 | LOG.error(String.format("stock %s collect error", symbol), e);
71 | }
72 | //按照时间从最早到最新
73 | return Lists.reverse(stocks);
74 | }
75 |
76 | /**
77 | * 获取指数日线数据
78 | * 上证综指:0000001
79 | * 深证成指:1399001
80 | * 深证综指:1399106
81 | * 沪深300:0000300
82 | * 创业板指:1399006
83 | * 创业板综:1399102
84 | * 中小板指:1399005
85 | * 中小板综:1399101
86 | * @param symbol stock symbol
87 | * @param startDate yyyyMMdd
88 | * @param stopDate yyyyMMdd
89 | * @return data list
90 | */
91 | public static List getIndex(String symbol, String startDate, String stopDate){
92 | String url;
93 | if(symbol.startsWith("3")){
94 | url = getPath(symbol,startDate,stopDate);
95 | }else{
96 | url = String.format(DAILY_DATA_URL,"0"+symbol,startDate,stopDate);
97 | }
98 | return getDailyDataWithURL(symbol,url);
99 | }
100 |
101 | /**
102 | * 获取未复权数据
103 | * @param symbol stock symbol
104 | * @param startDate yyyyMMdd
105 | * @param stopDate yyyyMMdd
106 | * @return data list
107 | */
108 | public static List get(String symbol,String startDate,String stopDate) throws StockDataParseException {
109 | String lastUrl = "http://d.10jqka.com.cn/v2/line/hs_%s/01/last.js";
110 | Map stringObjectMap = THSJSDownloader.download(lastUrl, symbol);
111 | Map years = (Map) stringObjectMap.get("year");
112 | String yearUrl = "http://d.10jqka.com.cn/v2/line/hs_%s/01/%s.js";
113 | List stockDatas = Lists.newLinkedList();
114 | for (String year : years.keySet()) {
115 | Map tmp = THSJSDownloader.download(String.format(yearUrl, symbol, year));
116 | if (tmp == null || tmp.size() == 0){
117 | throw new StockDataParseException();
118 | }
119 | String data = (String) tmp.get("data");
120 | if(data == null){
121 | throw new StockDataParseException();
122 | }
123 | String[] array = data.split(";");
124 | for(String line:array){
125 | StockData stockData = parseDailyData(symbol,line);
126 | if(stockData != null){
127 | stockDatas.add(stockData);
128 | }else{
129 | throw new StockDataParseException();
130 | }
131 | }
132 | }
133 | if(stockDatas.size() > 0){
134 | StockData stockData = stockDatas.get(stockDatas.size()-1);
135 | if(!Utils.isToday(stockData.date)){
136 | stockDatas.add(today(symbol));
137 | }
138 | }
139 | return stockDatas;
140 | }
141 |
142 | private static StockData today(String symbol) throws StockDataParseException{
143 | String url = "http://d.10jqka.com.cn/v2/line/hs_%s/01/today.js";
144 | Map download = THSJSDownloader.download(url, symbol);
145 | if(download == null || download.size() == 0){
146 | throw new StockDataParseException();
147 | }
148 | Map values = (Map) download.get("hs_" + symbol);
149 | if(values == null || values.size() == 0){
150 | throw new StockDataParseException();
151 | }
152 | StockData stockData = new StockData(symbol);
153 | stockData.date = DateTimeFormat.forPattern("yyyyMMddHHmm").parseLocalDateTime(
154 | String.valueOf(values.get("1")) + String.valueOf(values.get("dt"))).toDate();
155 | stockData.put("open",Double.parseDouble((String) values.get("7")));
156 | stockData.put("high",Double.parseDouble((String) values.get("8")));
157 | stockData.put("low",Double.parseDouble((String) values.get("9")));
158 | stockData.put("close",Double.parseDouble((String) values.get("11")));
159 | stockData.put("volume", (Double) values.get("13") /100);
160 | stockData.put("amount",Double.parseDouble((String) values.get("19")) /10000);
161 | stockData.put("turnover",Double.parseDouble((String) values.get("1968584")));
162 | return stockData;
163 | }
164 |
165 | private static StockData parseDailyData(String symbol,String line){
166 | String[] fields = line.split(",");
167 | if(fields.length != 8){
168 | return null;
169 | }
170 | DateTime dateTime = DateTimeFormat.forPattern("yyyyMMdd").parseDateTime(fields[0]);
171 | StockData stockData = new StockData(symbol);
172 | stockData.date = dateTime.toDate();
173 | stockData.put("open",Double.parseDouble(fields[1]));
174 | stockData.put("high",Double.parseDouble(fields[2]));
175 | stockData.put("low",Double.parseDouble(fields[3]));
176 | stockData.put("close",Double.parseDouble(fields[4]));
177 | stockData.put("volume",Double.parseDouble(fields[5])/100);
178 | if(!Strings.isNullOrEmpty(fields[6])){
179 | stockData.put("amount",Double.parseDouble(fields[6])/10000);
180 | }else{
181 | stockData.put("amount",0d);
182 | }
183 | stockData.put("turnover",Double.parseDouble(fields[7]));
184 | return stockData;
185 | }
186 |
187 | //http://d.10jqka.com.cn/v2/line/hs_600133/01/2015.js
188 | public static String FQ_URL = "http://d.10jqka.com.cn/v2/line/hs_%s/01/%s.js";
189 |
190 | private static void changeUnit(StockData stockData) {
191 | stockData.put(StockConstants.VOLUME, stockData.get(StockConstants.VOLUME) / 100); //成交量,单位:手
192 | stockData.put(StockConstants.AMOUNT, stockData.get(StockConstants.AMOUNT) / 10000); //成交金额,单位:万
193 | //指数数据没有下面两项
194 | if(stockData.get(StockConstants.TOTAL_VALUE)!=null){
195 | stockData.put(StockConstants.TOTAL_VALUE, stockData.get(StockConstants.TOTAL_VALUE) / 100000000); //总市值,单位:亿
196 | }
197 | if(stockData.get(StockConstants.MARKET_VALUE)!=null){
198 | stockData.put(StockConstants.MARKET_VALUE, stockData.get(StockConstants.MARKET_VALUE) / 100000000); //流通市值,单位:亿
199 | }
200 | }
201 |
202 | /**
203 | * @param symbol stock symbol
204 | * @param startDate yyyyMMdd
205 | * @param stopDate yyyyMMdd
206 | * @return url path with start and stop date
207 | */
208 | public static String getPath(String symbol, String startDate, String stopDate) {
209 | return String.format(DAILY_DATA_URL, Symbol.getSymbol(symbol, DAILY_DATA_URL), startDate, stopDate);
210 | }
211 |
212 | }
213 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/provider/FinanceDataProvider.java:
--------------------------------------------------------------------------------
1 | package net.jquant.provider;
2 |
3 | import com.google.common.base.Strings;
4 | import com.google.common.collect.Lists;
5 | import com.google.common.collect.Maps;
6 |
7 | import org.joda.time.DateTime;
8 | import net.jquant.common.DateRange;
9 | import net.jquant.common.Utils;
10 | import net.jquant.downloader.Downloader;
11 | import net.jquant.model.StockData;
12 |
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | /**
17 | * author: eryk
18 | * mail: xuqi86@gmail.com
19 | * date: 15-8-30.
20 | * 财务报表数据
21 | */
22 | public class FinanceDataProvider {
23 |
24 | //主要财务指标
25 | private static String mainFinanceReport = "http://quotes.money.163.com/service/zycwzb_%s.html?type=report";
26 | //盈利能力
27 | private static String profitReport = "http://quotes.money.163.com/service/zycwzb_%s.html?type=report&part=ylnl";
28 | //偿还能力
29 | private static String debtReport = "http://quotes.money.163.com/service/zycwzb_%s.html?type=report&part=chnl";
30 | //成长能力
31 | private static String growReport = "http://quotes.money.163.com/service/zycwzb_%s.html?type=report&part=cznl";
32 | //营运能力
33 | private static String operateReport = "http://quotes.money.163.com/service/zycwzb_%s.html?type=report&part=yynl";
34 | //财务报表摘要
35 | private static String abstractFinanceReport = "http://quotes.money.163.com/service/cwbbzy_%s.html";
36 |
37 | public static List get(String symbol, String startDate, String stopDate) {
38 | Map> reports = collect(symbol);
39 | List puts = Lists.newLinkedList();
40 | for(Map.Entry> entry:reports.entrySet()){
41 | StockData stockData = new StockData(symbol);
42 | stockData.date = Utils.str2Date(entry.getKey(), "yyyy-MM-dd");
43 |
44 | if(Utils.isInRange(stockData.date,startDate,stopDate)){
45 | for(Map.Entry kv:entry.getValue().entrySet()){
46 | if(Utils.isDouble(kv.getValue())){
47 | stockData.put(kv.getKey(),Double.parseDouble(kv.getValue()));
48 | }else{
49 | stockData.put(kv.getKey(),Double.MIN_VALUE);
50 | }
51 | }
52 | puts.add(stockData);
53 | }
54 | }
55 | return puts;
56 | }
57 |
58 | public static List getYear(String symbol){
59 | DateRange range = DateRange.getRange(365*20);
60 | List stockDataList = get(symbol,range.start(),range.stop());
61 | List stockDataYearList = Lists.newArrayList();
62 | for(StockData stockData:stockDataList){
63 | DateTime time = new DateTime(stockData.date);
64 | if(time.getMonthOfYear()==12){
65 | stockDataYearList.add(stockData);
66 | }
67 | }
68 | return stockDataYearList;
69 | }
70 |
71 | private static Map> collect(String symbol) {
72 | Map> report = Maps.newTreeMap();
73 |
74 | String[] lines = Downloader.download(String.format(mainFinanceReport, symbol)).split("\n");
75 | toReport(lines, report);
76 | lines = Downloader.download(String.format(profitReport, symbol)).split("\n");
77 | toReport(lines, report);
78 | lines = Downloader.download(String.format(debtReport, symbol)).split("\n");
79 | toReport(lines, report);
80 | lines = Downloader.download(String.format(growReport, symbol)).split("\n");
81 | toReport(lines, report);
82 | lines = Downloader.download(String.format(operateReport, symbol)).split("\n");
83 | toReport(lines, report);
84 | lines = Downloader.download(String.format(abstractFinanceReport, symbol)).split("\n");
85 | toReport(lines, report);
86 |
87 | return report;
88 | }
89 |
90 | private static void toReport(String[] lines, Map> report) {
91 | if(lines.length<=1){
92 | return;
93 | }
94 | List columns = Lists.newArrayList();
95 | for(int i=0;i maps = Maps.newTreeMap();
104 | //行
105 | for(int j=1;j get(String symbol, String startDate, String stopDate, String type){
41 | Map> results = collect(symbol, Utils.str2Date(startDate,"yyyyMMdd"),Utils.str2Date(stopDate,"yyyyMMdd"),type);
42 |
43 | List stockList = Lists.newLinkedList();
44 | for(Map.Entry> entry:results.entrySet()) {
45 | StockData stockData = new StockData();
46 | stockData.stockMarketType = StockMarketType.getType(symbol);
47 | stockData.boardType = BoardType.getType(symbol);
48 | stockData.symbol = symbol;
49 | stockData.date = Utils.str2Date(entry.getValue().get("day"), "yyyy-MM-dd HH:mm:ss");
50 |
51 | stockData.put("open",Utils.str2Double(entry.getValue().get("open")));
52 | stockData.put("high",Utils.str2Double(entry.getValue().get("high")));
53 | stockData.put("low",Utils.str2Double(entry.getValue().get("low")));
54 | stockData.put("close",Utils.str2Double(entry.getValue().get("close")));
55 | stockData.put("volume",Utils.str2Double(entry.getValue().get("volume"))/100); //成交量 单位:手
56 |
57 | stockList.add(stockData);
58 | }
59 | return stockList;
60 | }
61 |
62 | private static Map> collect(String symbol,Date startDate,Date stopDate,String type) {
63 | String url = getPath(symbol,type);
64 | Map> stocks = Maps.newTreeMap();
65 | String data = Downloader.download(url);
66 |
67 | Pattern pattern = Pattern.compile("\\{([\\w|\"|,|:|\\s|.|-]*)\\}");
68 | Matcher matcher = pattern.matcher(data.trim());
69 | Gson gson = new Gson();
70 | while (matcher.find()) {
71 | //{day:"2015-08-13 13:55:00",open:"16.300",high:"16.320",low:"16.270",close:"16.290",volume:"390800"}
72 | Map map = gson.fromJson(matcher.group(), Map.class);
73 | Date date = Utils.str2Date(map.get("day"), "yyyy-MM-dd HH:mm:ss");
74 | if (date.getTime() >= startDate.getTime() && date.getTime() <=stopDate.getTime()) {
75 | stocks.put(Utils.toString(Utils.getRowkeyWithMd5PrefixAndDateSuffix(symbol, Utils.formatDate(date, "yyyyMMddHHmm"))), map);
76 | }
77 | }
78 |
79 | return stocks;
80 | }
81 |
82 | public static String getPath(String symbol,String type) {
83 | return String.format(minuteDataURL, Symbol.getSymbol(symbol, minuteDataURL), type);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/provider/Provider.java:
--------------------------------------------------------------------------------
1 | package net.jquant.provider;
2 |
3 | import net.jquant.common.StockDataParseException;
4 | import net.jquant.common.Utils;
5 | import net.jquant.model.StockBlock;
6 | import net.jquant.model.StockData;
7 | import net.jquant.model.Tick;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import net.jquant.common.DateRange;
11 | import net.jquant.tools.StockCategory;
12 | import net.jquant.tools.StockList;
13 |
14 | import java.util.*;
15 |
16 | /**
17 | * 成交量,单位:手
18 | * 成交金额,单位:万
19 | * 总市值,单位:亿
20 | * 流通市值,单位:亿
21 | * 外盘,单位:手
22 | * 内盘,单位:手
23 | */
24 | public class Provider {
25 |
26 | private static final Logger LOG = LoggerFactory.getLogger(Provider.class);
27 |
28 | /**
29 | * 获取指定时间段内的日线股票数据
30 | *
31 | * @param symbol stock symbol
32 | * @param startDate 格式:yyyyMMdd
33 | * @param stopDate 格式:yyyyMMdd
34 | * @return stock data list
35 | */
36 | public static List dailyData(String symbol, String startDate, String stopDate) throws StockDataParseException {
37 | return DailyDataProvider.get(symbol, startDate, stopDate);
38 | }
39 |
40 | /**
41 | * 获取日线级别最近250天历史数据
42 | *
43 | * @param symbol stock symbol
44 | * @return stock data list
45 | */
46 | public static List dailyData(String symbol) throws StockDataParseException {
47 | DateRange range = DateRange.getRange(250);
48 | return dailyData(symbol, range.start(), range.stop());
49 | }
50 |
51 | /**
52 | * 获取指数日线数据
53 | * 上证综指:0000001
54 | * 深证成指:1399001
55 | * 深证综指:1399106
56 | * 沪深300:0000300
57 | * 创业板指:1399006
58 | * 创业板综:1399102
59 | * 中小板指:1399005
60 | * 中小板综:1399101
61 | *
62 | * @param symbol index symbol
63 | * @param startDate yyyyMMdd
64 | * @param stopDate yyyyMMdd
65 | * @return stock data list
66 | */
67 | public static List dailyDataZS(String symbol, String startDate, String stopDate) throws StockDataParseException {
68 | return DailyDataProvider.getIndex(symbol, startDate, stopDate);
69 | }
70 |
71 | public static List dailyDataZS(String symbol, int period) throws StockDataParseException {
72 | DateRange dateRange = DateRange.getRange(period);
73 | return dailyDataZS(symbol, dateRange.start(), dateRange.stop());
74 | }
75 |
76 | public static List dailyDataZS(String symbol) throws StockDataParseException {
77 | DateRange dateRange = DateRange.getRange(250);
78 | return dailyDataZS(symbol, dateRange.start(), dateRange.stop());
79 | }
80 |
81 | /**
82 | * 获取实时数据股票数据
83 | *
84 | * @param symbol stock symbol
85 | * @return stock data list
86 | */
87 | public static StockData realtimeData(String symbol) throws StockDataParseException {
88 | return RealTimeDataProvider.get(symbol);
89 | }
90 |
91 | /**
92 | * 获取最新10天股票分钟级别数据
93 | *
94 | * @param symbol stock symbol
95 | * @param type 参数值:5,15,30,60
96 | * @return stock data list
97 | */
98 | public static List minuteData(String symbol, String type) throws StockDataParseException {
99 | DateRange range = DateRange.getRange(120);//数据源不够30天
100 | List stockDataList = minuteData(symbol, range.start(), range.stop(), type);
101 | return stockDataList;
102 | }
103 |
104 | /**
105 | * 获取指定时间段内历史分钟级别数据,受数据源限制
106 | *
107 | * @param symbol stock symbol
108 | * @param startDate 格式:yyyyMMdd
109 | * @param stopDate 格式:yyyyMMdd
110 | * @param type 5,15,30,60
111 | * @return stock data list
112 | */
113 | public static List minuteData(String symbol, String startDate, String stopDate, String type) throws StockDataParseException {
114 | return MinuteDataProvider.get(symbol, startDate, stopDate, type);
115 | }
116 |
117 | /**
118 | * 获取个股当日资金流数据
119 | *
120 | * @param symbol stock symbol
121 | * @return stock data list
122 | */
123 | public static StockData moneyFlowData(String symbol) {
124 | return MoneyFlowDataProvider.get(symbol);
125 | }
126 |
127 | /**
128 | * 获取个股指定时间段内资金流数据,受数据源限制
129 | *
130 | * @param symbol stock symbol
131 | * @param startDate 格式:yyyyMMdd
132 | * @param stopDate 格式:yyyyMMdd
133 | * @return stock data list
134 | */
135 | public static List moneyFlowData(String symbol, String startDate, String stopDate) throws StockDataParseException {
136 | return MoneyFlowDataProvider.get(symbol, startDate, stopDate);
137 | }
138 |
139 | /**
140 | * 大盘资金流向历史数据
141 | *
142 | * @return stock data list
143 | */
144 | public static List moneyFlowDapanData() throws StockDataParseException {
145 | return MoneyFlowDataProvider.getDapan();
146 | }
147 |
148 | /**
149 | * 获取今天、5日、10日行业版块资金流数据
150 | *
151 | * @param type 1,5,10
152 | * @return stock data list
153 | */
154 | public static List moneyFlowIndustryData(String type) throws StockDataParseException {
155 | return MoneyFlowDataProvider.getIndustry(type);
156 | }
157 |
158 | /**
159 | * 获取今天、5日、10日行业版块资金流数据
160 | *
161 | * @param type 输入值:1,5,10
162 | * @return stock data list
163 | */
164 | public static List moneyFlowConceptData(String type) throws StockDataParseException {
165 | return MoneyFlowDataProvider.getConcept(type);
166 | }
167 |
168 | /**
169 | * 获取今天、5日、10日行业版块资金流数据
170 | *
171 | * @param type 1,5,10
172 | * @return stock data list
173 | */
174 | public static List moneyFlowRegionData(String type) throws StockDataParseException {
175 | return MoneyFlowDataProvider.getRegion(type);
176 | }
177 |
178 | /**
179 | * 历史财报
180 | *
181 | * @param symbol stock symbol
182 | * @param startDate 格式:yyyyMMdd
183 | * @param stopDate 格式:yyyyMMdd
184 | * @return stock data list
185 | */
186 | public static List financeData(String symbol, String startDate, String stopDate) throws StockDataParseException {
187 | return FinanceDataProvider.get(symbol, startDate, stopDate);
188 | }
189 |
190 | /**
191 | * 最新一期财报数据
192 | *
193 | * @param symbol stock symbol
194 | * @return stock data list
195 | */
196 | public static StockData financeData(String symbol) throws StockDataParseException {
197 | DateRange range = DateRange.getRange(365);
198 | List stockDataList = FinanceDataProvider.get(symbol, range.start(), range.stop());
199 | if (stockDataList.size() >= 1) {
200 | return stockDataList.get(stockDataList.size() - 1);
201 | } else {
202 | return new StockData(symbol);
203 | }
204 | }
205 |
206 | /**
207 | * 股票年报数据
208 | *
209 | * @param symbol stock symbol
210 | * @return stock data list
211 | */
212 | public static List financeYearData(String symbol) throws StockDataParseException {
213 | return FinanceDataProvider.getYear(symbol);
214 | }
215 |
216 | /**
217 | * 获取最新一天股票逐笔数据
218 | *
219 | * @param symbol stock symbol
220 | * @return tick list
221 | */
222 | public static List tickData(String symbol) throws StockDataParseException {
223 | return TickDataProvider.get(symbol);
224 | }
225 |
226 | /**
227 | * 获取指定日期逐笔股票数据
228 | *
229 | * @param symbol stock symbol
230 | * @param date 格式: yyyyMMdd
231 | * @return tick data list
232 | */
233 | public static List tickData(String symbol, String date) throws StockDataParseException {
234 | String _date = Utils.formatDate(Utils.str2Date(date, "yyyyMMdd"), "yyyy-MM-dd");
235 | return TickDataProvider.get(symbol, _date);
236 | }
237 |
238 | /**
239 | * 获取股票版块数据
240 | * map key: 股票的版块分类名称,包含三项:概念,地区,行业
241 | * value: list是版块分类下的版块,每个版块包含一个股票列表
242 | *
243 | * @return stock data list
244 | */
245 | public static Map> stockBlock() throws StockDataParseException {
246 | return StockCategory.getCategory();
247 | }
248 |
249 | /**
250 | * 获取某个股票在大分类下的具体分类:概念,行业,地域
251 | *
252 | * @param type category
253 | * @return stock data list
254 | */
255 | public static Map> stockCategory(String type) throws StockDataParseException {
256 | return StockCategory.getStockCategory(type);
257 | }
258 |
259 | /**
260 | * 获取股票列表
261 | *
262 | * @return stock data list
263 | */
264 | public static List stockList() throws StockDataParseException {
265 | return StockList.getSymbols();
266 | }
267 |
268 | //计算是今天第几个bar,从1开始
269 | private long getTimeSlot(long curTime, long startTime, int interval) {
270 | return (curTime - startTime) / interval;
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/provider/RealTimeDataProvider.java:
--------------------------------------------------------------------------------
1 | package net.jquant.provider;
2 |
3 | import com.google.common.base.Strings;
4 | import com.google.common.collect.Maps;
5 | import com.google.gson.Gson;
6 |
7 | import net.jquant.common.StockConstants;
8 | import net.jquant.common.Utils;
9 | import net.jquant.downloader.Downloader;
10 | import net.jquant.downloader.THSJSDownloader;
11 | import net.jquant.model.StockData;
12 | import net.jquant.model.Symbol;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import java.util.Date;
17 | import java.util.List;
18 | import java.util.Map;
19 |
20 |
21 | /**
22 | * author: eryk
23 | * mail: xuqi86@gmail.com
24 | * date: 15-8-30.
25 | */
26 | public class RealTimeDataProvider {
27 | private static final Logger LOG = LoggerFactory.getLogger(RealTimeDataProvider.class);
28 |
29 | private static String realTimeDateURL = "http://nuff.eastmoney.com/EM_Finance2015TradeInterface/JS.ashx?id=%s&_=%s";
30 |
31 | private static Map> collect(String symbol) {
32 | String data = Downloader.download(getPath(symbol));
33 | if (Strings.isNullOrEmpty(data)) {
34 | LOG.error("fail to get real time data from " + getPath(symbol));
35 | return Maps.newHashMap();
36 | }
37 | data = data.substring(9, data.length() - 1);
38 | Gson gson = new Gson();
39 | Map> map = gson.fromJson(data, Map.class);
40 | return map;
41 | }
42 |
43 | /**
44 | * 获取实时股票交易数据
45 | * @param symbol stock symbol
46 | * @return stock data list
47 | */
48 | public static StockData get(String symbol){
49 |
50 | Map> map = collect(symbol);
51 |
52 | if(map.size()==0){
53 | return new StockData();
54 | }
55 | List columns = map.get("Value");
56 | if (columns.size() != 53) {
57 | LOG.warn(String.format("stock [%s] data is not format",symbol));
58 | return new StockData();
59 | }
60 |
61 | StockData stockData = new StockData(symbol);
62 | stockData.name = columns.get(2);
63 | stockData.date = Utils.str2Date(columns.get(49), "yyyy-MM-dd HH:mm:ss");
64 |
65 | for (int i = 3; i < columns.size() - 1; i++) {
66 | if (getColumnName(i).equals("amount")) {
67 | stockData.put(getColumnName(i), Utils.getAmount(columns.get(i)));
68 | } else if (!getColumnName(i).equals("")) {
69 | if (Utils.isDouble(columns.get(i))) {
70 | stockData.put(getColumnName(i), Double.parseDouble(columns.get(i)));
71 | }
72 | }
73 | }
74 | changeUnit(stockData);
75 | return stockData;
76 | }
77 |
78 | private static void changeUnit(StockData stockData) {
79 | stockData.put(StockConstants.AMOUNT,stockData.get(StockConstants.AMOUNT)/10000); //成交金额,单位:万
80 | stockData.put(StockConstants.TOTAL_VALUE,stockData.get(StockConstants.TOTAL_VALUE)/100000000); //总市值,单位:亿
81 | stockData.put(StockConstants.MARKET_VALUE, stockData.get(StockConstants.MARKET_VALUE) / 100000000); //流通市值,单位:亿
82 | }
83 |
84 | /**
85 | * 获取原始数据对应的列名称
86 | * @param i column id
87 | * @return column name
88 | */
89 | private static String getColumnName(int i) {
90 | return StockConstants.REALTIME.get(i);
91 | }
92 |
93 | public static String getPath(String symbol) {
94 | Date date = new Date();
95 | return String.format(realTimeDateURL, Symbol.getSymbol(symbol, realTimeDateURL), date.getTime());
96 | }
97 |
98 | /**
99 | * http://d.10jqka.com.cn/v2/fiverange/hs_300033/last.js
100 | *
101 | * @return 五挡买卖数据
102 | */
103 | private StockData fiveRange(String symbol) {
104 | String url = "http://d.10jqka.com.cn/v2/fiverange/hs_%s/last.js";
105 | Map result = THSJSDownloader.download(url, symbol);
106 | Map parser = (Map) result.get("items");
107 | StockData stockData = new StockData(symbol);
108 | if (parser != null) {
109 | if (parser.size() > 0) {
110 | stockData.put("buy1", Double.parseDouble(parser.get("24")));
111 | stockData.put("buy1Volume", Double.parseDouble(parser.get("25")) / 100);
112 | stockData.put("buy2", Double.parseDouble(parser.get("26")));
113 | stockData.put("buy2Volume", Double.parseDouble(parser.get("27")) / 100);
114 | stockData.put("buy3", Double.parseDouble(parser.get("28")));
115 | stockData.put("buy3Volume", Double.parseDouble(parser.get("29")) / 100);
116 | stockData.put("buy4", Double.parseDouble(parser.get("150")));
117 | stockData.put("buy4Volume", Double.parseDouble(parser.get("151")) / 100);
118 | stockData.put("buy5", Double.parseDouble(parser.get("154")));
119 | stockData.put("buy5Volume", Double.parseDouble(parser.get("155")) / 100);
120 | stockData.put("sell1", Double.parseDouble(parser.get("30")));
121 | stockData.put("sell1Volume", Double.parseDouble(parser.get("31")) / 100);
122 | stockData.put("sell2", Double.parseDouble(parser.get("32")));
123 | stockData.put("sell2Volume", Double.parseDouble(parser.get("33")) / 100);
124 | stockData.put("sell3", Double.parseDouble(parser.get("34")));
125 | stockData.put("sell3Volume", Double.parseDouble(parser.get("35")) / 100);
126 | stockData.put("sell4", Double.parseDouble(parser.get("152")));
127 | stockData.put("sell4Volume", Double.parseDouble(parser.get("153")) / 100);
128 | stockData.put("sell5", Double.parseDouble(parser.get("156")));
129 | stockData.put("sell5Volume", Double.parseDouble(parser.get("157")) / 100);
130 | return stockData;
131 | }
132 | }
133 | return stockData;
134 | }
135 |
136 | public static void main(String[] args) {
137 | StockData stock = RealTimeDataProvider.get("000001");
138 | System.out.println(stock);
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/main/java/net/jquant/provider/ReportDataProvider.java:
--------------------------------------------------------------------------------
1 | package net.jquant.provider;
2 |
3 | import com.google.common.base.Strings;
4 | import com.google.common.collect.Lists;
5 | import com.google.gson.Gson;
6 | import net.jquant.common.Utils;
7 | import net.jquant.downloader.Downloader;
8 | import net.jquant.model.StockData;
9 |
10 | import java.util.Date;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | /**
15 | * author: eryk
16 | * mail: xuqi86@gmail.com
17 | * date: 15-9-22.
18 | * 分析师研究报告数据,数据来源
19 | * http://data.eastmoney.com/report/
20 | */
21 | public class ReportDataProvider {
22 |
23 | /**
24 | * 个股研究报告地址
25 | * p=页数,一页200条
26 | */
27 | private static String STOCK_REPORT_URL = "http://datainterface.eastmoney.com//EM_DataCenter/js.aspx?type=SR&sty=GGSR&ps=200&p=%s&mkt=0&stat=0&cmd=2&rt=48097671";
28 |
29 | /**
30 | * 盈利预期数据 http://data.eastmoney.com/report/ylyc.html
31 | * param1=版块,全部=_A
32 | */
33 | private static String EXPECT_EARNINGS_URL = "http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx?type=CT&cmd=C._A&sty=GEMCPF&st=(AllNum)&sr=-1&p=2&ps=5000&cb=&token=3a965a43f705cf1d9ad7e1a3e429d622&rt=48090871";
34 |
35 | private static String EXPECT_EARNINGS_PAGE_URL = "http://data.eastmoney.com/report/ylyc.html";
36 |
37 |
38 | /**
39 | * 个股研究报告
40 | * @param startDate 报告起始时间,格式:yyyyMMdd
41 | * @return stock data list
42 | */
43 | public static List getStockReportData(String startDate){
44 | Date start = Utils.str2Date(startDate,"yyyyMMdd");
45 |
46 | List stockDataList = Lists.newLinkedList();
47 | int pageCount = 1;
48 | while(pageCount<2){
49 | String url = String.format(STOCK_REPORT_URL, pageCount);
50 | String data = Downloader.download(url);
51 | Gson gson = new Gson();
52 | List