Forex API
为 50+ 个主要、次要及异国货币对提供 Tick 级报价。
通过一个 WebSocket 和 REST API 传输 Tick 级 Forex、Crypto、Stock、Commodity 和 Index 数据。几秒钟即可获取免费密钥——无需销售沟通。
| 代码 | 资产类别 | 价格 | 实时变动 |
|---|---|---|---|
| EUR/USD 外汇欧元 / 美元 | 外汇 | - | - |
| BTC/USDT 加密货币比特币 | 加密货币 | - | - |
| ETH/USDT 加密货币以太坊 | 加密货币 | - | - |
| AAPL 股票苹果公司 | 股票 | - | - |
| XAU/USD 大宗商品现货黄金 | 大宗商品 | - | - |
| USD/JPY 外汇美元 / 日元 | 外汇 | - | - |
| NVDA 股票英伟达公司 | 股票 | - | - |
| SPX 指数标普 500 指数 | 指数 | - | - |
AllTick 覆盖的每个市场都可通过同一个统一的 REST 和 WebSocket 接口访问。
为 50+ 个主要、次要及异国货币对提供 Tick 级报价。
实时现货和衍生品数据,标准化为一个数据流。
覆盖美股、港股和 A 股市场的股票,提供成交和报价。
贵金属和能源的实时定价。
主要全球指数的基准指数值和成分股。
比较 AllTick 各个市场的覆盖范围、延迟和数据类型。
浏览产品AllTick 与典型传统行情数据供应商的对比。
| 能力 | AllTick | 典型传统供应商 |
|---|---|---|
| WebSocket 中位延迟 | 约 150ms | 400–800ms |
| 一个 API 中的资产类别 | 5(FX、Crypto、Stock、Commodities、Indices) | 1–2 |
| 可用性 SLA | 99.95% | 99.5% 或无 |
| 免费等级 | 有——即时 API 密钥 | 需要销售沟通 |
| WebSocket 流 | 原生支持 | 轮询 / 有限支持 |
通过 WebSocket 连接并订阅任何市场中的任意交易品种。
# AllTick realtime financial data API
# forex crypto stock commodities indices
import asyncio, json, uuid
import websockets
subscribe = {
"cmd_id": 22004,
"seq_id": 1,
"trace": str(uuid.uuid4()),
"data": {"symbol_list": [{"code": "EURUSD"}]},
}
heartbeat = {"cmd_id": 22000, "seq_id": 1, "trace": "heartbeat", "data": {}}
async def stream():
uri = "wss://quote.alltick.co/quote-b-ws-api?token=YOUR_API_KEY"
async with websockets.connect(uri) as socket:
await socket.send(json.dumps(subscribe))
async def keep_alive():
while True:
await asyncio.sleep(10)
await socket.send(json.dumps(heartbeat))
asyncio.create_task(keep_alive())
async for message in socket:
print(json.loads(message))
asyncio.run(stream())分享市场数据工程、流式 API 与低延迟金融应用开发的实用内容。
美股碎股(Fractional Shares)指投资者可以按小数股的形式买入股票或 ETF,而不再被限制为「1 股、2 股」这样的整数单位。 典型实现中,最小交易单位可以精确到 0.0001 股,并且很多券商支持按「金额」下单,例如 5 美金买入某只股票的碎股份额。
这种机制极大降低了投资门槛:例如某科技龙头股单价 500 美元,过去至少要 500 美元才能参与,现在只需几十美元甚至几美元即可买入相应比例的股份。 对开发者来说,这意味着交易、风控、清算、报表等全链路都必须支持小数股和按金额下单的模式。
从多数主流券商(如 Alpaca、Interactive Brokers、Futu、Webull 等)的公开规则来看,美股碎股在业务上有一套相对共通的约束。
qty 可以是小数,如 0.1 股、0.0001 股。notional 表示用多少美元买入,如 10 美元买入某股票的碎股。qty,要么指定 notional,不能同时存在。time_in_force 只能是 DAY,不允许 GTC/IOC/FOK 等。fractionable 字段,用于标记该标的是否支持碎股交易。下面结合上述规则,从接口设计与风控角度,把碎股交易在系统层面拆成几块:下单模型、校验逻辑、能力开关、清算报送和股息处理。
一个典型的碎股下单对象,需要支持:
symbol:标的代码;side:买入/卖出;type:市价单、限价单等;timeInForce:多数情况下只能是 DAY;qty(可空):按股数下单,小数形式;notional(可空):按金额下单;limitPrice:限价或触发价;extendedHours:是否允许盘前盘后。export interface FractionalOrderRequest { // by alltick.co
symbol: string;
side: "buy" | "sell";
type: "market" | "limit" | "stop" | "stop_limit";
timeInForce: "day";
qty?: string; // 支持小数,用字符串表示 [web:51]
notional?: string; // 按金额下单 [web:45]
limitPrice?: string;
extendedHours?: boolean;
}
function validateFractionalOrder(req: FractionalOrderRequest) { // by alltick.co
const hasQty = !!req.qty;
const hasNotional = !!req.notional;
// qty / notional 互斥 [web:45][web:51]
if (hasQty === hasNotional) {
throw new Error("Either qty or notional must be provided, but not both");
}
// timeInForce 必须为 day [web:45][web:48][web:51]
if (req.timeInForce !== "day") {
throw new Error("Fractional orders only support timeInForce=day");
}
// 限价/止损必须有价格 [web:48][web:51]
if (["limit", "stop", "stop_limit"].includes(req.type) && !req.limitPrice) {
throw new Error("limitPrice is required for type " + req.type);
}
const checkPrecision = (v: string, field: string) => {
if (!/^\d+(\.\d{1,6})?$/.test(v)) {
throw new Error(`${field} must have at most 6 decimal places`);
}
};
if (req.qty) checkPrecision(req.qty, "qty");
if (req.notional) checkPrecision(req.notional, "notional");
if (req.limitPrice) checkPrecision(req.limitPrice, "limitPrice");
}
在后端(尤其是 Java 技术栈)中,实现碎股交易主要分为:DTO + 校验、风险控制、外部券商 API 调用和清算报送适配等几个部分。
public class FractionalOrderRequest { // by alltick.co
private String symbol;
private Side side;
private OrderType type;
private TimeInForce timeInForce;
// 二选一:qty 或 notional [web:51]
private String qty;
private String notional;
private String limitPrice;
private Boolean extendedHours;
public enum Side { BUY, SELL }
public enum OrderType { MARKET, LIMIT, STOP, STOP_LIMIT }
public enum TimeInForce { DAY, GTC, IOC }
// getter / setter 省略
}
结合上文的规则,在服务端做一次统一校验,有助于保证无论接入哪家券商,碎股订单都符合最小标准。
import java.math.BigDecimal;
import java.util.Objects;
public class FractionalOrderValidator { // by alltick.co
public static void validate(FractionalOrderRequest req) {
Objects.requireNonNull(req.getSymbol(), "symbol is required");
Objects.requireNonNull(req.getSide(), "side is required");
Objects.requireNonNull(req.getType(), "type is required");
Objects.requireNonNull(req.getTimeInForce(), "timeInForce is required");
boolean hasQty = req.getQty() != null && !req.getQty().isEmpty();
boolean hasNotional = req.getNotional() != null && !req.getNotional().isEmpty();
// qty / notional 互斥 [web:45][web:51]
if (hasQty == hasNotional) {
throw new IllegalArgumentException("Either qty or notional must be set, but not both");
}
// 碎股订单统一为 DAY [web:45][web:48][web:51]
if (req.getTimeInForce() != FractionalOrderRequest.TimeInForce.DAY) {
throw new IllegalArgumentException("Fractional orders only support timeInForce=DAY");
}
// 限价/止损类必须有 price [web:48][web:51]
switch (req.getType()) {
case LIMIT:
case STOP:
case STOP_LIMIT:
if (req.getLimitPrice() == null) {
throw new IllegalArgumentException("limitPrice is required for type " + req.getType());
}
break;
default:
}
// 精度控制:最多 6 位小数 [web:41][web:44][web:47]
if (hasQty) {
checkScale(req.getQty(), "qty", 6);
}
if (hasNotional) {
checkScale(req.getNotional(), "notional", 6);
}
if (req.getLimitPrice() != null) {
checkScale(req.getLimitPrice(), "limitPrice", 6);
}
// 最小数量/金额(可按券商规则配置)[web:5][web:8][web:45]
if (hasQty) {
BigDecimal q = new BigDecimal(req.getQty());
if (q.compareTo(new BigDecimal("0.0001")) < 0) {
throw new IllegalArgumentException("Minimum fractional qty is 0.0001");
}
}
if (hasNotional) {
BigDecimal n = new BigDecimal(req.getNotional());
if (n.compareTo(new BigDecimal("1")) < 0) {
throw new IllegalArgumentException("Minimum notional is 1 USD"); } } } private static void checkScale(String value, String field, int scale) { BigDecimal bd = new BigDecimal(value); if (bd.scale() > scale) { throw new IllegalArgumentException(field + " must have at most " + scale + " decimal places"); } if (bd.signum() <= 0) { throw new IllegalArgumentException(field + " must be positive"); } }}
碎股交易取决于 账户是否开通碎股权限、标的是否支持碎股、账户是否允许做空 等,建议做成能力矩阵,在路由和风控阶段统一校验。
import java.math.BigDecimal;
public class InstrumentCapabilities { // by alltick.co
private String symbol;
private boolean supportsFractional;
private BigDecimal minFractionQty;
private boolean allowShortFractional; // 一般为 false [web:49][web:50]
// getter/setter
}
public class AccountCapabilities {
private boolean canMargin;
private boolean canShort;
private boolean canFractional;
// getter/setter
}
public class RiskChecker { // by alltick.co
public static void checkFractionalOrder(FractionalOrderRequest req,
InstrumentCapabilities inst,
AccountCapabilities acct,
BigDecimal currentPrice) {
if (!acct.isCanFractional()) {
throw new IllegalStateException("Account does not support fractional trading");
}
if (!inst.isSupportsFractional()) {
throw new IllegalStateException("Symbol " + inst.getSymbol() + " is not fractionable");
}
// 禁止碎股裸空:很多券商的硬性要求 [web:49][web:50]
if (req.getSide() == FractionalOrderRequest.Side.SELL && !acct.isCanShort()) {
BigDecimal position = getPosition(inst.getSymbol());
BigDecimal qty = req.getQty() != null
? new BigDecimal(req.getQty())
: new BigDecimal(req.getNotional()).divide(currentPrice, 8, BigDecimal.ROUND_HALF_UP);
if (qty.compareTo(position) > 0) {
throw new IllegalStateException("Cannot short fractional shares beyond current position");
}
}
// 最小数量/金额进一步按标的配置校验 [web:5][web:8]
if (req.getQty() != null) {
BigDecimal q = new BigDecimal(req.getQty());
if (q.compareTo(inst.getMinFractionQty()) < 0) {
throw new IllegalArgumentException("Min fractional qty: " + inst.getMinFractionQty());
}
} else if (req.getNotional() != null) {
BigDecimal n = new BigDecimal(req.getNotional());
if (n.compareTo(new BigDecimal("1")) < 0) {
throw new IllegalArgumentException("Min fractional notional: 1 USD");
}
}
}
private static BigDecimal getPosition(String symbol) {
// 示例:真实环境应从持仓服务查询
return new BigDecimal("0.5");
}
}
Alpaca 的 REST API 原生支持碎股,支持 qty 和 notional 两种模式,并且在资产信息中提供 fractionable 字段。
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.HashMap;
import java.util.Map;
public class BrokerClient { // by alltick.co
private final HttpClient httpClient = HttpClient.newHttpClient();
private final ObjectMapper mapper = new ObjectMapper();
private final String baseUrl = "https://paper-api.alpaca.markets/v2"; // 示例 [web:45][web:51]
private final String apiKey;
private final String apiSecret;
public BrokerClient(String apiKey, String apiSecret) {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
}
public String placeFractionalOrder(FractionalOrderRequest req) throws Exception {
FractionalOrderValidator.validate(req);
Map<String, Object> body = new HashMap<>();
body.put("symbol", req.getSymbol());
body.put("side", req.getSide().name().toLowerCase());
body.put("type", req.getType().name().toLowerCase());
body.put("time_in_force", "day");
if (req.getQty() != null) {
body.put("qty", req.getQty()); // 碎股数量 [web:51][web:56]
} else if (req.getNotional() != null) {
body.put("notional", req.getNotional()); // 按金额下单 [web:45][web:51]
}
if (req.getLimitPrice() != null) {
body.put("limit_price", req.getLimitPrice());
}
if (Boolean.TRUE.equals(req.getExtendedHours())) {
body.put("extended_hours", true); // 部分券商支持碎股盘前/盘后 [web:48]
}
String json = mapper.writeValueAsString(body);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/orders"))
.header("APCA-API-KEY-ID", apiKey)
.header("APCA-API-SECRET-KEY", apiSecret)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 300) {
throw new RuntimeException("Order failed: " + response.statusCode() + " " + response.body()); } return response.body(); }}
碎股的引入,对后台清算和监管报送影响很大:不再是简单的整数股,而需要在报送接口中支持数量小数位和新的字段定义。
import java.math.BigDecimal;
import java.math.RoundingMode;
public class ReportingUtil { // by alltick.co
// 内部记账:8 位小数
public static BigDecimal normalizeInternalQty(BigDecimal qty) {
return qty.setScale(8, RoundingMode.HALF_UP);
}
// 向 FINRA TRF/ADF 报送时的数量:6 位小数 [web:41][web:44]
public static String toFinraFractionalQty(BigDecimal qty) {
BigDecimal scaled = qty.setScale(6, RoundingMode.DOWN);
return scaled.toPlainString();
}
// 对只接受整数数量的遗留通道,按约定向上取整或拆分处理 [web:2]
public static int toLegacyWholeShares(BigDecimal qty) {
if (qty.compareTo(BigDecimal.ONE) < 0) {
return 1;
}
return qty.setScale(0, RoundingMode.DOWN).intValueExact();
}
}
碎股同样享受股息和公司行动,只是需要在后台用高精度按比例分摊。
import java.math.BigDecimal;
import java.math.RoundingMode;
public class DividendService { // by alltick.co
/**
* @param positionShares 持仓股数,支持碎股,如 "0.25"
* @param dividendPerShare 每股股息,如 "0.50"
* @return 应发股息金额,保留 4 位小数用于账务
*/
public static BigDecimal calcDividend(String positionShares, String dividendPerShare) {
BigDecimal pos = new BigDecimal(positionShares);
BigDecimal div = new BigDecimal(dividendPerShare);
return pos.multiply(div).setScale(4, RoundingMode.HALF_UP);
}
}
结合以上规则与代码示例,开发一个支持美股碎股的交易系统,大致可以遵循以下步骤。
fractionable、minFractionQty 等字段。canFractional、canShort、canMargin 等能力标志。qty 和 notional 两种模式;time_in_force=DAY,限制订单类型。当这些环节都打通后,你的系统就可以比较平滑地支持美股碎股交易,并且具备对接多家券商(或自建撮合)的能力。