quickfix-j/quickfixj

GitHub: quickfix-j/quickfixj

QuickFIX/J 是一个功能完整的开源 Java FIX 协议消息引擎,支持 FIX 4.0 至 FIXLatest 多版本,为金融电子交易系统提供标准化的消息收发与会话管理能力。

Stars: 1143 | Forks: 666

# QuickFIX/J [![Java CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/f9/f938c5cffc2b94aa1d41877bfe1a169b45bc88138f5e16ea85ae1252b29a76f2.svg)](https://github.com/quickfix-j/quickfixj/actions/workflows/maven.yml) [![CodeQL](https://static.pigsec.cn/wp-content/uploads/repos/cas/53/539e9a6bf48ad24469a4363bff3aa68124154549e26592783d3d8577f2acbbfc.svg)](https://github.com/quickfix-j/quickfixj/actions/workflows/codeql-analysis.yml) [![Sonatype Central](https://maven-badges.sml.io/sonatype-central/org.quickfixj/quickfixj-core/badge.svg)](https://maven-badges.sml.io/sonatype-central/org.quickfixj/quickfixj-core) 这是 QuickFIX/J 的官方项目仓库。 ## 简介 QuickFIX/J 是一个功能齐全的 FIX 协议消息引擎(支持 FIX 4.0 - 5.0SP2/FIXT1.1 以及 FIXLatest 版本)。 它是流行的 C++ QuickFIX 引擎的 100% Java 开源实现。 金融信息交换协议是一种专门为证券交易的实时电子交换而开发的消息传递标准。 FIX 是一个由 FIX Protocol, Ltd (FPL) 拥有和维护的公共领域规范。 了解更多信息,请访问项目网站:http://www.quickfixj.org。 ## 发行说明 查看 wiki:https://github.com/quickfix-j/quickfixj/wiki ## 提问 如需提问,请使用邮件列表 https://lists.sourceforge.net/lists/listinfo/quickfixj-users 或在 Stack Overflow 上提问 https://stackoverflow.com/questions/ask?tags=quickfixj 。 ## 问题 请在此处报告问题:https://github.com/quickfix-j/quickfixj/issues ## 安全性 QuickFIX/J 欢迎并感谢负责任的披露。贡献者将在发行说明和 Git 日志中获得适当的致谢。 对于 QuickFIX/J 本身的安全问题,请联系项目维护者:christoph.john-at-macd.com 对于 QuickFIX/J 所使用的库的安全问题,请联系相关项目团队(例如 Apache MINA:https://www.apache.org/security/ )。如果您认为这些问题可以通过 QuickFIX/J 被轻易利用,也欢迎按照上述方式跟进项目维护者,以便我们及时升级到新版本。 一旦 QuickFIX/J 中的安全问题得到修复,将通过用户邮件列表和其他适当渠道进行通知。 ## 构建说明 最快方法:克隆仓库并执行以下命令。 ``` $ mvnw clean package -Dmaven.javadoc.skip=true -DskipTests -PskipBundlePlugin,minimal-fix-latest ``` 较慢方法:如果您只想跳过验收测试套件: ``` $ mvnw clean package -Dmaven.javadoc.skip=true -DskipAT=true -PskipBundlePlugin,minimal-fix-latest ``` 最慢方法:如果您想运行所有测试: ``` $ mvnw clean package -Dmaven.javadoc.skip=true -PskipBundlePlugin,minimal-fix-latest ``` 注意:如果您想在 OSGi 环境中使用生成的 JAR 文件,您必须省略 `-PskipBundlePlugin` 选项。 ## 将项目导入 IDE 项目刚创建时,不会包含生成的 message 类,并且会发生编译错误!最好在将项目导入 IDE 之前先在命令行上编译一次。 如果在执行 `mvnw clean package` 编译后 IDE 报告了一些错误,请尝试使用 `mvnw clean install`,如下所示: ``` $ mvnw clean install -Dmaven.javadoc.skip=true -DskipTests -PskipBundlePlugin,minimal-fix-latest ``` ## 配置选项 https://quickfix-j.github.io/quickfixj/quickfixj-core/src/main/doc/usermanual/usage/configuration.html ## 基础知识 ### 相关项目 QuickFIX/J 在 `quickfixj-examples` 模块中包含了一些示例应用程序。此外,以下是一些相关项目的链接: Geoffrey Gershaw 编写的示例:https://github.com/ggershaw/Examples QuickFIX/J Spring Boot Starter 的示例:https://github.com/esanchezros/quickfixj-spring-boot-starter-examples QuickFIX/J 的 AssertJ 断言:https://github.com/esanchezros/assertj-quickfixj FixMock,一个受 WireMock 启发的 FIX 模拟库:https://github.com/esanchezros/fix-mock ### 创建 QuickFIX/J 应用程序 实现 `quickfix.Application` 接口。 通过在您的派生类中实现这些接口方法,您可以请求接收有关 FIX 引擎中发生的 event 的通知。您最应该关注的函数是 `fromApp`。 以下是这些函数为您提供的功能的说明。 `onCreate` 在 QFJ 创建新 session 时被调用。session 在应用程序的整个生命周期内存在并保持活动状态。无论对手方是否连接到 session,它都是存在的。一旦创建了 session,您就可以开始向其发送消息。如果没有人登录,消息将在与对手方建立连接时发送。 `onLogon` 会在与对手方建立有效登录时通知您。这在建立连接并且 FIX 登录过程完成(双方交换了有效的登录消息)时被调用。 `onLogout` 会在 FIX session 不再在线时通知您。这可能发生在正常的登出交换期间,或者是由于强制终止或网络连接丢失。 `toAdmin` 让您窥探正在从您的 FIX 引擎发送给对手方的管理消息。这通常对应用程序没有用处,但是提供它是为了方便您进行任何所需的日志记录。请注意,`quickfix.Message` 是可变的。这允许您在管理消息发送之前向其添加字段。 `toApp` 是一个针对正在发送给对手方的应用程序消息的回调。如果您在此方法中抛出 `DoNotSend` 异常,应用程序将不会发送该消息。如果应用程序被要求重新发送一条消息(例如不再适用于当前市场的订单),这通常非常有用。正在重新发送的消息在 header 中的 `PossDupFlag` 会被设置为 true;如果抛出 `DoNotSend` 异常且该标志设置为 true,将发送一个 sequence reset 来代替该消息。如果设置为 false,则不会发送该消息。请注意,`quickfix.Message` 是可变的。这允许您在应用程序消息发送之前向其添加字段。 `fromAdmin` 会在管理消息从对手方发送到您的 FIX 引擎时通知您。这对于对 `Logon` 消息进行额外验证(例如检查密码)非常有用。抛出 `RejectLogon` 异常将断开与对手方的连接。 `fromApp` 是您的 FIX 应用程序的核心入口点之一。每个应用程序级别的请求都会通过这里。例如,如果您的应用程序是卖方 OMS,这就是您接收新订单请求的地方。如果您是买方,您将在这里收到您的执行报告。如果抛出 `FieldNotFound` 异常,对手方将收到一个 reject,指示缺少条件必填字段。`Message` 类在尝试获取缺失的字段时会抛出此异常,因此您很少需要显式抛出它。您也可以抛出 `UnsupportedMessageType` 异常。这将导致对手方收到一个 reject,通知他们您的应用程序无法处理那些类型的消息。如果某个字段包含超出范围的值或您不支持的值,也可以抛出 `IncorrectTagValue` 异常。 下面的示例代码展示了如何启动一个监听 socket 的 FIX acceptor。如果您想要一个 initiator,只需将此代码片段中的 acceptor 替换为 `SocketInitiator`。此外还提供了 `ThreadedSocketInitiator` 和 `ThreadedSocketAcceptor` 类。这些类将为创建的每个 session 提供一个线程。如果您使用它们,必须确保您的应用程序是线程安全的。 有关 QuickFIX/J 线程模型的详细描述——包括单线程与每 session 一线程策略、定时器线程、心跳管理、队列背压以及对应用程序开发者的线程安全影响——请参阅 [docs/threading-model.md](docs/threading-model.md) 和 [docs/threading-developer-guide.md‎](docs/threading-developer-guide.md)。 ``` import quickfix.*; import java.io.FileInputStream; public class MyClass { public static void main(String args[]) throws Exception { if (args.length != 1) return; String fileName = args[0]; // FooApplication is your class that implements the Application interface Application application = new FooApplication(); SessionSettings settings = new SessionSettings(new FileInputStream(fileName)); MessageStoreFactory storeFactory = new FileStoreFactory(settings); LogFactory logFactory = new FileLogFactory(settings); MessageFactory messageFactory = new DefaultMessageFactory(); Acceptor acceptor = new SocketAcceptor (application, storeFactory, settings, logFactory, messageFactory); acceptor.start(); // while(condition == true) { do something; } acceptor.stop(); } } ``` ### 接收消息 您感兴趣查看的大多数消息都将到达您应用程序中重载的 `fromApp` 方法中。您可以使用不同程度类型安全性的方式从消息中提取字段。这里讨论的类型是 FIX 消息类型。 当应用程序向您传递一个 `Message` 类时,Java 类型检查器并不知道它是什么具体的 FIX 消息,您必须动态地确定它。然而,有一种方法可以让 Java 识别这种类型信息。 请记住,所有消息都包含一个 header 和一个 trailer。如果您想查看其中的字段,必须先调用 `getHeader()` 或 `getTrailer()` 来获取对它们的访问权限。否则,您访问它们的方式与访问消息正文中的字段完全一样。 QuickFIX/J 拥有与规范中定义的所有消息相对应的消息类。就像字段类一样,它们是直接根据 FIX 规范生成的。为了利用这一点,您必须使用提供的 `MessageCracker` 来拆解消息。 ``` import quickfix.*; import quickfix.field.*; public void fromApp(Message message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue { crack(message, sessionID); } public void onMessage(quickfix.fix42.NewOrderSingle message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue { ClOrdID clOrdID = new ClOrdID(); message.get(clOrdID); ClearingAccount clearingAccount = new ClearingAccount(); message.get(clearingAccount); } public void onMessage(quickfix.fix42.OrderCancelRequest message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue { ClOrdID clOrdID = new ClOrdID(); message.get(clOrdID); // compile time error!! field not defined for OrderCancelRequest ClearingAccount clearingAccount = new ClearingAccount(); message.get(clearingAccount); } ``` 为了使用它,您必须将 `MessageCracker` 作为 mixin 加入到您的应用程序中。这将为您提供 `crack` 方法,并允许您重载特定的消息处理函数。 任何您没有重载的函数默认都会抛出一个 `UnsupportedMessageType` 异常。 像这样定义您的应用程序: ``` import quickfix.Application; import quickfix.MessageCracker; public class MyApplication extends MessageCracker implements quickfix.Application { public void fromApp(Message message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue { crack(message, sessionID); } // Using annotation @Handler public void myEmailHandler(quickfix.fix50.Email email, SessionID sessionID) { // handler implementation } // By convention (notice different version of FIX. It's an error to have two handlers for the same message) // Convention is "onMessage" method with message object as first argument and SessionID as second argument public void onMessage(quickfix.fix44.Email email, SessionID sessionID) { // handler implementation } } ``` 如果您更愿意使用组合而不是继承 `MessageCracker`,您可以构造一个带有委托对象的消息 cracker。委托的消息处理方法将被自动发现。 为了向后兼容,仍然会为每个 FIX 版本生成消息 cracker,但定义您需要的特定处理程序会更加高效。 生成的类定义了该 FIX 版本定义的所有消息的处理程序。这要求 JVM 在加载 cracker 时加载这些类。大多数应用程序只需要处理 FIX 版本定义的消息中的一小部分,因此在那些情况下加载所有的消息类是过度的开销。 #### 用于接收消息的函数式接口 如果您更喜欢在处理接收到的消息时使用 lambda 表达式,那么可以使用 ApplicationFunctionalAdapterApplicationExtendedFunctionalAdapter 来注册对应用程序感兴趣的 event 的响应。 它们还允许以类型安全的方式注册对给定消息类型的关注。 ``` import quickfix.ApplicationFunctionalAdapter; import quickfix.SessionID; public class EmailForwarder { public void init(ApplicationFunctionalAdapter adapter) { adapter.addOnLogonListener(this::captureUsername); adapter.addFromAppListener(quickfix.fix44.Email.class, (e , s) -> forward(e)); } private void forward(quickfix.fix44.Email email) { // implementation } private void captureUsername(SessionID sessionID) { // implementation } } ``` ApplicationFunctionalAdapterApplicationExtendedFunctionalAdapter 支持对同一个 event 进行多次注册,并且注册的回调以 FIFO(先进先出)方式被调用。 然而,在针对特定消息类型(例如 quickfix.fix44.Email)的注册和不针对特定消息类型的注册之间,无法保证 FIFO。例如,在以下两个回调之间没有调用顺序的保证: ``` adapter.addFromAppListener((e , s) -> handleGeneral(e)); adapter.addFromAppListener(quickfix.fix44.Email.class, (e , s) -> handleSpecific(e)); ``` ### 发送消息 可以使用静态的 `Session.sendToTarget` 方法之一将消息发送给对手方。此方法有几种签名,分别是: ``` package quickfix; public static boolean sendToTarget(Message message) throws SessionNotFound public static boolean sendToTarget(Message message, SessionID sessionID) throws SessionNotFound public static boolean sendToTarget (Message message, String senderCompID, String targetCompID) throws SessionNotFound ``` 强烈建议的方法是使用类型安全的 message 类。通常,这应该是您创建消息的唯一方式。 在这里,构造函数接收所有必填字段,并为您添加正确的 `MsgType` 和 `BeginString`。此外,通过使用 `set` 方法而不是 `setField`,编译器将不允许您添加基于 FIX4.1 规范不属于 `OrderCancelRequest` 的字段。请记住,如果您想强制将任何字段添加到消息中,您仍然可以使用 `setField`。 ``` import quickfix.*; void sendOrderCancelRequest() throws SessionNotFound { quickfix.fix41.OrderCancelRequest message = new quickfix.fix41.OrderCancelRequest( new OrigClOrdID("123"), new ClOrdID("321"), new Symbol("LNUX"), new Side(Side.BUY)); message.set(new Text("Cancel My Order!")); Session.sendToTarget(message, "TW", "TARGET"); } ``` ## QuickFIX/J 运行时 本项目为从 FIX 4.0 到 FIX Latest 的标准发布 FIX 规范版本构建构件。 * ```quickfixj-messages-fix40``` * ```quickfixj-messages-fix41``` * ```quickfixj-messages-fix42``` * ```quickfixj-messages-fix43``` * ```quickfixj-messages-fix44``` * ```quickfixj-messages-fix50``` * ```quickfixj-messages-fix50sp1``` * ```quickfixj-messages-fix50sp2``` * ```quickfixj-messages-fixlatest``` * ```quickfixj-messages-fixt11``` * ```quickfixj-messages-all``` - 包含以上所有内容 这些构件是 ```quickfixj-core``` 的 **测试** 依赖项。它们**没有**被指定为 _runtime_ 依赖项,这使得自定义 QuickFIX/J 部署变得更加容易。 如果您不需要自定义 FIX 集成,您可以直接使用由本项目构建的 ```org.quickfixj``` 构件。只需将它们作为您应用程序的依赖项包含进去即可。 未使用的 FIX 规范版本的构件可以从您的 runtime 中省略。 许多集成将不需要 ```quickfixj-messages-all```,只需依赖于 FIX 标准版本子集的构件即可。请注意,FIX Protocol 5.0 及更高版本依赖于 ```quickfixj-messages-fixt11```,它提供了 FIXT1.1 传输消息的实现。 许多集成需要专门化 FIX Messages、Components 和/或 Fields。这是通过构建和使用自定义构件来完成的。详情请参阅[自定义 QuickFIX/J](./customising-quickfixj.md)。 ### QuickFIX/J 消息构建的应用程序依赖 ![image info](https://static.pigsec.cn/wp-content/uploads/repos/cas/f0/f0f545d6704d6786bac56f88a1370bebbc407915a0cb7194f8a481b9ec0e9550.png) ![image info](https://static.pigsec.cn/wp-content/uploads/repos/cas/fb/fbb75b365d33d609cc02e3fcfa60b0259932a04ce56514bdda08decbb05d38b6.png) ### 自定义消息构建的应用程序依赖 ![image info](https://static.pigsec.cn/wp-content/uploads/repos/cas/e8/e8596270e99fa06f0481e2a8fd0050ab532d4556cb3944b4fb281d3425ae9323.png) ![image info](https://static.pigsec.cn/wp-content/uploads/repos/cas/7f/7f2cb5e075d0c57147e67d3588079803a0924beb02563521a9ee5dd72b0f6b38.png) ### 使用 SNAPSHOTs 每日构建的 SNAPSHOT 发布在 **GitHub Packages** 上。 GitHub Packages 需要身份验证——即使是对于公开的代码库也是如此。 您需要使用一个 [Personal Access Token (PAT)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) 并具有 `read:packages` 权限。 #### Maven 设置: 在您的 `settings.xml` 文件中的 `servers` 下添加身份验证部分: ``` github-quickfixj USERNAME GITHUB_PAT ``` 在 `repositories` 下添加 GitHub Packages 代码库,并在 `dependencies` 中添加所需的 `SNAPSHOT` 版本: ``` github-quickfixj GitHub Packages for quickfixj https://maven.pkg.github.com/quickfix-j/quickfixj true org.quickfixj quickfixj-all 3.0.0-SNAPSHOT ``` #### Gradle 设置: 将以下内容添加到您的 `build.gradle` 文件中: ``` //build.gradle repositories { maven { url = uri("https://maven.pkg.github.com/quickfix-j/quickfixj") credentials { username = "USERNAME" // Your GitHub username password = "GITHUB_PAT" // Your GitHub PAT } } } ```
标签:FIX协议, JS文件枚举, 域名枚举, 开源, 消息引擎, 证券交易, 通信中间件, 金融