中文 | English — 本技能双语编写,代码示例通用。This skill is bilingual with shared code examples.
老虎量化开放平台 Java SDK (openapi-java-sdk) 为个人和机构用户提供交易与行情 API,支持美股、港股、A股、新加坡、澳洲市场的股票、期权、期货等品种。 The Tiger Open Platform Java SDK provides trading and market data APIs for stocks, options, futures across US, HK, China A-shares, Singapore, and Australia markets.
- 官方文档 / Docs: https://quant.itigerup.com/openapi/java/overview/introduction
- GitHub: https://github.com/tigerbrokers/openapi-java-sdk
- Gitee: https://gitee.com/tigerbrokers/openapi-java-sdk
- SDK Version: 2.4.1 | Java: JDK 1.7+ (64-bit), 推荐 1.8+
| 市场 Market | 股票/ETF | 期权 Options | 期货 Futures | 窝轮/牛熊证 Warrants/CBBC |
|---|---|---|---|---|
| 美国 US | ✅ | ✅ | ✅ | - |
| 香港 HK | ✅ | ✅ | ✅ | ✅ |
| 新加坡 SG | ✅ | - | ✅ | - |
| 澳洲 AU | ✅ | - | - | - |
| A股 CN | ✅ | - | - | - |
<dependency>
<groupId>io.github.tigerbrokers</groupId>
<artifactId>openapi-java-sdk</artifactId>
<version>2.4.1</version>
</dependency>如果无法下载,尝试添加仓库源 / If download fails, add repository:
<repositories>
<repository>
<id>sonatype-public</id>
<name>sonatype-public</name>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
</repositories>dependencies {
implementation 'io.github.tigerbrokers:openapi-java-sdk:2.4.1'
}git clone https://github.com/tigerbrokers/openapi-java-sdk.git
cd openapi-java-sdk
mvn clean install- 开通老虎证券账户并入金 / Open a Tiger Brokers account and fund it
- 个人用户访问 https://developer.itigerup.com/profile 激活 API 权限 / Individual users activate API permissions 机构用户访问机构账户中心 / Institutional users visit institutional account center
- 获取
tiger_id、RSA 私钥(PKCS#8 格式)和资金账号 / Obtaintiger_id, RSA private key (PKCS#8 format), and account - 导出配置文件
tiger_openapi_config.properties/ Export config file
私钥不会保存在服务端,刷新页面后消失,请务必保存。Private key is not stored on server; save it before refreshing the page. Java SDK 使用 PKCS#8 格式(
private_key_pk8)。Java SDK uses PKCS#8 format.
| 类型 Type | 说明 Description | 推荐 |
|---|---|---|
| 综合账户 Comprehensive (Live) | 支持所有市场、保证金交易、全品种。推荐 | ✅ |
| 环球账户 Global (Live) | 以 U 开头,如 U12300123 | - |
| 模拟账户 Paper | 17位数字,测试用,无需真实资金 | 测试推荐 |
从开发者网站导出 tiger_openapi_config.properties,放入 clientConfig.configFilePath 指定的目录。
Export tiger_openapi_config.properties from developer website and place it in the configFilePath directory.
tiger_id=YOUR_TIGER_ID
account=12345678
license=TBHK
private_key_pk1=MIICXgIBAAKBgQC.....your_pkcs1_private_key
private_key_pk8=MIICeAIBADANBgk.....your_pkcs8_private_key
env=PROD
# 机构用户需额外配置 / Institutional users also need:
# secret_key=your_secret_keyJava SDK 同时支持 PKCS#8(
private_key_pk8)和 PKCS#1(private_key_pk1)格式,优先读取private_key_pk8。 Java SDK supports both PKCS#8 (private_key_pk8) and PKCS#1 (private_key_pk1).private_key_pk8takes priority.
港股牌照(TBHK)还需将
tiger_openapi_token.properties放入同一目录。Token 有效期 30 天。 HK license (TBHK) also requirestiger_openapi_token.propertiesin the same directory. Token valid for 30 days.
import com.tigerbrokers.stock.openapi.client.config.ClientConfig;
import com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient;
import com.tigerbrokers.stock.openapi.client.util.ApiLogger;
public static ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG;
public static TigerHttpClient client;
static {
// 开启日志 / Enable logging
ApiLogger.setEnabled(true, "/data/tiger_openapi/logs/");
// 配置文件存放路径 / Config file directory
clientConfig.configFilePath = "/data/tiger_config";
// 可选配置 / Optional settings:
// clientConfig.isSslSocket = true; // 长连接是否使用SSL, default true
// clientConfig.isAutoGrabPermission = true; // 启动时抢占行情权限, default true
// clientConfig.failRetryCounts = 2; // API请求失败重试次数(最多5), default 2
// clientConfig.timeZone = TimeZoneId.Shanghai; // 默认时区
// clientConfig.language = Language.en_US; // 默认语言
// clientConfig.secretKey = "xxxxxx"; // 机构用户私钥 / Institutional secret key
// clientConfig.isAutoRefreshToken = false; // TBHK牌照自动刷新token, default false
// clientConfig.refreshTokenIntervalDays = 5; // 自动刷新周期(天)
// clientConfig.refreshTokenTime = "12:30:00"; // 自动刷新时间 HH:mm:ss
client = TigerHttpClient.getInstance().clientConfig(clientConfig);
}ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG;
clientConfig.tigerId = "your tiger id";
clientConfig.defaultAccount = "your account";
clientConfig.privateKey = FileUtil.readPrivateKey("/path/to/rsa_private_key_pkcs8.pem");
// clientConfig.token = "xx"; // TBHK牌照需要配置
// clientConfig.secretKey = "xxxxxx"; // 机构用户
TigerHttpClient client = TigerHttpClient.getInstance().clientConfig(clientConfig);| 配置项 Config | 说明 Description |
|---|---|
configFilePath |
tiger_openapi_config.properties 和 token 文件存放目录 |
tigerId |
开发者ID (配置文件优先) |
defaultAccount |
资金账号,综合/模拟账号 (配置文件优先) |
privateKey |
RSA 私钥 PKCS#8 格式 (配置文件优先) |
secretKey |
机构交易员密钥,个人用户不设置 |
token |
TBHK 牌照两步验证 |
isSslSocket |
长连接是否使用 SSL |
isAutoGrabPermission |
是否启动时抢占行情 |
failRetryCounts |
失败重试次数,最多5 |
timeZone |
默认时区 |
language |
默认语言 |
import com.tigerbrokers.stock.openapi.client.config.ClientConfig;
import com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient;
import com.tigerbrokers.stock.openapi.client.https.request.quote.QuoteKlineRequest;
import com.tigerbrokers.stock.openapi.client.https.response.quote.QuoteKlineResponse;
import com.tigerbrokers.stock.openapi.client.struct.enums.KType;
import com.tigerbrokers.stock.openapi.client.struct.enums.RightOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class QuoteDemo {
private static final TigerHttpClient client;
static {
ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG;
clientConfig.configFilePath = "./src/main/resources";
client = TigerHttpClient.getInstance().clientConfig(clientConfig);
}
public static void main(String[] args) {
new QuoteDemo().kline();
}
public void kline() {
List<String> symbols = new ArrayList<>();
symbols.add("AAPL");
QuoteKlineRequest request =
QuoteKlineRequest.newRequest(symbols, KType.day, "2022-10-01", "2022-12-25")
.withLimit(1000)
.withRight(RightOption.br);
QuoteKlineResponse response = client.execute(request);
if (response.isSuccess()) {
System.out.println(Arrays.toString(response.getKlineItems().toArray()));
} else {
System.out.println("response error:" + response.getMessage());
}
}
}import com.alibaba.fastjson.JSONObject;
import com.tigerbrokers.stock.openapi.client.config.ClientConfig;
import com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient;
import com.tigerbrokers.stock.openapi.client.https.domain.contract.item.ContractItem;
import com.tigerbrokers.stock.openapi.client.https.request.trade.TradeOrderRequest;
import com.tigerbrokers.stock.openapi.client.https.response.trade.TradeOrderResponse;
import com.tigerbrokers.stock.openapi.client.struct.enums.ActionType;
public class TradeDemo {
private static final TigerHttpClient client;
static {
ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG;
clientConfig.configFilePath = "./src/main/resources";
client = TigerHttpClient.getInstance().clientConfig(clientConfig);
}
public static void main(String[] args) {
new TradeDemo().placeUsStockOrder();
}
public void placeUsStockOrder() {
ContractItem contract = ContractItem.buildStockContract("AAPL", "USD");
TradeOrderRequest request =
TradeOrderRequest.buildLimitOrder(contract, ActionType.BUY, 1, 100.0);
TradeOrderResponse response = client.execute(request);
System.out.println(JSONObject.toJSONString(response));
}
}订阅采用异步推送模式:自定义回调函数绑定事件,服务器推送时自动调用。 Subscription uses async push: bind custom callbacks to events, auto-called on server push.
1. 定义回调接口 / Define Callback
实现 ApiComposeCallback 接口:
import com.tigerbrokers.stock.openapi.client.socket.ApiComposeCallback;
import com.tigerbrokers.stock.openapi.client.socket.data.pb.*;
import com.tigerbrokers.stock.openapi.client.socket.data.TradeTick;
import com.tigerbrokers.stock.openapi.client.struct.SubscribedSymbol;
import com.tigerbrokers.stock.openapi.client.util.ApiLogger;
import com.tigerbrokers.stock.openapi.client.util.ProtoMessageUtil;
public class DefaultApiComposeCallback implements ApiComposeCallback {
@Override
public void orderStatusChange(OrderStatusData data) {
ApiLogger.info("orderStatusChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void positionChange(PositionData data) {
ApiLogger.info("positionChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void assetChange(AssetData data) {
ApiLogger.info("assetChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void quoteChange(QuoteBasicData data) {
ApiLogger.info("quoteChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void quoteAskBidChange(QuoteBBOData data) {
ApiLogger.info("quoteAskBidChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void depthQuoteChange(QuoteDepthData data) {
ApiLogger.info("depthQuoteChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void klineChange(KlineData data) {
ApiLogger.info("klineChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void optionChange(QuoteBasicData data) {
ApiLogger.info("optionChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void futureChange(QuoteBasicData data) {
ApiLogger.info("futureChange:" + ProtoMessageUtil.toJson(data));
}
@Override
public void tradeTickChange(TradeTick data) {
ApiLogger.info("tradeTickChange:" + data);
}
@Override
public void connectionAck() {
ApiLogger.info("connect success.");
}
@Override
public void connectionClosed() {
ApiLogger.info("connection closed.");
}
@Override
public void error(String errorMsg) {
ApiLogger.info("receive error:" + errorMsg);
}
@Override
public void error(int id, int errorCode, String errorMsg) {
ApiLogger.info("error id:" + id + ",code:" + errorCode + ",msg:" + errorMsg);
}
// ... 其他回调方法参见 ApiComposeCallback 接口
}2. 进行订阅 / Subscribe
import com.tigerbrokers.stock.openapi.client.config.ClientConfig;
import com.tigerbrokers.stock.openapi.client.socket.WebSocketClient;
import java.util.HashSet;
import java.util.Set;
public class SubscribeDemo {
private static ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG;
private static WebSocketClient client;
static {
clientConfig.configFilePath = "/data/tiger_config";
client = WebSocketClient.getInstance()
.clientConfig(clientConfig)
.apiComposeCallback(new DefaultApiComposeCallback());
}
public static void main(String[] args) {
new SubscribeDemo().subscribe();
}
public void subscribe() {
client.connect();
Set<String> symbols = new HashSet<>();
// 股票订阅 / Stock subscription
symbols.add("AAPL");
symbols.add("SPY");
client.subscribeQuote(symbols);
// 期货订阅 / Futures subscription
symbols.clear();
symbols.add("ESmain");
client.subscribeFuture(symbols);
// 期权订阅 / Options subscription
symbols.clear();
symbols.add("AAPL 20190614 200.0 CALL");
// 或21位格式 / Or 21-char format:
// symbols.add("SPY 190508C00290000");
client.subscribeOption(symbols);
// 深度数据 / Depth data
client.subscribeDepthQuote(symbols);
// 逐笔数据 / Trade tick
client.subscribeTradeTick(symbols);
// 查询订阅详情 / Query subscriptions
client.getSubscribedSymbols();
// 断开连接时自动注销订阅 / Auto-unsubscribe on disconnect
// client.disconnect();
}
}| 对象 Object | 包路径 Package | 功能 Purpose |
|---|---|---|
TigerHttpClient |
client.https.client |
HTTP 客户端,发送交易类和行情类请求 |
WebSocketClient |
client.socket |
WebSocket 客户端,处理订阅推送请求 |
| 包路径 Package | 说明 Description |
|---|---|
client.constant |
常量:MethodName(API方法)、OrderChangeKey、PositionChangeKey、AssetChangeKey |
client.https.client |
TigerHttpClient HTTP请求客户端 |
client.https.domain |
返回的业务数据对象 |
client.https.request |
封装的请求对象 |
client.https.response |
封装的返回对象 |
client.socket |
ApiComposeCallback(回调接口)、WebSocketClient(长连接客户端) |
client.struct.enums |
枚举与数据结构 |
client.util |
工具类 |
| 枚举 Enum | 说明 Description | 常用值 Values |
|---|---|---|
Market |
市场 | US, HK, CN, SG, AU, ALL |
SecType |
证券类型 | STK, OPT, FUT, WAR, IOPT, FUND, CASH, MLEG |
Currency |
货币 | USD, HKD, CNH, SGD, AUD |
ActionType |
交易方向 | BUY, SELL |
OrderType |
订单类型 | MKT, LMT, STP, STP_LMT, TRAIL |
KType |
K线周期 | day, week, month, year, min1, min3, min5, min10, min15, min30, min60 |
RightOption |
复权类型 | br(前复权), nr(不复权) |
Right |
期权方向 | CALL, PUT |
TimeInForce |
订单有效期 | DAY, GTC, GTD |
Category |
资产分类 | S(股票), C(期货) |
Language |
语言 | zh_CN, zh_TW, en_US |
TimeZoneId |
时区 | Shanghai, NewYork, LosAngeles, Chicago, HongKong, Singapore |
MethodName |
API方法名 | ACTIVE_ORDERS, FILLED_ORDERS, INACTIVE_ORDERS, CANCEL_ORDER, POSITIONS, ASSETS, GRAB_QUOTE_PERMISSION 等 |
License |
牌照 | TBUS, TBHK, TBSG, TBAU |
// 股票合约 / Stock contract
ContractItem stock = ContractItem.buildStockContract("AAPL", "USD");
// 期权合约 / Option contract
ContractItem option = ContractItem.buildOptionContract("AAPL", "CALL", "2024-01-19", 190.0);
// 期货合约 / Futures contract
ContractItem future = ContractItem.buildFutureContract("ES", "USD", "20240315");// 限价单 / Limit order
TradeOrderRequest.buildLimitOrder(contract, ActionType.BUY, quantity, limitPrice);
// 市价单 / Market order
TradeOrderRequest.buildMarketOrder(contract, ActionType.BUY, quantity);
// 止损单 / Stop order
TradeOrderRequest.buildStopOrder(contract, ActionType.SELL, quantity, auxPrice);
// 止损限价单 / Stop limit order
TradeOrderRequest.buildStopLimitOrder(contract, ActionType.SELL, quantity, limitPrice, auxPrice);用于调用未封装的API方法 / For calling unwrapped API methods:
TigerHttpRequest request = new TigerHttpRequest(MethodName.POSITIONS);
String bizContent = AccountParamBuilder.instance()
.account(clientConfig.defaultAccount)
.market(Market.US)
.secType(SecType.STK)
.buildJson();
request.setBizContent(bizContent);
TigerHttpResponse response = client.execute(request);注意:本策略仅为接口使用示例,策略本身未经验证,不构成投资建议。 Note: This strategy is only a usage example. Not investment advice.
基本思路:定期选取纳斯达克100成份股中周期内涨幅最高的若干股票买入持有,对未入选的持仓平仓。 Logic: Periodically select top momentum stocks from Nasdaq-100, buy and hold, close positions not selected.
import static java.lang.Thread.sleep;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tigerbrokers.stock.openapi.client.config.ClientConfig;
import com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient;
import com.tigerbrokers.stock.openapi.client.https.domain.contract.item.ContractItem;
import com.tigerbrokers.stock.openapi.client.https.domain.quote.item.KlineItem;
import com.tigerbrokers.stock.openapi.client.https.domain.quote.item.KlinePoint;
import com.tigerbrokers.stock.openapi.client.https.domain.trade.item.PrimeAssetItem;
import com.tigerbrokers.stock.openapi.client.https.request.TigerHttpRequest;
import com.tigerbrokers.stock.openapi.client.https.request.quote.QuoteKlineRequest;
import com.tigerbrokers.stock.openapi.client.https.request.quote.QuoteRealTimeQuoteRequest;
import com.tigerbrokers.stock.openapi.client.https.request.trade.PrimeAssetRequest;
import com.tigerbrokers.stock.openapi.client.https.request.trade.TradeOrderRequest;
import com.tigerbrokers.stock.openapi.client.https.response.TigerHttpResponse;
import com.tigerbrokers.stock.openapi.client.https.response.quote.QuoteKlineResponse;
import com.tigerbrokers.stock.openapi.client.https.response.quote.QuoteRealTimeQuoteResponse;
import com.tigerbrokers.stock.openapi.client.https.response.trade.PrimeAssetResponse;
import com.tigerbrokers.stock.openapi.client.https.response.trade.TradeOrderResponse;
import com.tigerbrokers.stock.openapi.client.struct.enums.*;
import com.tigerbrokers.stock.openapi.client.util.builder.AccountParamBuilder;
import com.tigerbrokers.stock.openapi.client.util.builder.TradeParamBuilder;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class Nasdaq100 {
private static final ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG;
private static final TigerHttpClient client;
static {
clientConfig.configFilePath = "./src/main/resources";
client = TigerHttpClient.getInstance().clientConfig(clientConfig);
}
private static final int HISTORY_DAYS = 100;
private static final int BATCH_SIZE = 50;
private static final int REQUEST_SYMBOLS_SIZE = 50;
private static final int MOMENTUM_PERIOD = 30;
private static final int HOLDING_NUM = 5;
private static final int ORDERS_CHECK_MAX_TIMES = 20;
private static final double TARGET_OVERNIGHT_LIQUIDATION_RATIO = 0.6;
private List<String> selectedSymbols = new ArrayList<>();
private static final Logger logger = Logger.getLogger(Nasdaq100.class.getName());
private final List<String> UNIVERSE_NDX = Arrays.asList(
"AAPL", "ADBE", "ADI", "ADP", "ADSK", "AEP", "ALGN", "AMAT", "AMD", "AMGN", "AMZN",
"ANSS", "ASML", "ATVI", "AVGO", "BIDU", "BIIB", "BKNG", "CDNS", "CDW", "CERN", "CHKP",
"CHTR", "CMCSA", "COST", "CPRT", "CRWD", "CSCO", "CSX", "CTAS", "CTSH", "DLTR", "DOCU",
"DXCM", "EA", "EBAY", "EXC", "FAST", "FB", "FISV", "FOX", "GILD", "GOOG", "HON", "IDXX",
"ILMN", "INCY", "INTC", "INTU", "ISRG", "JD", "KDP", "KHC", "KLAC", "LRCX", "LULU", "MAR",
"MCHP", "MDLZ", "MELI", "MNST", "MRNA", "MRVL", "MSFT", "MTCH", "MU", "NFLX", "NTES",
"NVDA", "NXPI", "OKTA", "ORLY", "PAYX", "PCAR", "PDD", "PEP", "PTON", "PYPL", "QCOM",
"REGN", "ROST", "SBUX", "SGEN", "SIRI", "SNPS", "SPLK", "SWKS", "TCOM", "TEAM", "TMUS",
"TSLA", "TXN", "VRSK", "VRSN", "VRTX", "WBA", "WDAY", "XEL", "XLNX", "ZM");
/** 运行策略 / Run strategy */
public void run() throws InterruptedException {
grabQuotePerm();
screenStocks();
rebalancePortfolio();
}
/** 选股:计算周期内涨跌幅,选取涨幅最高几只 / Screen: pick top momentum stocks */
public void screenStocks() throws InterruptedException {
Map<String, List<KlinePoint>> history = getHistory(UNIVERSE_NDX, HISTORY_DAYS, BATCH_SIZE);
Map<String, Double> momentum = new HashMap<>();
history.forEach((symbol, klinePoints) -> {
int size = klinePoints.size();
Double priceChange =
(klinePoints.get(size - 1).getClose() - klinePoints.get(size - MOMENTUM_PERIOD).getClose())
/ klinePoints.get(size - MOMENTUM_PERIOD).getClose();
momentum.put(symbol, priceChange);
});
Map<String, Double> sortedMap = momentum.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(HOLDING_NUM)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
selectedSymbols = new ArrayList<>(sortedMap.keySet());
logger.info("selected symbols:" + selectedSymbols);
}
/** 调仓 / Rebalance portfolio */
public void rebalancePortfolio() throws InterruptedException {
if (selectedSymbols.isEmpty()) {
logger.warning("no selected symbols, strategy exit!");
return;
}
closePosition();
openPosition(getAdjustValue());
}
/** 获取调仓金额 / Get rebalance amount */
private Double getAdjustValue() {
PrimeAssetItem.Segment asset = getPrimeAsset();
double targetOvernightLiquidation = asset.getEquityWithLoan() * TARGET_OVERNIGHT_LIQUIDATION_RATIO;
return asset.getOvernightLiquidation() - targetOvernightLiquidation;
}
/** 平仓未入选股票 / Close positions not selected */
private void closePosition() throws InterruptedException {
Map<String, Integer> positions = getPositions();
Set<String> needCloseSymbols = positions.keySet();
for (String selectedSymbol : selectedSymbols) {
needCloseSymbols.remove(selectedSymbol);
}
if (!needCloseSymbols.isEmpty()) {
Map<String, Double> latestPrice = getQuote(new ArrayList<>(needCloseSymbols));
List<TradeOrderRequest> orderRequests = new ArrayList<>();
needCloseSymbols.forEach(symbol -> {
ContractItem contract = ContractItem.buildStockContract(symbol, Currency.USD.name());
TradeOrderRequest request =
TradeOrderRequest.buildLimitOrder(contract, ActionType.SELL, positions.get(symbol), latestPrice.get(symbol));
orderRequests.add(request);
});
executeOrders(orderRequests);
}
}
/** 开仓入选股票 / Open positions for selected stocks */
private void openPosition(Double adjustValue) throws InterruptedException {
double adjustValuePerStock = adjustValue / HOLDING_NUM;
if (adjustValue <= 0) {
logger.info("no enough liquidation");
return;
}
Map<String, Double> latestPrice = getQuote(selectedSymbols);
List<TradeOrderRequest> orders = new ArrayList<>();
for (String symbol : selectedSymbols) {
int quantity = (int) (adjustValuePerStock / latestPrice.get(symbol));
if (quantity == 0) continue;
ContractItem contract = ContractItem.buildStockContract(symbol, Currency.USD.name());
TradeOrderRequest request =
TradeOrderRequest.buildLimitOrder(contract, ActionType.BUY, quantity, latestPrice.get(symbol));
orders.add(request);
}
executeOrders(orders);
}
/** 执行订单 / Execute orders */
private void executeOrders(List<TradeOrderRequest> orderRequests) throws InterruptedException {
orderRequests.forEach(order -> {
TradeOrderResponse response = client.execute(order);
logger.info("place order: " + response);
});
sleep(20000);
int i = 0;
while (i <= ORDERS_CHECK_MAX_TIMES) {
JSONArray openOrders = getOrders(MethodName.ACTIVE_ORDERS);
if (openOrders.isEmpty()) break;
if (i >= ORDERS_CHECK_MAX_TIMES) {
for (int k = 0; k < openOrders.size(); ++k) {
cancelOrder(openOrders.getJSONObject(k).getLong("id"));
}
}
i++;
sleep(20000);
}
}
/** 撤单 / Cancel order */
private void cancelOrder(Long id) {
TigerHttpRequest request = new TigerHttpRequest(MethodName.CANCEL_ORDER);
String bizContent = TradeParamBuilder.instance().account(clientConfig.defaultAccount).id(id).buildJson();
request.setBizContent(bizContent);
client.execute(request);
}
/** 获取订单 / Get orders */
private JSONArray getOrders(MethodName apiServiceType) {
TigerHttpRequest request = new TigerHttpRequest(apiServiceType);
String bizContent = AccountParamBuilder.instance()
.account(clientConfig.defaultAccount)
.startDate(Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli())
.endDate(Instant.now().toEpochMilli())
.secType(SecType.STK).market(Market.US).isBrief(false).buildJson();
request.setBizContent(bizContent);
TigerHttpResponse response = client.execute(request);
return JSON.parseObject(response.getData()).getJSONArray("items");
}
/** 获取综合/模拟账户资产 / Get prime assets */
private PrimeAssetItem.Segment getPrimeAsset() {
PrimeAssetRequest assetRequest = PrimeAssetRequest.buildPrimeAssetRequest(clientConfig.defaultAccount);
PrimeAssetResponse response = client.execute(assetRequest);
return response.getSegment(Category.S);
}
/** 获取持仓 / Get positions */
private Map<String, Integer> getPositions() {
Map<String, Integer> result = new HashMap<>();
TigerHttpRequest request = new TigerHttpRequest(MethodName.POSITIONS);
String bizContent = AccountParamBuilder.instance()
.account(clientConfig.defaultAccount).market(Market.US).secType(SecType.STK).buildJson();
request.setBizContent(bizContent);
TigerHttpResponse response = client.execute(request);
if (response.getData() == null || response.getData().isEmpty()) return result;
JSONArray positions = JSON.parseObject(response.getData()).getJSONArray("items");
for (int i = 0; i < positions.size(); ++i) {
JSONObject pos = positions.getJSONObject(i);
result.put(pos.getString("symbol"), pos.getInteger("quantity"));
}
return result;
}
/** 抢占行情权限 / Grab quote permission */
private void grabQuotePerm() {
TigerHttpRequest request = new TigerHttpRequest(MethodName.GRAB_QUOTE_PERMISSION);
request.setBizContent(AccountParamBuilder.instance().buildJson());
TigerHttpResponse response = client.execute(request);
logger.info("quote permissions: " + response.getData());
}
/** 获取实时行情 / Get real-time quotes */
private Map<String, Double> getQuote(List<String> symbols) throws InterruptedException {
Map<String, Double> quote = new HashMap<>();
Collection<List<String>> partitions = partition(symbols, REQUEST_SYMBOLS_SIZE);
for (List<String> part : partitions) {
QuoteRealTimeQuoteResponse response = client.execute(QuoteRealTimeQuoteRequest.newRequest(part));
if (response.isSuccess()) {
response.getRealTimeQuoteItems().forEach(item -> quote.put(item.getSymbol(), item.getLatestPrice()));
}
sleep(1000);
}
return quote;
}
/** 获取历史行情 / Get historical klines */
private Map<String, List<KlinePoint>> getHistory(List<String> symbols, int total, int batchSize)
throws InterruptedException {
Map<String, List<KlinePoint>> history = new HashMap<>();
Collection<List<String>> partitions = partition(symbols, REQUEST_SYMBOLS_SIZE);
for (List<String> part : partitions) {
int i = 0;
long endTime = Instant.now().toEpochMilli();
while (i < total) {
QuoteKlineRequest request = QuoteKlineRequest.newRequest(part, KType.day, -1L, endTime);
request.withLimit(batchSize);
QuoteKlineResponse response = client.execute(request);
if (response.isSuccess()) {
for (KlineItem item : response.getKlineItems()) {
List<KlinePoint> points = history.getOrDefault(item.getSymbol(), new ArrayList<>());
points.addAll(item.getItems());
endTime = item.getItems().get(0).getTime();
history.put(item.getSymbol(), points);
}
}
i += batchSize;
sleep(1000);
}
}
Map<String, List<KlinePoint>> sorted = new HashMap<>();
history.forEach((symbol, points) -> {
points.sort(Comparator.comparingLong(KlinePoint::getTime));
sorted.put(symbol, points);
});
return sorted;
}
static <T> Collection<List<T>> partition(List<T> list, int size) {
final AtomicInteger counter = new AtomicInteger(0);
return list.stream().collect(Collectors.groupingBy(s -> counter.getAndIncrement() / size)).values();
}
public static void main(String[] args) throws InterruptedException {
new Nasdaq100().run();
}
}期权计算工具可用于希腊值计算、预测价格、隐含波动率计算,基于 jquantlib 库。
Option calc tool for Greeks, predicted price, implied volatility. Based on jquantlib.
不支持末日期权的价格预测 / Does not support expiry-day price prediction.
import com.alibaba.fastjson.JSONObject;
import com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient;
import com.tigerbrokers.stock.openapi.client.https.domain.option.item.OptionBriefItem;
import com.tigerbrokers.stock.openapi.client.https.domain.option.model.OptionCommonModel;
import com.tigerbrokers.stock.openapi.client.https.domain.quote.item.RealTimeQuoteItem;
import com.tigerbrokers.stock.openapi.client.https.request.option.OptionBriefQueryRequest;
import com.tigerbrokers.stock.openapi.client.https.request.quote.QuoteRealTimeQuoteRequest;
import com.tigerbrokers.stock.openapi.client.https.response.option.OptionBriefResponse;
import com.tigerbrokers.stock.openapi.client.https.response.quote.QuoteRealTimeQuoteResponse;
import com.tigerbrokers.stock.openapi.client.struct.OptionFundamentals;
import com.tigerbrokers.stock.openapi.client.struct.enums.Right;
import com.tigerbrokers.stock.openapi.client.struct.enums.TimeZoneId;
import com.tigerbrokers.stock.openapi.client.util.DateUtils;
import com.tigerbrokers.stock.openapi.client.util.OptionCalcUtils;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
public class OptionCalcTool {
public static void main(String[] args) {
// 初始化客户端 (省略配置代码) / Init client (config omitted)
TigerHttpClient client = TigerHttpClient.getInstance().clientConfig(ClientConfig.DEFAULT_CONFIG);
String symbol = "AAPL";
Right optionRight = Right.PUT;
String strike = "185.0";
String settlementDate = "2024-02-05"; // 一般是当前日期 / Usually current date
String expiryDate = "2024-02-09";
long expiryTimestamp = DateUtils.parseEpochMill(expiryDate, TimeZoneId.NewYork);
// 查询期权盘口价格和国债利率 / Query option brief and treasury rates
OptionCommonModel model = new OptionCommonModel(symbol, optionRight.name(), strike, expiryTimestamp);
OptionBriefResponse optionBriefResponse = client.execute(OptionBriefQueryRequest.of(model));
OptionBriefItem optionBrief = optionBriefResponse.getOptionBriefItems().get(0);
// 查询标的最新价格 / Get underlying latest price
QuoteRealTimeQuoteResponse quoteResponse =
client.execute(QuoteRealTimeQuoteRequest.newRequest(Arrays.asList(symbol)));
Double latestPrice = quoteResponse.getRealTimeQuoteItems().get(0).getLatestPrice();
// 注意:结算日期=过期日时,需改为前一天 / If settlement == expiry, use day before
if (settlementDate.equals(expiryDate)) {
settlementDate = LocalDate.parse(settlementDate).minusDays(1).toString();
}
// 美式期权计算 / American option calculation
OptionFundamentals result = OptionCalcUtils.calcOptionIndex(
optionRight, latestPrice, Double.valueOf(strike),
optionBrief.getRatesBonds(), 0,
optionBrief.getAskPrice(), optionBrief.getBidPrice(),
LocalDate.parse(settlementDate), LocalDate.parse(expiryDate));
// 欧式期权(指数期权)使用 / For European options (index options):
// OptionCalcUtils.calcEuropeanOptionIndex(...)
System.out.println(JSONObject.toJSONString(result));
}
}import com.tigerbrokers.stock.openapi.client.struct.OptionFundamentals;
import com.tigerbrokers.stock.openapi.client.struct.enums.Right;
import com.tigerbrokers.stock.openapi.client.util.OptionCalcUtils;
import java.time.LocalDate;
OptionFundamentals result = OptionCalcUtils.calcOptionIndex(
Right.CALL,
243.35, // 标的资产价格 / Underlying price
240, // 行权价格 / Strike price
0.0241, // 无风险利率(美国国债) / Risk-free rate (treasury)
0, // 股息率 / Dividend yield
0.4648, // 隐含波动率 / Implied volatility
LocalDate.of(2022, 8, 12), // 预测日期(< 到期日) / Prediction date
LocalDate.of(2022, 8, 19)); // 到期日 / Expiry date
System.out.println("predictedValue: " + result.getPredictedValue());
System.out.println("delta: " + result.getDelta());
System.out.println("gamma: " + result.getGamma());
System.out.println("theta: " + result.getTheta());
System.out.println("vega: " + result.getVega());
System.out.println("rho: " + result.getRho());输出示例 / Sample output:
predictedValue: 8.09628869758489
delta: 0.6005809882595446
gamma: 0.024620648675961896
theta: -0.4429512650168838
vega: 0.13035018339603335
rho: 0.026470369092588868
官方示例仓库 / Official demo repository: https://github.com/tigerfintech/openapi-java-sdk-demo
- Java SDK 使用 PKCS#8 格式私钥 / Java SDK uses PKCS#8 format private key
TigerHttpClient和ClientConfig建议配置为全局静态变量复用 / Recommend as global static singletons- 行情权限需单独购买,API 与 App 独立 / Quote permissions require separate purchase
- 同一 tiger_id 只能有一个 WebSocket 长连接 / Only one WebSocket connection per tiger_id
- 机构用户需额外配置
secretKey/ Institutional users must setsecretKey - TBHK 牌照需配置 token,有效期 30 天,可配置自动刷新 / TBHK license needs token (30-day expiry, auto-refresh available)
- 官方文档 / Official docs: https://quant.itigerup.com/openapi/java/overview/introduction