├── README.md ├── TradingMachineMonitorUI ├── .settings │ ├── org.eclipse.m2e.core.prefs │ ├── org.eclipse.core.resources.prefs │ └── org.eclipse.jdt.core.prefs ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── projects │ │ │ └── tradingMachine │ │ │ └── tradeMonitor │ │ │ ├── util │ │ │ ├── PanelCleanUp.java │ │ │ ├── SwingUtility.java │ │ │ ├── DatetimeTableCellRenderer.java │ │ │ ├── OrdersStats.java │ │ │ ├── MarketDataSummary.java │ │ │ ├── TooltipCellRenderer.java │ │ │ └── GenericListTableModel.java │ │ │ ├── MarketDataSummaryTableModel.java │ │ │ ├── OrdersStatsTableModel.java │ │ │ ├── MarketDataTableModel.java │ │ │ ├── RejectedOrdersTableModel.java │ │ │ ├── FilledOrdersTableModel.java │ │ │ ├── MarketDataPanel.java │ │ │ ├── OrdersPanel.java │ │ │ └── TradeMonitorUI.java │ │ └── resources │ │ ├── tradeMonitor.properties │ │ └── log4j2.xml ├── .project ├── .classpath └── pom.xml ├── TradingMachineServer ├── .settings │ ├── org.eclipse.m2e.core.prefs │ └── org.eclipse.jdt.core.prefs ├── src │ └── main │ │ ├── resources │ │ ├── tradingMachine.properties │ │ ├── tradingMachineFixEngine.properties │ │ └── log4j.properties │ │ └── java │ │ └── com │ │ └── projects │ │ └── tradingMachine │ │ └── server │ │ ├── MarketDataManager.java │ │ ├── TradingMachineServer.java │ │ ├── TradingMachineFixAcceptorApplication.java │ │ └── MatchingEngine.java ├── .project ├── .classpath └── pom.xml ├── TradingMachineServices ├── .settings │ ├── org.eclipse.m2e.core.prefs │ ├── org.eclipse.core.resources.prefs │ └── org.eclipse.jdt.core.prefs ├── .project ├── src │ └── main │ │ ├── resources │ │ ├── tradingMachineServices.properties │ │ ├── log4j2.xml │ │ └── MySqlScripts.sql │ │ └── java │ │ └── com │ │ └── projects │ │ └── tradingMachine │ │ └── services │ │ ├── database │ │ ├── noSql │ │ │ ├── MongoDBConnection.java │ │ │ └── MongoDBManager.java │ │ ├── DataManager.java │ │ ├── OrdersBackEndStore.java │ │ └── sql │ │ │ └── MySqlManager.java │ │ ├── simulation │ │ ├── orders │ │ │ ├── RandomOrdersBuilder.java │ │ │ └── OrdersProducer.java │ │ └── marketData │ │ │ └── MarketDataProducer.java │ │ ├── ServicesRunner.java │ │ └── StatsRunner.java ├── .classpath └── pom.xml ├── TradingMachineUtility ├── .settings │ ├── org.eclipse.m2e.core.prefs │ ├── org.eclipse.core.resources.prefs │ └── org.eclipse.jdt.core.prefs ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── projects │ │ │ └── tradingMachine │ │ │ └── utility │ │ │ ├── ServiceLifeCycle.java │ │ │ ├── database │ │ │ ├── DatabaseConnection.java │ │ │ ├── DbUtility.java │ │ │ ├── creditCheck │ │ │ │ ├── ICreditCheck.java │ │ │ │ ├── CreditCheckException.java │ │ │ │ └── CreditCheck.java │ │ │ ├── MySqlConnection.java │ │ │ ├── PooledDataSourceBuilder.java │ │ │ ├── DatabaseProperties.java │ │ │ └── AbstractDatabaseConnection.java │ │ │ ├── order │ │ │ ├── OrderSide.java │ │ │ ├── OrderType.java │ │ │ ├── OrderTimeInForce.java │ │ │ └── SimpleOrder.java │ │ │ ├── TradingMachineMessageProducer.java │ │ │ ├── TradingMachineMessageConsumer.java │ │ │ ├── marketData │ │ │ ├── MarketData.java │ │ │ └── MarketDataManagerInPlaceUpdater.java │ │ │ └── Utility.java │ └── test │ │ └── java │ │ └── com │ │ └── projects │ │ └── tradingMachine │ │ └── utility │ │ ├── database │ │ ├── PooledDataSourceBuilderTest.java │ │ ├── DbUtilityTest.java │ │ ├── creditCheck │ │ │ └── CreditCheckExceptionTest.java │ │ └── DatabasePropertiesTest.java │ │ ├── order │ │ └── SimpleOrderTest.java │ │ ├── UtilityTest.java │ │ └── marketData │ │ └── MarketDataTest.java ├── .project ├── .classpath └── pom.xml ├── TradingMachineOrderRouter ├── .settings │ ├── org.eclipse.m2e.core.prefs │ └── org.eclipse.jdt.core.prefs ├── src │ └── main │ │ ├── resources │ │ ├── tradingMachineOrderRouter.properties │ │ ├── tradingMachineOrderRouterFixEngine.properties │ │ └── log4j.properties │ │ └── java │ │ └── com │ │ └── projects │ │ └── tradingMachine │ │ └── orderRouter │ │ ├── OrderManager.java │ │ ├── TradingMachineOrderRouter.java │ │ └── TradingMachineFixInitiatorApplication.java ├── .project ├── .classpath └── pom.xml ├── .gitignore └── TradingMachineParentPom.xml /README.md: -------------------------------------------------------------------------------- 1 | Documentation at: http://nicolanardino.github.io/TradingMachine. 2 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /TradingMachineServer/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /TradingMachineServices/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /TradingMachineUtility/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /TradingMachineOrderRouter/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /TradingMachineServices/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /TradingMachineUtility/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /TradingMachineOrderRouter/src/main/resources/tradingMachineOrderRouter.properties: -------------------------------------------------------------------------------- 1 | activeMQ.url=tcp://localhost:61616 2 | activeMQ.executedOrdersTopic=ExecutedOrdersTopic 3 | activeMQ.ordersQueue=OrdersQueue -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/util/PanelCleanUp.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor.util; 2 | 3 | @FunctionalInterface 4 | public interface PanelCleanUp { 5 | void cleanUp() throws Exception; 6 | } 7 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/ServiceLifeCycle.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility; 2 | 3 | public interface ServiceLifeCycle { 4 | 5 | void start() throws Exception; 6 | 7 | void stop() throws Exception; 8 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/DatabaseConnection.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | import java.sql.Connection; 4 | 5 | public interface DatabaseConnection extends AutoCloseable { 6 | 7 | Connection getConnection(); 8 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/DbUtility.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | public final class DbUtility { 4 | 5 | public static String getMySqlConnectionUrl(final DatabaseProperties dbProperties) { 6 | return "jdbc:mysql://"+dbProperties.getHost()+"/"+dbProperties.getDatabaseName(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /TradingMachineServer/src/main/resources/tradingMachine.properties: -------------------------------------------------------------------------------- 1 | activeMQ.url=tcp://localhost:61616 2 | activeMQ.marketDataTopic=MarketDataTopic 3 | 4 | numberProcessingOrderThreads=10 5 | 6 | mySQL.host=localhost 7 | mySQL.port=3306 8 | mySQL.database=TradingMachine 9 | mySQL.userName=TradingUser 10 | mySQL.password=TradingUser 11 | 12 | creditCheckDatabasePoolConnections=10 -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/resources/tradeMonitor.properties: -------------------------------------------------------------------------------- 1 | activeMQ.url=tcp://localhost:61616 2 | activeMQ.executedOrdersTopic=ExecutedOrdersTopic 3 | activeMQ.marketDataTopic=MarketDataTopic 4 | 5 | mongoDB.host=localhost 6 | mongoDB.port=27017 7 | mongoDB.database=TradingMachine 8 | mongoDB.executedOrdersCollection=ExecutedOrdersCollection 9 | mongoDB.marketDataCollection=MarketDataCollection -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/creditCheck/ICreditCheck.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database.creditCheck; 2 | 3 | import java.sql.SQLException; 4 | 5 | public interface ICreditCheck { 6 | 7 | boolean hasEnoughCredit(double credit); 8 | 9 | void setCredit(double credit); 10 | 11 | void closeConnection() throws SQLException; 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | RemoteSystemsTempFiles/ 2 | .recommenders/ 3 | .metadata/ 4 | TradingMachineBinaries 5 | TradingMachineServer/log/ 6 | TradingMachineServer/target/ 7 | TradingMachineOrderRouter/log/ 8 | TradingMachineOrderRouter/target/ 9 | TradingMachineServices/log/ 10 | TradingMachineServices/target/ 11 | TradingMachineUtility/log/ 12 | TradingMachineUtility/target/ 13 | TradingMachineMonitorUI/log/ 14 | TradingMachineMonitorUI/target/ 15 | 16 | -------------------------------------------------------------------------------- /TradingMachineServer/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.compliance=1.8 5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 7 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 8 | org.eclipse.jdt.core.compiler.source=1.8 9 | -------------------------------------------------------------------------------- /TradingMachineServer/src/main/resources/tradingMachineFixEngine.properties: -------------------------------------------------------------------------------- 1 | [default] 2 | FileStorePath=/home/main/Projects/TradingMachineMessageStore/Executor 3 | ConnectionType=acceptor 4 | StartTime=00:00:00 5 | EndTime=00:00:00 6 | HeartBtInt=30 7 | SenderCompID=MyExecutor 8 | UseDataDictionary=Y 9 | 10 | [session] 11 | BeginString=FIXT.1.1 12 | TargetCompID=MyInitiator 13 | DefaultApplVerID=FIX.5.0 14 | SocketAcceptPort=9882 15 | UserName=MyInitiatorUserName 16 | Password=MyInitiatorPassword 17 | 18 | -------------------------------------------------------------------------------- /TradingMachineOrderRouter/src/main/resources/tradingMachineOrderRouterFixEngine.properties: -------------------------------------------------------------------------------- 1 | [default] 2 | FileStorePath=/home/main/Projects/TradingMachineMessageStore/Initiator 3 | ConnectionType=initiator 4 | TargetCompID=MyExecutor 5 | SocketConnectHost=127.0.0.1 6 | StartTime=00:00:00 7 | EndTime=00:00:00 8 | HeartBtInt=30 9 | ReconnectInterval=5 10 | 11 | [session] 12 | BeginString=FIXT.1.1 13 | DefaultApplVerID=FIX.5.0 14 | SocketConnectPort=9882 15 | SenderCompID=MyInitiator 16 | UserName=MyInitiatorUserName 17 | Password=MyInitiatorPassword 18 | 19 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/MySqlConnection.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | import java.sql.SQLException; 4 | 5 | public class MySqlConnection extends AbstractDatabaseConnection { 6 | 7 | public MySqlConnection(final DatabaseProperties databaseProperties) throws ClassNotFoundException, SQLException { 8 | super(databaseProperties); 9 | } 10 | 11 | @Override 12 | protected String getConnectionString() { 13 | return DbUtility.getMySqlConnectionUrl(databaseProperties); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/creditCheck/CreditCheckException.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database.creditCheck; 2 | 3 | public class CreditCheckException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public CreditCheckException() {} 8 | 9 | public CreditCheckException(final String message) { 10 | super(message); 11 | } 12 | 13 | public CreditCheckException(final String message, final Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | public CreditCheckException(final Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TradingMachineServer/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TradingMachineServer 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /TradingMachineServices/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TradingMachineServices 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /TradingMachineUtility/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TradingMachineUtility 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TradingMachineMonitorUI 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /TradingMachineOrderRouter/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TradingMachineOrderRouter 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/resources/tradingMachineServices.properties: -------------------------------------------------------------------------------- 1 | activeMQ.url=tcp://localhost:61616 2 | activeMQ.executedOrdersTopic=ExecutedOrdersTopic 3 | activeMQ.ordersQueue=OrdersQueue 4 | activeMQ.marketDataTopic=MarketDataTopic 5 | 6 | mongoDB.host=localhost 7 | mongoDB.port=27017 8 | mongoDB.database=TradingMachine 9 | mongoDB.executedOrdersCollection=ExecutedOrdersCollection 10 | mongoDB.marketDataCollection=MarketDataCollection 11 | 12 | mySQL.host=localhost 13 | mySQL.port=3306 14 | mySQL.database=TradingMachine 15 | mySQL.userName=TradingUser 16 | mySQL.password=TradingUser 17 | 18 | #seconds 19 | ordersPublishingPeriod=1 20 | marketDataPublishingPeriod=1 21 | statsPublishingPeriod=30 22 | 23 | allowedSymbols=ABBN,BION,HBMN,AEVS,SAHN,RO,RIEN,UBSN,CSGN -------------------------------------------------------------------------------- /TradingMachineServices/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/test/java/com/projects/tradingMachine/utility/database/PooledDataSourceBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | import com.projects.tradingMachine.utility.database.PooledDataSourceBuilder; 4 | import org.junit.Assert; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | import org.junit.rules.Timeout; 9 | 10 | public class PooledDataSourceBuilderTest { 11 | @Rule 12 | public final Timeout globalTimeout = new Timeout(10000); 13 | 14 | @Rule 15 | public final ExpectedException thrown = ExpectedException.none(); 16 | 17 | @Test 18 | public void testConstructor() { 19 | final PooledDataSourceBuilder actual = new PooledDataSourceBuilder(); 20 | Assert.assertNotNull(actual); 21 | } 22 | } -------------------------------------------------------------------------------- /TradingMachineServices/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.8 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 13 | org.eclipse.jdt.core.compiler.source=1.8 14 | -------------------------------------------------------------------------------- /TradingMachineUtility/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.8 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 13 | org.eclipse.jdt.core.compiler.source=1.8 14 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.8 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 13 | org.eclipse.jdt.core.compiler.source=1.8 14 | -------------------------------------------------------------------------------- /TradingMachineOrderRouter/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.8 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 13 | org.eclipse.jdt.core.compiler.source=1.8 14 | -------------------------------------------------------------------------------- /TradingMachineServer/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=DEBUG, file, stdout 3 | 4 | # Direct log messages to a log file 5 | log4j.appender.file=org.apache.log4j.RollingFileAppender 6 | log4j.appender.file.File=log/TradingMachine.log 7 | log4j.appender.file.MaxFileSize=50MB 8 | log4j.appender.file.MaxBackupIndex=10 9 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 10 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 11 | 12 | # Direct log messages to stdout 13 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 14 | log4j.appender.stdout.Target=System.out 15 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 16 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n -------------------------------------------------------------------------------- /TradingMachineOrderRouter/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=DEBUG, file, stdout 3 | 4 | # Direct log messages to a log file 5 | log4j.appender.file=org.apache.log4j.RollingFileAppender 6 | log4j.appender.file.File=log/tradingMachineClient.log 7 | log4j.appender.file.MaxFileSize=50MB 8 | log4j.appender.file.MaxBackupIndex=10 9 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 10 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 11 | 12 | # Direct log messages to stdout 13 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 14 | log4j.appender.stdout.Target=System.out 15 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 16 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/PooledDataSourceBuilder.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | import org.apache.commons.dbcp2.BasicDataSource; 4 | 5 | public final class PooledDataSourceBuilder { 6 | 7 | /** 8 | * Creates a database connection pool. 9 | * 10 | * @param dbProperties 11 | * @param poolSize 12 | * @return 13 | */ 14 | public static BasicDataSource getDataSource(final DatabaseProperties dbProperties, final int poolSize) { 15 | final BasicDataSource ds = new BasicDataSource(); 16 | ds.setUrl(DbUtility.getMySqlConnectionUrl(dbProperties)); 17 | ds.setUsername(dbProperties.getUserName()); 18 | ds.setPassword(dbProperties.getPassword()); 19 | ds.setInitialSize(poolSize); 20 | ds.setMaxTotal(poolSize); 21 | return ds; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /TradingMachineOrderRouter/src/main/java/com/projects/tradingMachine/orderRouter/OrderManager.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.orderRouter; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.concurrent.ConcurrentMap; 5 | 6 | import com.projects.tradingMachine.utility.order.SimpleOrder; 7 | 8 | /** 9 | * Traces all orders sent to the acceptor and updates them when come back. 10 | * */ 11 | public final class OrderManager { 12 | 13 | private final ConcurrentMap orders; 14 | 15 | public OrderManager() { 16 | orders = new ConcurrentHashMap<>(); 17 | } 18 | 19 | public void add(final SimpleOrder order) { 20 | orders.putIfAbsent(order.getID(), order); 21 | } 22 | 23 | public SimpleOrder getOrder(final String orderId) { 24 | return orders.get(orderId); 25 | } 26 | 27 | public void updateOrder(final SimpleOrder order) { 28 | orders.put(order.getID(), order); 29 | } 30 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/test/java/com/projects/tradingMachine/utility/order/SimpleOrderTest.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.order; 2 | 3 | import com.projects.tradingMachine.utility.order.SimpleOrder; 4 | import org.junit.Assert; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | import org.junit.rules.Timeout; 9 | 10 | public class SimpleOrderTest { 11 | @Rule 12 | public final Timeout globalTimeout = new Timeout(10000); 13 | 14 | @Rule 15 | public final ExpectedException thrown = ExpectedException.none(); 16 | 17 | @Test 18 | public void testConstructor() { 19 | final SimpleOrder actual = new SimpleOrder(); 20 | Assert.assertNotNull(actual); 21 | } 22 | 23 | @Test 24 | public void testConstructor2() { 25 | final String arg0 = ""; 26 | final SimpleOrder actual = new SimpleOrder(arg0); 27 | Assert.assertNotNull(actual); 28 | } 29 | } -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/util/SwingUtility.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.swing.JTable; 7 | import javax.swing.RowSorter; 8 | import javax.swing.SortOrder; 9 | import javax.swing.table.TableModel; 10 | import javax.swing.table.TableRowSorter; 11 | 12 | public final class SwingUtility { 13 | 14 | public static void setTableSorter(final JTable table, final int sortColumn, final SortOrder sortOrder) { 15 | final TableRowSorter tableSorter = new TableRowSorter(table.getModel()); 16 | table.setRowSorter(tableSorter); 17 | final List sortKeys = new ArrayList<>(); 18 | sortKeys.add(new RowSorter.SortKey(sortColumn, sortOrder)); 19 | tableSorter.setSortKeys(sortKeys); 20 | tableSorter.sort(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/util/DatetimeTableCellRenderer.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor.util; 2 | 3 | import java.awt.Component; 4 | import java.text.DateFormat; 5 | import java.text.SimpleDateFormat; 6 | 7 | import javax.swing.JTable; 8 | import javax.swing.table.DefaultTableCellRenderer; 9 | 10 | public final class DatetimeTableCellRenderer extends DefaultTableCellRenderer { 11 | private static final long serialVersionUID = 1L; 12 | 13 | private final DateFormat dateFormat; 14 | 15 | public DatetimeTableCellRenderer(final SimpleDateFormat dateFormat) { 16 | super(); 17 | this.dateFormat = dateFormat; 18 | } 19 | 20 | @Override 21 | public Component getTableCellRendererComponent(final JTable table, Object value, final boolean isSelected, final boolean hasFocus, 22 | final int row, final int column) { 23 | value = dateFormat.format(value); 24 | return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/test/java/com/projects/tradingMachine/utility/UtilityTest.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility; 2 | 3 | import com.projects.tradingMachine.utility.Utility.DestinationType; 4 | import java.lang.reflect.Array; 5 | import org.junit.Assert; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.ExpectedException; 9 | import org.junit.rules.Timeout; 10 | 11 | public class UtilityTest { 12 | @Rule 13 | public final Timeout globalTimeout = new Timeout(10000); 14 | 15 | @Rule 16 | public final ExpectedException thrown = ExpectedException.none(); 17 | 18 | @Test 19 | public void values() { 20 | final DestinationType[] actual = DestinationType.values(); 21 | Assert.assertArrayEquals(new DestinationType[]{DestinationType.Queue, DestinationType.Topic}, actual); 22 | } 23 | 24 | @Test 25 | public void valueOf() { 26 | final String arg0 = "Queue"; 27 | final DestinationType actual = DestinationType.valueOf(arg0); 28 | Assert.assertEquals(DestinationType.Queue, actual); 29 | } 30 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/test/java/com/projects/tradingMachine/utility/database/DbUtilityTest.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 4 | import com.projects.tradingMachine.utility.database.DbUtility; 5 | import org.junit.Assert; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.ExpectedException; 9 | import org.junit.rules.Timeout; 10 | 11 | public class DbUtilityTest { 12 | @Rule 13 | public final Timeout globalTimeout = new Timeout(10000); 14 | 15 | @Rule 16 | public final ExpectedException thrown = ExpectedException.none(); 17 | 18 | @Test 19 | public void testConstructor() { 20 | final DbUtility actual = new DbUtility(); 21 | Assert.assertNotNull(actual); 22 | } 23 | 24 | @Test 25 | public void getMySqlConnectionUrl() { 26 | final DatabaseProperties arg0 = new DatabaseProperties("aaaaa", 1, "aaaaa"); 27 | final String actual = DbUtility.getMySqlConnectionUrl(arg0); 28 | Assert.assertEquals("jdbc:mysql://aaaaa/aaaaa", actual); 29 | } 30 | } -------------------------------------------------------------------------------- /TradingMachineUtility/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/database/noSql/MongoDBConnection.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services.database.noSql; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.mongodb.MongoClient; 7 | import com.mongodb.client.MongoDatabase; 8 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 9 | 10 | public final class MongoDBConnection implements AutoCloseable { 11 | private static Logger logger = LoggerFactory.getLogger(MongoDBConnection.class); 12 | 13 | private final MongoClient mongoClient; 14 | private final MongoDatabase mongoDatabase; 15 | 16 | public MongoDBConnection(final DatabaseProperties databaseProperties) { 17 | mongoClient = new MongoClient(databaseProperties.getHost(), databaseProperties.getPort()); 18 | mongoDatabase = mongoClient.getDatabase(databaseProperties.getDatabaseName()); 19 | } 20 | 21 | @Override 22 | public void close() throws Exception { 23 | if (mongoClient != null) { 24 | mongoClient.close(); 25 | logger.info("Connection closed."); 26 | } 27 | } 28 | 29 | public MongoDatabase getMongoDatabase() { 30 | return mongoDatabase; 31 | } 32 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/order/OrderSide.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.order; 2 | 3 | import java.util.Arrays; 4 | import java.util.Optional; 5 | 6 | import quickfix.field.Side; 7 | 8 | public enum OrderSide { 9 | BUY("Buy"), SELL("Sell"); 10 | 11 | private final String sideName; 12 | 13 | private OrderSide(final String name) { 14 | this.sideName = name; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return sideName; 20 | } 21 | 22 | public Side toFIXSide() { 23 | switch(this) { 24 | case BUY: return new Side(Side.BUY); 25 | case SELL: return new Side(Side.SELL); 26 | default: throw new IllegalArgumentException("Unable to convert "+this+" to quickfixj order side."); 27 | } 28 | } 29 | 30 | public static OrderSide fromString(final String orderSide) { 31 | final Optional result = Arrays.stream(OrderSide.values()).filter(o -> o.sideName.equals(orderSide)).findFirst(); 32 | if (result.isPresent()) 33 | return result.get(); 34 | throw new IllegalArgumentException("Unknown order side: "+orderSide); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/DatabaseProperties.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | public class DatabaseProperties { 4 | private final int port; 5 | private final String host; 6 | private final String databaseName; 7 | private final String userName; 8 | private final String password; 9 | 10 | public DatabaseProperties(final String host, final int port, final String databaseName, final String userName, final String password) { 11 | this.port = port; 12 | this.host = host; 13 | this.databaseName = databaseName; 14 | this.userName = userName; 15 | this.password = password; 16 | } 17 | 18 | public DatabaseProperties(final String host, final int port, final String databaseName) { 19 | this(host, port, databaseName, null, null); 20 | } 21 | 22 | public final int getPort() { 23 | return port; 24 | } 25 | 26 | public final String getUserName() { 27 | return userName; 28 | } 29 | 30 | public final String getPassword() { 31 | return password; 32 | } 33 | 34 | public final String getHost() { 35 | return host; 36 | } 37 | 38 | public final String getDatabaseName() { 39 | return databaseName; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /TradingMachineServices/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/order/OrderType.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.order; 2 | 3 | import java.util.Arrays; 4 | import java.util.Optional; 5 | 6 | import quickfix.field.OrdType; 7 | 8 | public enum OrderType { 9 | MARKET("Market"), LIMIT("Limit"), STOP("Stop"); 10 | private final String typeName; 11 | 12 | private OrderType(final String name) { 13 | this.typeName = name; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return typeName; 19 | } 20 | 21 | public OrdType toFIXOrderType() { 22 | switch(this) { 23 | case MARKET: return new OrdType(OrdType.MARKET); 24 | case LIMIT: return new OrdType(OrdType.LIMIT); 25 | case STOP: return new OrdType(OrdType.STOP); 26 | default: throw new IllegalArgumentException("Unable to convert "+this+" to quickfixj order type."); 27 | } 28 | } 29 | 30 | public static OrderType fromString(final String orderType) { 31 | final Optional result = Arrays.stream(OrderType.values()).filter(o -> o.typeName.equals(orderType)).findFirst(); 32 | if (result.isPresent()) 33 | return result.get(); 34 | throw new IllegalArgumentException("Unknown order type: "+orderType); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/util/OrdersStats.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor.util; 2 | 3 | public final class OrdersStats { 4 | private final long total; 5 | private final long filled; 6 | private final long rejected; 7 | private final long buy; 8 | private final long sell; 9 | private final long market; 10 | private final long limit; 11 | private final long stop; 12 | 13 | public OrdersStats(final long total, final long filled, final long rejected, final long buy, final long sell, final long market, final long limit, final long stop) { 14 | this.total = total; 15 | this.filled = filled; 16 | this.rejected = rejected; 17 | this.buy = buy; 18 | this.sell = sell; 19 | this.market = market; 20 | this.limit = limit; 21 | this.stop = stop; 22 | } 23 | 24 | public long getTotal() { 25 | return total; 26 | } 27 | 28 | public long getFilled() { 29 | return filled; 30 | } 31 | 32 | public long getRejected() { 33 | return rejected; 34 | } 35 | 36 | public long getBuy() { 37 | return buy; 38 | } 39 | 40 | public long getSell() { 41 | return sell; 42 | } 43 | 44 | public long getMarket() { 45 | return market; 46 | } 47 | 48 | public long getLimit() { 49 | return limit; 50 | } 51 | 52 | public long getStop() { 53 | return stop; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /TradingMachineParentPom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | com.projects.tradingMachine 7 | TradingMachineServicesParent 8 | 1.0 9 | pom 10 | 11 | TradingMachineParent 12 | Trading machine parent pom. 13 | 14 | TradingMachineUtility 15 | TradingMachineServices 16 | TradingMachineOrderRouter 17 | TradingMachineServer 18 | TradingMachineMonitorUI 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-compiler-plugin 26 | 27 | 1.8 28 | 1.8 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/order/OrderTimeInForce.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.order; 2 | 3 | import java.util.Arrays; 4 | import java.util.Optional; 5 | 6 | import quickfix.field.TimeInForce; 7 | 8 | public enum OrderTimeInForce { 9 | DAY("Day"), IOC("IOC"), FOK("FOK"); 10 | 11 | private final String timeInForceName; 12 | 13 | private OrderTimeInForce(final String timeInForceName) { 14 | this.timeInForceName = timeInForceName; 15 | } 16 | 17 | public String toString() { 18 | return timeInForceName; 19 | } 20 | 21 | public TimeInForce toFIXTimeInForce() { 22 | switch(this) { 23 | case DAY: return new TimeInForce(TimeInForce.DAY); 24 | case IOC: return new TimeInForce(TimeInForce.IMMEDIATE_OR_CANCEL); 25 | case FOK: return new TimeInForce(TimeInForce.FILL_OR_KILL); 26 | default: throw new IllegalArgumentException("Unable to convert "+this+" to quickfixj time in force."); 27 | } 28 | } 29 | 30 | public static OrderTimeInForce fromString(final String timeInForceName) { 31 | final Optional result = Arrays.stream(OrderTimeInForce.values()).filter(o -> o.timeInForceName.equals(timeInForceName)).findFirst(); 32 | if (result.isPresent()) 33 | return result.get(); 34 | throw new IllegalArgumentException("Unknown order time in force: "+timeInForceName); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/AbstractDatabaseConnection.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.SQLException; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public abstract class AbstractDatabaseConnection implements DatabaseConnection { 11 | private static Logger logger = LoggerFactory.getLogger(AbstractDatabaseConnection.class); 12 | 13 | protected final DatabaseProperties databaseProperties; 14 | private final Connection databaseConnection; 15 | 16 | public AbstractDatabaseConnection(final DatabaseProperties databaseProperties) throws ClassNotFoundException, SQLException { 17 | this.databaseProperties = databaseProperties; 18 | final String connectionString = getConnectionString(); 19 | logger.info("Connection string: "+connectionString); 20 | databaseConnection = DriverManager.getConnection(connectionString, databaseProperties.getUserName(), databaseProperties.getPassword()); 21 | } 22 | 23 | @Override 24 | public Connection getConnection() { 25 | return databaseConnection; 26 | } 27 | 28 | @Override 29 | public void close() throws Exception { 30 | if (databaseConnection != null) { 31 | databaseConnection.close(); 32 | logger.info("Connection closed."); 33 | } 34 | } 35 | 36 | protected abstract String getConnectionString(); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/util/MarketDataSummary.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor.util; 2 | 3 | public final class MarketDataSummary { 4 | private final String Symbol; 5 | private final double AvgBid; 6 | private final double AvgAsk; 7 | private final double AvgBidSize; 8 | private final double AvgAskSize; 9 | private final long ItemsNumber; 10 | 11 | public MarketDataSummary(final String symbol, final double avgBid, final double avgAsk, final double avgBidSize, final double avgAskSize, final long itemsNumber) { 12 | Symbol = symbol; 13 | AvgBid = avgBid; 14 | AvgAsk = avgAsk; 15 | AvgBidSize = avgBidSize; 16 | AvgAskSize = avgAskSize; 17 | ItemsNumber = itemsNumber; 18 | } 19 | 20 | public String getSymbol() { 21 | return Symbol; 22 | } 23 | 24 | public double getAvgBid() { 25 | return AvgBid; 26 | } 27 | 28 | public double getAvgAsk() { 29 | return AvgAsk; 30 | } 31 | 32 | public double getAvgBidSize() { 33 | return AvgBidSize; 34 | } 35 | 36 | public double getAvgAskSize() { 37 | return AvgAskSize; 38 | } 39 | 40 | public double getItemsNumber() { 41 | return ItemsNumber; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "MarketDataSummary [Symbol=" + Symbol + ", AvgBid=" + AvgBid + ", AvgAsk=" + AvgAsk + ", AvgBidSize=" 47 | + AvgBidSize + ", AvgAskSize=" + AvgAskSize + ", ItemsNumber=" + ItemsNumber + "]"; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/util/TooltipCellRenderer.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor.util; 2 | 3 | import java.awt.Component; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | import javax.swing.JLabel; 8 | import javax.swing.JTable; 9 | import javax.swing.table.DefaultTableCellRenderer; 10 | 11 | import com.projects.tradingMachine.utility.marketData.MarketData; 12 | 13 | public final class TooltipCellRenderer extends DefaultTableCellRenderer { 14 | private static final long serialVersionUID = 1L; 15 | 16 | private final List marketDataItems; 17 | 18 | public TooltipCellRenderer(final List marketDataItems) { 19 | this.marketDataItems = marketDataItems; 20 | } 21 | 22 | @Override 23 | public Component getTableCellRendererComponent( 24 | final JTable table, final Object value, 25 | final boolean isSelected, final boolean hasFocus, 26 | final int row, final int column) { 27 | final JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 28 | final Optional marketDataItem = marketDataItems.stream().filter((m) -> m.getID().equals(value)).findFirst(); 29 | if (marketDataItem.isPresent()) 30 | label.setToolTipText(marketDataItem.toString()); 31 | return label; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/util/GenericListTableModel.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor.util; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import javax.jms.JMSException; 8 | import javax.swing.table.AbstractTableModel; 9 | 10 | /** 11 | * Custom table model with the initial data set coming from the database.. 12 | * */ 13 | public abstract class GenericListTableModel extends AbstractTableModel { 14 | private static final long serialVersionUID = 1L; 15 | private final String[] columnNames; 16 | protected final List data; 17 | 18 | public GenericListTableModel(final List data, final String[] columnNames) throws FileNotFoundException, IOException, JMSException { 19 | super(); 20 | this.columnNames = columnNames; 21 | this.data = data; 22 | } 23 | 24 | @Override 25 | public int getColumnCount() { 26 | return columnNames.length; 27 | } 28 | 29 | @Override 30 | public int getRowCount() { 31 | return data.size(); 32 | } 33 | 34 | @Override 35 | public String getColumnName(final int colId) { 36 | return columnNames[colId]; 37 | } 38 | 39 | @Override 40 | public Class getColumnClass(final int c) { 41 | final Object valueAt = getValueAt(0, c); 42 | if (valueAt == null) 43 | return Object.class; 44 | return valueAt.getClass(); 45 | } 46 | } -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/database/DataManager.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services.database; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | import com.projects.tradingMachine.utility.marketData.MarketData; 7 | import com.projects.tradingMachine.utility.order.OrderType; 8 | import com.projects.tradingMachine.utility.order.SimpleOrder; 9 | 10 | public interface DataManager extends AutoCloseable { 11 | 12 | /** 13 | * Stores an order in the back-end. 14 | * 15 | * @param order Order to store. 16 | */ 17 | void storeOrder(SimpleOrder order); 18 | 19 | /** 20 | * Gets the orders from the the back-end. 21 | * 22 | * @param orderType If passed, then it only retrieves orders with the given type. 23 | * 24 | * @return List of orders. 25 | */ 26 | List getOrders(Optional orderType); 27 | 28 | /** 29 | * Stores a list of market data items in the back-end. 30 | * 31 | * @param marketDataItems List of market data items. 32 | * @param deleteFirst If true, then it deletes all market data items before adding the ones passed in. 33 | */ 34 | void storeMarketDataItems(List marketDataItems, boolean deleteFirst); 35 | 36 | /** 37 | * Gets the market data from the back-end. 38 | * 39 | * @param symbol If passed, then it only retrieves market data with the given symbol. 40 | * 41 | * @return List of market data items. 42 | */ 43 | List getMarketData(Optional symbol); 44 | } 45 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | com.projects.tradingMachine 5 | TradingMachineServicesParent 6 | 1.0 7 | 8 | 9 | 4.0.0 10 | 11 | TradingMachineMonitorUI 12 | jar 13 | 14 | TradeMonitor 15 | http://maven.apache.org 16 | 17 | 18 | UTF-8 19 | 20 | 21 | 22 | 23 | org.apache.logging.log4j 24 | log4j-api 25 | 2.17.1 26 | 27 | 28 | org.apache.logging.log4j 29 | log4j-core 30 | 2.17.1 31 | 32 | 33 | com.projects.tradingMachine 34 | TradingMachineUtility 35 | 1.0 36 | 37 | 38 | com.projects.tradingMachine 39 | TradingMachineServices 40 | 1.0 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /TradingMachineServer/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /TradingMachineOrderRouter/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/test/java/com/projects/tradingMachine/utility/database/creditCheck/CreditCheckExceptionTest.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database.creditCheck; 2 | 3 | import com.projects.tradingMachine.utility.database.creditCheck.CreditCheckException; 4 | import org.junit.Assert; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | import org.junit.rules.Timeout; 9 | 10 | public class CreditCheckExceptionTest { 11 | @Rule 12 | public final Timeout globalTimeout = new Timeout(10000); 13 | 14 | @Rule 15 | public final ExpectedException thrown = ExpectedException.none(); 16 | 17 | @Test 18 | public void testConstructor() { 19 | final Throwable arg0 = new Throwable(); 20 | final CreditCheckException actual = new CreditCheckException(arg0); 21 | Assert.assertNotNull(actual); 22 | Assert.assertEquals("java.lang.Throwable", actual.getMessage()); 23 | } 24 | 25 | @Test 26 | public void testConstructor2() { 27 | final String arg0 = "aaaaa"; 28 | final Throwable arg1 = new Throwable(); 29 | final CreditCheckException actual = new CreditCheckException(arg0, arg1); 30 | Assert.assertNotNull(actual); 31 | Assert.assertEquals("aaaaa", actual.getMessage()); 32 | } 33 | 34 | @Test 35 | public void testConstructor3() { 36 | final String arg0 = "aaaaa"; 37 | final CreditCheckException actual = new CreditCheckException(arg0); 38 | Assert.assertNotNull(actual); 39 | Assert.assertEquals("aaaaa", actual.getMessage()); 40 | } 41 | 42 | @Test 43 | public void testConstructor4() { 44 | final CreditCheckException actual = new CreditCheckException(); 45 | Assert.assertNotNull(actual); 46 | Assert.assertNull(actual.getMessage()); 47 | } 48 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/database/creditCheck/CreditCheck.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database.creditCheck; 2 | 3 | import java.sql.CallableStatement; 4 | import java.sql.Connection; 5 | import java.sql.SQLException; 6 | import java.sql.Types; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public final class CreditCheck implements ICreditCheck { 12 | private static Logger logger = LoggerFactory.getLogger(CreditCheck.class); 13 | 14 | private final Connection connection; 15 | 16 | public CreditCheck(final Connection connection) { 17 | this.connection = connection; 18 | } 19 | 20 | @Override 21 | public boolean hasEnoughCredit(final double credit) { 22 | try(final CallableStatement stm = connection.prepareCall("{? = call hasEnoughCredit(?,?)}")) { 23 | stm.registerOutParameter(1, Types.BOOLEAN); 24 | stm.setString(2, "TRADING_COUNTERPARTY"); 25 | stm.setDouble(3, credit); 26 | stm.execute(); 27 | final short result = stm.getShort(1); 28 | System.out.println("Has credit: "+result); 29 | return result > 0; 30 | } 31 | catch(final Exception ex) { 32 | logger.warn(ex.getMessage()); 33 | throw new RuntimeException(ex); 34 | } 35 | } 36 | 37 | @Override 38 | public void setCredit(final double credit) { 39 | try(final CallableStatement stm = connection.prepareCall("{call setCredit(?,?)}")) { 40 | stm.setString(1, "TRADING_COUNTERPARTY"); 41 | stm.setDouble(2, credit); 42 | stm.execute(); 43 | } 44 | catch(final Exception ex) { 45 | logger.warn(ex.getMessage()); 46 | throw new RuntimeException(ex); 47 | } 48 | } 49 | 50 | @Override 51 | public void closeConnection() throws SQLException { 52 | connection.close(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/TradingMachineMessageProducer.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility; 2 | 3 | import javax.jms.Connection; 4 | import javax.jms.ExceptionListener; 5 | import javax.jms.JMSException; 6 | import javax.jms.MessageProducer; 7 | import javax.jms.Session; 8 | 9 | import org.apache.activemq.ActiveMQConnectionFactory; 10 | 11 | import com.projects.tradingMachine.utility.Utility.DestinationType; 12 | 13 | public final class TradingMachineMessageProducer implements ServiceLifeCycle { 14 | 15 | private final Connection connection; 16 | private final Session session; 17 | private final MessageProducer producer; 18 | 19 | public TradingMachineMessageProducer(final String brokerUrl, final String destinationName, final DestinationType destinationType, final String clientIDSuffix, 20 | final ExceptionListener exceptionListener) throws JMSException { 21 | final ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl); 22 | connection = connectionFactory.createConnection(); 23 | connection.setClientID(destinationName + "Producer_" + clientIDSuffix); 24 | session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); 25 | producer = session.createProducer(destinationType == DestinationType.Queue ? session.createQueue(destinationName) : session.createTopic(destinationName)); 26 | if (exceptionListener != null) 27 | connection.setExceptionListener(exceptionListener); 28 | } 29 | 30 | 31 | public Session getSession() { 32 | return session; 33 | } 34 | 35 | 36 | public MessageProducer getProducer() { 37 | return producer; 38 | } 39 | 40 | @Override 41 | public void start() throws JMSException { 42 | connection.start(); 43 | } 44 | 45 | @Override 46 | public void stop() throws JMSException { 47 | connection.close(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/MarketDataSummaryTableModel.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import javax.jms.JMSException; 8 | 9 | import com.projects.tradingMachine.tradeMonitor.util.GenericListTableModel; 10 | import com.projects.tradingMachine.tradeMonitor.util.MarketDataSummary; 11 | 12 | public final class MarketDataSummaryTableModel extends GenericListTableModel { 13 | private static final long serialVersionUID = 1L; 14 | 15 | public MarketDataSummaryTableModel(final List marketDataSummaryItems) throws FileNotFoundException, IOException, JMSException { 16 | super(marketDataSummaryItems, new String[] {"Symbol", "Avg. Bid", "Avg. Ask", "Avg. BidSize", "Avg. AskSize", "Nr. of Items"}); 17 | } 18 | 19 | @Override 20 | public Object getValueAt(final int rowId, final int colId) { 21 | Object value = null; 22 | if (data.size() == 0) 23 | return null; 24 | final MarketDataSummary marketDataSummary = data.get(rowId); 25 | switch (colId) { 26 | case 0: 27 | value = marketDataSummary.getSymbol(); 28 | break; 29 | case 1: 30 | value = marketDataSummary.getAvgBid(); 31 | break; 32 | case 2: 33 | value = marketDataSummary.getAvgAsk(); 34 | break; 35 | case 3: 36 | value = marketDataSummary.getAvgBidSize(); 37 | break; 38 | case 4: 39 | value = marketDataSummary.getAvgAskSize(); 40 | break; 41 | case 5: 42 | value = marketDataSummary.getItemsNumber(); 43 | break; 44 | } 45 | return value; 46 | } 47 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/TradingMachineMessageConsumer.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility; 2 | 3 | import javax.jms.Connection; 4 | import javax.jms.ExceptionListener; 5 | import javax.jms.JMSException; 6 | import javax.jms.MessageConsumer; 7 | import javax.jms.MessageListener; 8 | import javax.jms.Session; 9 | 10 | import org.apache.activemq.ActiveMQConnectionFactory; 11 | 12 | import com.projects.tradingMachine.utility.Utility.DestinationType; 13 | 14 | public final class TradingMachineMessageConsumer implements ServiceLifeCycle { 15 | 16 | private final Connection connection; 17 | private final Session session; 18 | private final MessageConsumer consumer; 19 | 20 | public TradingMachineMessageConsumer(final String brokerUrl, final String destinationName, final DestinationType destinationType, 21 | final MessageListener messageListener, final String clientIDSuffix, final String messageSelector, final ExceptionListener exceptionListener) throws JMSException { 22 | final ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl); 23 | connection = connectionFactory.createConnection(); 24 | connection.setClientID(destinationName + "Consumer_" + clientIDSuffix); 25 | session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); 26 | consumer = session.createConsumer(destinationType == DestinationType.Queue ? session.createQueue(destinationName) : session.createTopic(destinationName), messageSelector); 27 | if (messageListener != null) 28 | consumer.setMessageListener(messageListener); 29 | if (exceptionListener != null) 30 | connection.setExceptionListener(exceptionListener); 31 | } 32 | 33 | @Override 34 | public void start() throws JMSException { 35 | connection.start(); 36 | } 37 | 38 | @Override 39 | public void stop() throws JMSException { 40 | connection.close(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TradingMachineUtility/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | com.projects.tradingMachine 6 | TradingMachineServicesParent 7 | 1.0 8 | 9 | 10 | 4.0.0 11 | 12 | TradingMachineUtility 13 | jar 14 | 15 | Trading Machine Utility 16 | 17 | 18 | MarketceteraRepo 19 | http://repo.marketcetera.org/maven 20 | 21 | true 22 | 23 | 24 | 25 | http://maven.apache.org 26 | 27 | 28 | UTF-8 29 | 30 | 31 | 32 | org.apache.activemq 33 | activemq-core 34 | 5.7.0 35 | 36 | 37 | quickfixj 38 | quickfixj-all 39 | 1.6.1 40 | 41 | 42 | org.apache.commons 43 | commons-dbcp2 44 | 2.1 45 | 46 | 47 | mysql 48 | mysql-connector-java 49 | 8.0.28 50 | 51 | 52 | junit 53 | junit 54 | 4.13.1 55 | test 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/OrdersStatsTableModel.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import javax.jms.JMSException; 8 | 9 | import com.projects.tradingMachine.tradeMonitor.util.GenericListTableModel; 10 | import com.projects.tradingMachine.tradeMonitor.util.OrdersStats; 11 | 12 | public final class OrdersStatsTableModel extends GenericListTableModel { 13 | private static final long serialVersionUID = 1L; 14 | 15 | public OrdersStatsTableModel(final List orderStats) throws FileNotFoundException, IOException, JMSException { 16 | super(orderStats, new String[] {"Total", "Filled", "Rejected", "Buy", "Sell", "Market", "Limit", "Stop"}); 17 | } 18 | 19 | @Override 20 | public Object getValueAt(final int rowId, final int colId) { 21 | Object value = null; 22 | if (data.size() == 0) 23 | return null; 24 | final OrdersStats ordersStats = data.get(rowId); 25 | switch (colId) { 26 | case 0: 27 | value = ordersStats.getTotal(); 28 | break; 29 | case 1: 30 | value = ordersStats.getFilled(); 31 | break; 32 | case 2: 33 | value = ordersStats.getRejected(); 34 | break; 35 | case 3: 36 | value = ordersStats.getBuy(); 37 | break; 38 | case 4: 39 | value = ordersStats.getSell(); 40 | break; 41 | case 5: 42 | value = ordersStats.getMarket(); 43 | break; 44 | case 6: 45 | value = ordersStats.getLimit(); 46 | break; 47 | case 7: 48 | value = ordersStats.getStop(); 49 | break; 50 | } 51 | 52 | return value; 53 | } 54 | } -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/MarketDataTableModel.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import javax.jms.JMSException; 8 | 9 | import com.projects.tradingMachine.tradeMonitor.util.GenericListTableModel; 10 | import com.projects.tradingMachine.utility.marketData.MarketData; 11 | 12 | /** 13 | * Custom table model with the initial data set coming from the market data stored in a MongoDB collection, then incremented with updates coming from the topic 14 | * MarketDataTopic. 15 | * */ 16 | public final class MarketDataTableModel extends GenericListTableModel { 17 | private static final long serialVersionUID = 1L; 18 | 19 | public MarketDataTableModel(final List marketDataItems) throws FileNotFoundException, IOException, JMSException { 20 | super(marketDataItems, new String[] {"ID", "Symbol", "Bid", "Ask", "BidSize", "AskSize", "QuoteTime"}); 21 | } 22 | 23 | @Override 24 | public Object getValueAt(final int rowId, final int colId) { 25 | Object value = null; 26 | if (data.size() == 0) 27 | return null; 28 | final MarketData marketData = data.get(rowId); 29 | switch (colId) { 30 | case 0: 31 | value = marketData.getID(); 32 | break; 33 | case 1: 34 | value = marketData.getSymbol(); 35 | break; 36 | case 2: 37 | value = marketData.getBid(); 38 | break; 39 | case 3: 40 | value = marketData.getAsk(); 41 | break; 42 | case 4: 43 | value = marketData.getBidSize(); 44 | break; 45 | case 5: 46 | value = marketData.getAskSize(); 47 | break; 48 | case 6: 49 | value = marketData.getQuoteTime(); 50 | break; 51 | } 52 | return value; 53 | } 54 | } -------------------------------------------------------------------------------- /TradingMachineServer/src/main/java/com/projects/tradingMachine/server/MarketDataManager.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.server; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Properties; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.ConcurrentMap; 7 | 8 | import javax.jms.JMSException; 9 | import javax.jms.Message; 10 | import javax.jms.MessageListener; 11 | import javax.jms.ObjectMessage; 12 | 13 | import com.projects.tradingMachine.utility.ServiceLifeCycle; 14 | import com.projects.tradingMachine.utility.TradingMachineMessageConsumer; 15 | import com.projects.tradingMachine.utility.Utility; 16 | import com.projects.tradingMachine.utility.Utility.DestinationType; 17 | import com.projects.tradingMachine.utility.marketData.MarketData; 18 | 19 | /** 20 | * Receives market data from a given queue. 21 | * */ 22 | public class MarketDataManager implements MessageListener, ServiceLifeCycle { 23 | private final TradingMachineMessageConsumer marketDataConsumer; 24 | private final ConcurrentMap marketDataRepository; 25 | 26 | public MarketDataManager(final Properties properties) throws JMSException { 27 | marketDataRepository = new ConcurrentHashMap<>(); 28 | marketDataConsumer = new TradingMachineMessageConsumer(properties.getProperty("activeMQ.url"), properties.getProperty("activeMQ.marketDataTopic"), DestinationType.Topic, this, "MarketDataManager", null, null); 29 | } 30 | 31 | public MarketData get(final String symbol) { 32 | return marketDataRepository.getOrDefault(symbol, Utility.buildRandomMarketDataItem(symbol)); 33 | } 34 | 35 | @Override 36 | public void onMessage(final Message message) { 37 | try { 38 | @SuppressWarnings("unchecked") 39 | final ArrayList marketDataList = (ArrayList)((ObjectMessage)message).getObject(); 40 | marketDataList.forEach(marketDataItem -> 41 | marketDataRepository.merge(marketDataItem.getSymbol(), marketDataItem, (oldValue, newValue) -> marketDataItem)); 42 | } catch (final JMSException e) { 43 | throw new RuntimeException(e); 44 | } 45 | } 46 | 47 | @Override 48 | public void start() throws Exception { 49 | marketDataConsumer.start(); 50 | } 51 | 52 | @Override 53 | public void stop() throws Exception { 54 | marketDataConsumer.stop(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /TradingMachineServer/src/main/java/com/projects/tradingMachine/server/TradingMachineServer.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.server; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import quickfix.ConfigError; 10 | import quickfix.DefaultMessageFactory; 11 | import quickfix.FileStoreFactory; 12 | import quickfix.LogFactory; 13 | import quickfix.MessageFactory; 14 | import quickfix.MessageStoreFactory; 15 | import quickfix.RuntimeError; 16 | import quickfix.ScreenLogFactory; 17 | import quickfix.SessionSettings; 18 | import quickfix.SocketAcceptor; 19 | 20 | /** 21 | * Main class for configuring and starting the FIX acceptor. 22 | * */ 23 | public final class TradingMachineServer { 24 | private final static Logger logger = LoggerFactory.getLogger(TradingMachineServer.class); 25 | private final SocketAcceptor acceptor; 26 | private final TradingMachineFixAcceptorApplication application; 27 | 28 | public TradingMachineServer() throws Exception { 29 | final SessionSettings settings = getSessionSettings(); 30 | application = new TradingMachineFixAcceptorApplication(settings); 31 | final MessageStoreFactory messageStoreFactory = new FileStoreFactory(settings); 32 | final LogFactory logFactory = new ScreenLogFactory(true, true, true); 33 | final MessageFactory messageFactory = new DefaultMessageFactory(); 34 | acceptor = new SocketAcceptor(application, messageStoreFactory, settings, logFactory, messageFactory); 35 | } 36 | 37 | private SessionSettings getSessionSettings() throws IOException, ConfigError { 38 | try (final InputStream inputStream = TradingMachineServer.class.getResourceAsStream("/tradingMachineFixEngine.properties");) { 39 | return new SessionSettings(inputStream); 40 | } 41 | } 42 | 43 | private void start() throws RuntimeError, ConfigError { 44 | acceptor.start(); 45 | } 46 | 47 | private void stop() { 48 | acceptor.stop(); 49 | application.cleanUp(); 50 | } 51 | 52 | public static void main(String[] args) throws Exception { 53 | try { 54 | final TradingMachineServer executor = new TradingMachineServer(); 55 | executor.start(); 56 | logger.info("press to quit"); 57 | System.in.read(); 58 | executor.stop(); 59 | } catch (final Exception e) { 60 | logger.error(e.getMessage(), e); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/RejectedOrdersTableModel.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import javax.jms.JMSException; 8 | 9 | import com.projects.tradingMachine.tradeMonitor.util.GenericListTableModel; 10 | import com.projects.tradingMachine.utility.order.SimpleOrder; 11 | 12 | /** 13 | * Custom table model with the initial data set coming from the orders stored in a MongoDB collection, then incremented with the ones received onto the 14 | * ExecutedOrdersTopic. 15 | * */ 16 | public final class RejectedOrdersTableModel extends GenericListTableModel { 17 | private static final long serialVersionUID = 1L; 18 | 19 | public RejectedOrdersTableModel(final List rejectedOrders) throws FileNotFoundException, IOException, JMSException { 20 | super(rejectedOrders, new String[] {"ID", "Symbol", "Quantity", "Side", "Type", "Time in Force", "Limit Price", "Stop Price", "Reject Date", "Credit Check Failed"}); 21 | } 22 | 23 | @Override 24 | public Object getValueAt(final int rowId, final int colId) { 25 | Object value = null; 26 | if (data.size() == 0) 27 | return null; 28 | final SimpleOrder order = data.get(rowId); 29 | switch (colId) { 30 | case 0: 31 | value = order.getID(); 32 | break; 33 | case 1: 34 | value = order.getSymbol(); 35 | break; 36 | case 2: 37 | value = order.getQuantity(); 38 | break; 39 | case 3: 40 | value = order.getSide(); 41 | break; 42 | case 4: 43 | value = order.getType(); 44 | break; 45 | case 5: 46 | value = order.getTimeInForce(); 47 | break; 48 | case 6: 49 | value = order.getLimit(); 50 | break; 51 | case 7: 52 | value = order.getStop(); 53 | break; 54 | case 8: 55 | value = order.getStoreDate(); 56 | break; 57 | case 9: 58 | value = order.isCreditCheckFailed(); 59 | break; 60 | } 61 | 62 | return value; 63 | } 64 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/marketData/MarketData.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.marketData; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | import java.util.UUID; 6 | import java.util.stream.IntStream; 7 | 8 | import com.projects.tradingMachine.utility.Utility; 9 | 10 | public final class MarketData implements Serializable { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | private final String symbol; 15 | private final double bid; 16 | private final double ask; 17 | private final int bidSize; 18 | private final int askSize; 19 | private final Date quoteDateTime; 20 | private volatile String id; 21 | 22 | /** 23 | * This is used to build a new instance. 24 | * */ 25 | public MarketData(final String symbol, final double bid, final double ask, final int bidSize, final int askSize) { 26 | this.symbol = symbol; 27 | this.bid = bid; 28 | this.ask = ask; 29 | this.bidSize = bidSize; 30 | this.askSize = askSize; 31 | quoteDateTime = new Date(); 32 | } 33 | 34 | /** 35 | * This is used to set a new instance when loaded from a back-end store. 36 | * */ 37 | public MarketData(final String id, final String symbol, final double bid, final double ask, final int bidSize, final int askSize, final Date quoteDateTime) { 38 | this.symbol = symbol; 39 | this.bid = bid; 40 | this.ask = ask; 41 | this.bidSize = bidSize; 42 | this.askSize = askSize; 43 | this.quoteDateTime = quoteDateTime; 44 | this.id = id; 45 | } 46 | 47 | public String getID() { 48 | if (id == null) 49 | id = UUID.randomUUID().toString(); 50 | return id; 51 | } 52 | 53 | public String getSymbol() { 54 | return symbol; 55 | } 56 | 57 | 58 | public double getBid() { 59 | return bid; 60 | } 61 | 62 | public double getAsk() { 63 | return ask; 64 | } 65 | 66 | 67 | public int getBidSize() { 68 | return bidSize; 69 | } 70 | 71 | 72 | public int getAskSize() { 73 | return askSize; 74 | } 75 | 76 | public Date getQuoteTime() { 77 | return quoteDateTime; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "MarketData [id = "+id+", symbol=" + symbol + ", bid=" + bid + ", ask=" + ask + ", bidSize=" + bidSize + ", askSize=" 83 | + askSize + ", quoteTime=" + quoteDateTime + "]"; 84 | } 85 | 86 | public static void main(final String[] args) { 87 | IntStream.range(1, 100).forEach(i -> System.out.println(Utility.buildRandomMarketDataItem("ABC").getID())); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/simulation/orders/RandomOrdersBuilder.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services.simulation.orders; 2 | 3 | import java.util.Arrays; 4 | import java.util.Date; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.stream.*; 8 | 9 | import com.projects.tradingMachine.utility.Utility; 10 | import com.projects.tradingMachine.utility.order.OrderSide; 11 | import com.projects.tradingMachine.utility.order.OrderTimeInForce; 12 | import com.projects.tradingMachine.utility.order.OrderType; 13 | import com.projects.tradingMachine.utility.order.SimpleOrder; 14 | 15 | /** 16 | * Randomly built orders. 17 | * */ 18 | public final class RandomOrdersBuilder { 19 | private static final Random randomGenerator = new Random(); 20 | 21 | public static SimpleOrder build(final List allowedSymbols) { 22 | final SimpleOrder order = new SimpleOrder(); 23 | order.setSide(randomEnumValue(OrderSide.class)); 24 | final OrderType randomOrderType = randomEnumValue(OrderType.class); 25 | switch(randomOrderType) { 26 | case LIMIT: order.setLimit(Utility.roundDouble(randomGenerator.nextDouble() * 100, 2)); break; 27 | case STOP: order.setStop(Utility.roundDouble(randomGenerator.nextDouble() * 100, 2)); break; 28 | default: 29 | break; 30 | } 31 | order.setType(randomOrderType); 32 | order.setQuantity(randomGenerator.nextInt(1000) + 1); 33 | order.setSymbol(randomListValue(allowedSymbols)); 34 | order.setTimeInForce(randomEnumValue(OrderTimeInForce.class)); 35 | order.SetStoreDate(new Date()); 36 | order.setRejected(randomGenerator.nextBoolean()); 37 | return order; 38 | } 39 | 40 | private static > T randomEnumValue(final Class enumClass){ 41 | return enumClass.getEnumConstants()[randomGenerator.nextInt(enumClass.getEnumConstants().length)]; 42 | } 43 | 44 | private static T randomListValue(final List list){ 45 | if(list == null || list.isEmpty()){ 46 | return null; 47 | } 48 | return list.get(randomGenerator.nextInt(list.size())); 49 | } 50 | 51 | public static void main(final String[] args) { 52 | //IntStream.range(0, 10).forEach(i -> System.out.println(RandomOrdersBuilder.build(Arrays.asList("RIEN", "UBSN", "CSGN")))); 53 | final Stream ordersStream = Stream.generate(() -> RandomOrdersBuilder.build(Arrays.asList("RIEN", "UBSN", "CSGN"))).limit(100000).parallel(); 54 | ordersStream.forEach(System.out::println); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/FilledOrdersTableModel.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import javax.jms.JMSException; 8 | 9 | import com.projects.tradingMachine.tradeMonitor.util.GenericListTableModel; 10 | import com.projects.tradingMachine.utility.order.SimpleOrder; 11 | 12 | /** 13 | * Custom table model with the initial data set coming from the orders stored in a MongoDB collection, then incremented with the ones received onto the 14 | * ExecutedOrdersTopic. 15 | * */ 16 | public final class FilledOrdersTableModel extends GenericListTableModel { 17 | private static final long serialVersionUID = 1L; 18 | 19 | public FilledOrdersTableModel(final List filledOrders) throws FileNotFoundException, IOException, JMSException { 20 | super(filledOrders, new String[] {"ID", "Symbol", "Quantity", "Side", "Type", "Time in Force", "Fill Price", "Limit Price", "Stop Price", "Fill Date", "Market Data ID"}); 21 | } 22 | 23 | @Override 24 | public Object getValueAt(final int rowId, final int colId) { 25 | Object value = null; 26 | if (data.size() == 0) 27 | return null; 28 | final SimpleOrder order = data.get(rowId); 29 | switch (colId) { 30 | case 0: 31 | value = order.getID(); 32 | break; 33 | case 1: 34 | value = order.getSymbol(); 35 | break; 36 | case 2: 37 | value = order.getQuantity(); 38 | break; 39 | case 3: 40 | value = order.getSide(); 41 | break; 42 | case 4: 43 | value = order.getType(); 44 | break; 45 | case 5: 46 | value = order.getTimeInForce(); 47 | break; 48 | case 6: 49 | value = order.getAvgPx(); 50 | break; 51 | case 7: 52 | value = order.getLimit(); 53 | break; 54 | case 8: 55 | value = order.getStop(); 56 | break; 57 | case 9: 58 | value = order.getStoreDate(); 59 | break; 60 | case 10: 61 | value = order.getMarketDataID(); 62 | break; 63 | } 64 | 65 | return value; 66 | } 67 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/marketData/MarketDataManagerInPlaceUpdater.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.marketData; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.concurrent.ConcurrentMap; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ScheduledExecutorService; 8 | import java.util.concurrent.ScheduledFuture; 9 | import java.util.concurrent.ScheduledThreadPoolExecutor; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import com.projects.tradingMachine.utility.Utility; 16 | 17 | /** 18 | * This can be used to produce market in-place data updates, i.e., without relying on to the queue-based infrastructure. 19 | * */ 20 | public final class MarketDataManagerInPlaceUpdater { 21 | private static final Logger logger = LoggerFactory.getLogger(MarketDataManagerInPlaceUpdater.class); 22 | 23 | private final ScheduledExecutorService scheduler; 24 | private final ConcurrentMap marketDataRepository; 25 | private final List allowedSymbols; 26 | private ScheduledFuture marketDataUpdateFuture; 27 | 28 | public MarketDataManagerInPlaceUpdater(final List allowedSymbols) { 29 | this.allowedSymbols = allowedSymbols; 30 | marketDataRepository = new ConcurrentHashMap<>(); 31 | scheduler = Executors.newScheduledThreadPool(1); 32 | ((ScheduledThreadPoolExecutor)scheduler).setRemoveOnCancelPolicy(true); 33 | } 34 | 35 | public MarketData get(final String symbol) { 36 | return marketDataRepository.getOrDefault(symbol, Utility.buildRandomMarketDataItem(symbol)); 37 | } 38 | 39 | public void startUpdates() { 40 | marketDataUpdateFuture = scheduler.scheduleWithFixedDelay(() -> { 41 | Thread.currentThread().setName("MarketDataUpdater-Thread"); 42 | allowedSymbols.forEach(symbol -> { 43 | final MarketData marketDataValue = Utility.buildRandomMarketDataItem(symbol); 44 | marketDataRepository.merge(symbol, marketDataValue, (oldValue, newValue) -> marketDataValue); 45 | }); 46 | Thread.currentThread().setName("MarketDataUpdater-Thread"); 47 | }, 500, 400, TimeUnit.MILLISECONDS); 48 | } 49 | 50 | public void stopUpdates() throws InterruptedException { 51 | marketDataUpdateFuture.cancel(false); 52 | logger.info("Cancelled scheduled future"); 53 | scheduler.shutdown(); 54 | if (!scheduler.awaitTermination(10, TimeUnit.SECONDS)) 55 | scheduler.shutdownNow(); 56 | logger.info("Terminated ScheduledExecutorService"); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/resources/MySqlScripts.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `ORDER` ( 2 | `ID` varchar(50) NOT NULL, 3 | `SYMBOL` varchar(5) NOT NULL, 4 | `QUANTITY` int(11) NOT NULL, 5 | `SIDE` varchar(4) NOT NULL, 6 | `TYPE` varchar(20) NOT NULL, 7 | `TIME_IN_FORCE` varchar(3) NOT NULL, 8 | `LIMIT_PRICE` decimal(10,2) DEFAULT NULL, 9 | `STOP_PRICE` decimal(10,2) DEFAULT NULL, 10 | `PRICE` decimal(10,2) DEFAULT NULL, 11 | `ORIGINAL_ID` int(11) DEFAULT NULL, 12 | `FILL_DATE` datetime DEFAULT NULL, 13 | `REJECTED` varchar(1) DEFAULT NULL, 14 | `CREDIT_CHECK_FAILED` varchar(45) DEFAULT NULL, 15 | PRIMARY KEY (`ID`) 16 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 17 | 18 | CREATE TABLE `COUNTERPARTY` ( 19 | `ID` varchar(100) NOT NULL, 20 | `CREDIT_LIMIT` decimal(10,2) NOT NULL, 21 | PRIMARY KEY (`ID`) 22 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 23 | 24 | 25 | DELIMITER $$ 26 | CREATE DEFINER=`root`@`localhost` PROCEDURE `addOrder`(pId varchar(50), pSymbol varchar(5), pQuantity int, pSide varchar(4), pType varchar(20), pTimeInForce varchar(3), 27 | pLimitPrice decimal(10, 2), pStopPrice decimal(10, 2), pPrice decimal(10, 2), pRejected varchar(1), pCreditCheckFailed varchar(1)) 28 | BEGIN 29 | DECLARE isPresent int default null; 30 | select max(1) into isPresent 31 | from `ORDER` where id = pId; 32 | if (isPresent is null) then 33 | insert into `ORDER`(id, symbol, quantity, side, type, time_in_force, limit_price, price, fill_date, rejected, credit_check_failed) 34 | values (pId, pSymbol, pQuantity, pSide, pType, pTimeInForce, pLimitPrice, pPrice, NOW(), pRejected, pCreditCheckFailed); 35 | end if; 36 | END$$ 37 | DELIMITER ; 38 | 39 | 40 | DELIMITER $$ 41 | CREATE DEFINER=`root`@`localhost` PROCEDURE `getOrders`(pOrderType varchar(20)) 42 | BEGIN 43 | if (pOrderType is null) then 44 | select * from `ORDER`; 45 | else 46 | select * from `ORDER` where type = pOrderType; 47 | end if; 48 | END$$ 49 | DELIMITER ; 50 | 51 | DELIMITER $$ 52 | CREATE DEFINER=`root`@`localhost` PROCEDURE `setCredit`(pCounterpartyId varchar(100), pCredit decimal(10, 2)) 53 | BEGIN 54 | update COUNTERPARTY c 55 | set credit_limit = c.credit_limit + pCredit 56 | where c.id = pCounterpartyId; 57 | commit; 58 | END$$ 59 | DELIMITER ; 60 | 61 | DELIMITER $$ 62 | CREATE DEFINER=`root`@`localhost` FUNCTION `hasEnoughCredit`(pCounterpartyId varchar(100), pCredit decimal(10, 2)) RETURNS tinyint(1) 63 | BEGIN 64 | DECLARE result decimal(10,2); 65 | select credit_limit - pCredit into result 66 | from COUNTERPARTY where id = pCounterpartyId; 67 | if (result > 0) then 68 | return 1; 69 | else 70 | return 0; 71 | end if; 72 | END$$ 73 | DELIMITER ; 74 | 75 | 76 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/simulation/orders/OrdersProducer.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services.simulation.orders; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Properties; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.Future; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.stream.Collectors; 11 | 12 | import javax.jms.JMSException; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import com.projects.tradingMachine.utility.TradingMachineMessageProducer; 18 | import com.projects.tradingMachine.utility.Utility; 19 | import com.projects.tradingMachine.utility.Utility.DestinationType; 20 | 21 | public final class OrdersProducer implements Runnable { 22 | private static final Logger logger = LoggerFactory.getLogger(OrdersProducer.class); 23 | 24 | private final Properties properties; 25 | private final TradingMachineMessageProducer ordersProducer; 26 | 27 | public OrdersProducer(final Properties properties) throws JMSException { 28 | this.properties = properties; 29 | ordersProducer = new TradingMachineMessageProducer(properties.getProperty("activeMQ.url"), properties.getProperty("activeMQ.ordersQueue"), DestinationType.Queue, "OrdersProducer", null); 30 | ordersProducer.start(); 31 | } 32 | 33 | @Override 34 | public void run() { 35 | final List allowedSymbols = Arrays.stream(properties.getProperty("allowedSymbols").split(",")).collect(Collectors.toList()); 36 | while (!Thread.currentThread().isInterrupted()) { 37 | try { 38 | ordersProducer.getProducer().send(ordersProducer.getSession().createObjectMessage(RandomOrdersBuilder.build(allowedSymbols))); 39 | TimeUnit.SECONDS.sleep(Integer.valueOf(properties.getProperty("ordersPublishingPeriod"))); 40 | } 41 | catch(final InterruptedException ex) { 42 | Thread.currentThread().interrupt(); 43 | } 44 | catch (final Exception e) { 45 | logger.warn("Failed to produce order, due to: "+e.getMessage()); 46 | } 47 | } 48 | //close subscription. 49 | try { 50 | ordersProducer.stop(); 51 | } 52 | catch(final Exception ex) { 53 | throw new RuntimeException(ex); 54 | } 55 | } 56 | 57 | public static void main(final String[] args) throws JMSException, Exception { 58 | final ExecutorService es = Executors.newFixedThreadPool(1); 59 | final Future f = es.submit(new OrdersProducer(Utility.getApplicationProperties("tradingMachineServices.properties"))); 60 | TimeUnit.SECONDS.sleep(10); 61 | f.cancel(true); 62 | Utility.shutdownExecutorService(es, 1, TimeUnit.SECONDS); 63 | } 64 | } -------------------------------------------------------------------------------- /TradingMachineOrderRouter/src/main/java/com/projects/tradingMachine/orderRouter/TradingMachineOrderRouter.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.orderRouter; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.concurrent.CountDownLatch; 6 | 7 | import org.quickfixj.jmx.JmxExporter; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import quickfix.ConfigError; 12 | import quickfix.DefaultMessageFactory; 13 | import quickfix.FileStoreFactory; 14 | import quickfix.Initiator; 15 | import quickfix.LogFactory; 16 | import quickfix.MessageFactory; 17 | import quickfix.MessageStoreFactory; 18 | import quickfix.RuntimeError; 19 | import quickfix.ScreenLogFactory; 20 | import quickfix.Session; 21 | import quickfix.SessionSettings; 22 | import quickfix.SocketInitiator; 23 | 24 | /** 25 | * Main class for configuring and starting the FIX initiator. 26 | * */ 27 | public class TradingMachineOrderRouter { 28 | private static final Logger logger = LoggerFactory.getLogger(TradingMachineOrderRouter.class); 29 | private static final CountDownLatch monitorLatch = new CountDownLatch(1); 30 | private final TradingMachineFixInitiatorApplication myApplication; 31 | 32 | private final Initiator initiator; 33 | 34 | public TradingMachineOrderRouter() throws Exception { 35 | final SessionSettings settings = getSessionSettings(); 36 | final MessageStoreFactory messageStoreFactory = new FileStoreFactory(settings); 37 | final LogFactory logFactory = new ScreenLogFactory(true, true, true, true); 38 | final MessageFactory messageFactory = new DefaultMessageFactory(); 39 | myApplication = new TradingMachineFixInitiatorApplication(settings); 40 | initiator = new SocketInitiator(myApplication, messageStoreFactory, settings, logFactory, messageFactory); 41 | new JmxExporter().register(initiator); 42 | } 43 | 44 | private SessionSettings getSessionSettings() throws IOException, ConfigError { 45 | try (final InputStream inputStream = TradingMachineOrderRouter.class.getResourceAsStream("/tradingMachineOrderRouterFixEngine.properties");) { 46 | return new SessionSettings(inputStream); 47 | } 48 | } 49 | 50 | public TradingMachineFixInitiatorApplication getMyApplication() { 51 | return myApplication; 52 | } 53 | 54 | 55 | public void start() throws RuntimeError, ConfigError { 56 | initiator.start(); 57 | logger.info("Initiator started."); 58 | } 59 | 60 | public void stop() throws Exception { 61 | initiator.stop(); 62 | logger.info("Initiator stopped."); 63 | myApplication.closeOrdersConsumer(); 64 | monitorLatch.countDown(); 65 | } 66 | 67 | public void logon() { 68 | initiator.getSessions().stream().forEach(sessionId -> Session.lookupSession(sessionId).logon()); 69 | } 70 | 71 | public static void main(final String[] args) throws Exception { 72 | final TradingMachineOrderRouter initiator = new TradingMachineOrderRouter(); 73 | initiator.start(); 74 | initiator.logon(); 75 | monitorLatch.await(); 76 | } 77 | } -------------------------------------------------------------------------------- /TradingMachineOrderRouter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.projects.tradingMachine 4 | TradingMachineServicesParent 5 | 1.0 6 | 7 | 4.0.0 8 | TradingMachineOrderRouter 9 | Trading Machine Order Router 10 | 11 | 12 | MarketceteraRepo 13 | http://repo.marketcetera.org/maven 14 | 15 | true 16 | 17 | 18 | 19 | 20 | 21 | 22 | quickfixj 23 | quickfixj-all 24 | 1.6.1 25 | 26 | 27 | org.apache.mina 28 | mina-core 29 | 2.1.5 30 | 31 | 32 | org.slf4j 33 | slf4j-log4j12 34 | 1.7.12 35 | 36 | 37 | org.slf4j 38 | slf4j-api 39 | 1.7.12 40 | 41 | 42 | quickfixj 43 | quickfixj-codegenerator 44 | 1.6.1 45 | 46 | 47 | org.apache.logging.log4j 48 | log4j-api 49 | 2.17.1 50 | 51 | 52 | org.apache.logging.log4j 53 | log4j-core 54 | 2.17.1 55 | 56 | 57 | org.apache.activemq 58 | activemq-core 59 | 5.7.0 60 | 61 | 62 | com.projects.tradingMachine 63 | TradingMachineUtility 64 | 1.0 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-dependency-plugin 73 | 74 | 75 | default-cli 76 | package 77 | 78 | copy-dependencies 79 | 80 | 81 | ${basedir}/../TradingMachineBinaries/OrderRouter/lib 82 | true 83 | compile 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /TradingMachineServices/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | com.projects.tradingMachine 6 | TradingMachineServicesParent 7 | 1.0 8 | 9 | 10 | 4.0.0 11 | 12 | TradingMachineServices 13 | jar 14 | 15 | Trading Machine Services 16 | 17 | 18 | MarketceteraRepo 19 | http://repo.marketcetera.org/maven 20 | 21 | true 22 | 23 | 24 | 25 | http://maven.apache.org 26 | 27 | 28 | UTF-8 29 | 30 | 31 | 32 | 33 | org.apache.activemq 34 | activemq-core 35 | 5.7.0 36 | 37 | 38 | org.mongodb 39 | mongodb-driver 40 | 3.2.2 41 | 42 | 43 | org.apache.logging.log4j 44 | log4j-api 45 | 2.17.1 46 | 47 | 48 | org.apache.logging.log4j 49 | log4j-core 50 | 2.17.1 51 | 52 | 53 | mysql 54 | mysql-connector-java 55 | 8.0.28 56 | 57 | 58 | org.apache.logging.log4j 59 | log4j-slf4j-impl 60 | 2.0.1 61 | 62 | 63 | 64 | com.projects.tradingMachine 65 | TradingMachineUtility 66 | 1.0 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-dependency-plugin 75 | 76 | 77 | default-cli 78 | package 79 | 80 | copy-dependencies 81 | 82 | 83 | ${basedir}/../TradingMachineBinaries/Services/lib 84 | true 85 | false 86 | false 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /TradingMachineServer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.projects.tradingMachine 4 | TradingMachineServicesParent 5 | 1.0 6 | 7 | 4.0.0 8 | TradingMachineServer 9 | Trading Machine Server 10 | Acceptor FIX and matching engines. 11 | 12 | 13 | MarketceteraRepo 14 | http://repo.marketcetera.org/maven 15 | 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | quickfixj 24 | quickfixj-all 25 | 1.6.1 26 | 27 | 28 | org.apache.mina 29 | mina-core 30 | 2.1.5 31 | 32 | 33 | org.slf4j 34 | slf4j-log4j12 35 | 1.7.12 36 | 37 | 38 | org.slf4j 39 | slf4j-api 40 | 1.7.12 41 | 42 | 43 | quickfixj 44 | quickfixj-codegenerator 45 | 1.6.1 46 | 47 | 48 | org.apache.logging.log4j 49 | log4j-api 50 | 2.17.1 51 | 52 | 53 | org.apache.logging.log4j 54 | log4j-core 55 | 2.17.1 56 | 57 | 58 | org.apache.activemq 59 | activemq-core 60 | 5.7.0 61 | 62 | 63 | com.projects.tradingMachine 64 | TradingMachineUtility 65 | 1.0 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-dependency-plugin 74 | 75 | 76 | default-cli 77 | package 78 | 79 | copy-dependencies 80 | 81 | 82 | ${basedir}/../TradingMachineBinaries/Server/lib 83 | true 84 | compile 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/test/java/com/projects/tradingMachine/utility/marketData/MarketDataTest.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.marketData; 2 | 3 | import com.projects.tradingMachine.utility.marketData.MarketData; 4 | import java.util.Date; 5 | import org.junit.Assert; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.ExpectedException; 9 | import org.junit.rules.Timeout; 10 | 11 | public class MarketDataTest { 12 | @Rule 13 | public final Timeout globalTimeout = new Timeout(10000); 14 | 15 | @Rule 16 | public final ExpectedException thrown = ExpectedException.none(); 17 | 18 | 19 | @Test 20 | public void getSymbolOutputNotNull() { 21 | final MarketData thisObj = new MarketData("ABC", 1.0, 1.0, 1, 1); 22 | final String actual = thisObj.getSymbol(); 23 | Assert.assertEquals("ABC", actual); 24 | } 25 | 26 | @Test 27 | public void getBidOutputPositive() { 28 | final MarketData thisObj = new MarketData("ABC", 1.0, 1.0, 1, 1); 29 | final double actual = thisObj.getBid(); 30 | Assert.assertEquals(1.0, actual, 0.0); 31 | } 32 | 33 | @Test 34 | public void getAskOutputPositive() { 35 | final MarketData thisObj = new MarketData("ABC", 1.0, 1.0, 1, 1); 36 | final double actual = thisObj.getAsk(); 37 | Assert.assertEquals(1.0, actual, 0.0); 38 | } 39 | 40 | @Test 41 | public void getBidSizeOutputPositive() { 42 | final MarketData thisObj = new MarketData("ABC", 1.0, 1.0, 1, 1); 43 | final int actual = thisObj.getBidSize(); 44 | Assert.assertEquals(1, actual); 45 | } 46 | 47 | @Test 48 | public void testConstructor() { 49 | final String arg0 = "ABC"; 50 | final String arg1 = "ABC"; 51 | final double arg2 = 1.0; 52 | final double arg3 = 1.0; 53 | final int arg4 = 1; 54 | final int arg5 = 1; 55 | final Date arg6 = new Date(1L); 56 | final MarketData actual = new MarketData(arg0, arg1, arg2, arg3, arg4, arg5, arg6); 57 | Assert.assertNotNull(actual); 58 | Assert.assertEquals(1, actual.getAskSize()); 59 | Assert.assertEquals(1.0, actual.getBid(), 0.0); 60 | Assert.assertEquals(1.0, actual.getAsk(), 0.0); 61 | Assert.assertEquals(1, actual.getBidSize()); 62 | Assert.assertNotNull(actual.getQuoteTime()); 63 | Assert.assertEquals("ABC", actual.getSymbol()); 64 | } 65 | 66 | @Test 67 | public void getAskSize() { 68 | final MarketData thisObj = new MarketData("ABC", 1.0, 1.0, 1, 1); 69 | final int actual = thisObj.getAskSize(); 70 | Assert.assertEquals(1, actual); 71 | } 72 | 73 | @Test 74 | public void testConstructor2() { 75 | final String arg0 = "ABC"; 76 | final double arg1 = 1.0; 77 | final double arg2 = 1.0; 78 | final int arg3 = 1; 79 | final int arg4 = 1; 80 | final MarketData actual = new MarketData(arg0, arg1, arg2, arg3, arg4); 81 | Assert.assertNotNull(actual); 82 | Assert.assertEquals(1, actual.getAskSize()); 83 | Assert.assertEquals(1.0, actual.getBid(), 0.0); 84 | Assert.assertEquals(1.0, actual.getAsk(), 0.0); 85 | Assert.assertEquals(1, actual.getBidSize()); 86 | Assert.assertNotNull(actual.getQuoteTime()); 87 | Assert.assertEquals("ABC", actual.getSymbol()); 88 | } 89 | 90 | @Test 91 | public void getQuoteTimeOutputNotNull() { 92 | final MarketData thisObj = new MarketData("ABC", 1.0, 1.0, 1, 1); 93 | final Date actual = thisObj.getQuoteTime(); 94 | Assert.assertNotNull(actual); 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/ServicesRunner.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.sql.SQLException; 6 | import java.util.Properties; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.Future; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import javax.jms.JMSException; 13 | 14 | import com.projects.tradingMachine.services.database.OrdersBackEndStore; 15 | import com.projects.tradingMachine.services.simulation.marketData.MarketDataProducer; 16 | import com.projects.tradingMachine.services.simulation.orders.OrdersProducer; 17 | import com.projects.tradingMachine.utility.ServiceLifeCycle; 18 | import com.projects.tradingMachine.utility.Utility; 19 | 20 | /** 21 | * Starts all the support services, namely: 22 | * 23 | *
    24 | *
  • MarketDataProducer: it builds random ask and bid prices for a selected range of symbols and sends them to a queue, every X seconds.
  • 25 | *
  • OrdersProducer: it builds random buy/ sell market, limit and stop orders and sends them to a queue, every X seconds.
  • 26 | *
  • FilledOrdersBackEndStore: it subscribes to the FilledOrdersTopic to receive fully filled orders and stores them to MySQL and MongDB databases.
  • 27 | *
  • StatsRunner: prints some order execution statistics.
  • 28 | *
29 | * */ 30 | public final class ServicesRunner implements ServiceLifeCycle { 31 | 32 | private final ExecutorService es; 33 | private final OrdersBackEndStore filledOrdersBackEndStore; 34 | private final Properties properties; 35 | private Future ordersProducerFuture; 36 | private Future marketDataProducerFuture; 37 | private Future statsRunnerFuture; 38 | 39 | /** 40 | * Sets up the executor service with 3 threads for OrdersProducer, MarketDataProducer and StatsRunner. 41 | * */ 42 | public ServicesRunner(final Properties properties) throws ClassNotFoundException, JMSException, SQLException, FileNotFoundException, IOException { 43 | this.properties = properties; 44 | es = Executors.newFixedThreadPool(3); 45 | filledOrdersBackEndStore = new OrdersBackEndStore(properties); 46 | } 47 | 48 | /** 49 | * Starts all services. 50 | * */ 51 | @Override 52 | public void start() throws Exception { 53 | ordersProducerFuture = es.submit(new OrdersProducer(properties)); 54 | marketDataProducerFuture = es.submit(new MarketDataProducer(properties)); 55 | statsRunnerFuture = es.submit(new StatsRunner(properties)); 56 | filledOrdersBackEndStore.start(); 57 | } 58 | 59 | /** 60 | * Stops all services. 61 | * */ 62 | @Override 63 | public void stop() throws Exception { 64 | try { 65 | filledOrdersBackEndStore.stop(); 66 | if (ordersProducerFuture != null) 67 | ordersProducerFuture.cancel(true); 68 | if (marketDataProducerFuture != null) 69 | marketDataProducerFuture.cancel(true); 70 | if (statsRunnerFuture != null) 71 | statsRunnerFuture.cancel(true); 72 | } 73 | finally { 74 | Utility.shutdownExecutorService(es, 5, TimeUnit.SECONDS); //thread pool gets shut down by ExecutorService.shutdown, not shutdownNow which would have cancelled by running tasks. 75 | } 76 | } 77 | 78 | public static void main(String[] args) throws Exception { 79 | final ServicesRunner servicesRunner = new ServicesRunner(Utility.getApplicationProperties("tradingMachineServices.properties")); 80 | servicesRunner.start(); 81 | //TimeUnit.SECONDS.sleep(10); 82 | //servicesRunner.stop(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/Utility.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.math.BigDecimal; 7 | import java.math.RoundingMode; 8 | import java.util.Properties; 9 | import java.util.Random; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import com.projects.tradingMachine.utility.marketData.MarketData; 17 | 18 | import quickfix.DataDictionaryProvider; 19 | import quickfix.FixVersions; 20 | import quickfix.LogUtil; 21 | import quickfix.Message; 22 | import quickfix.MessageUtils; 23 | import quickfix.Session; 24 | import quickfix.SessionID; 25 | import quickfix.SessionNotFound; 26 | import quickfix.field.ApplVerID; 27 | 28 | public final class Utility { 29 | private static final Logger logger = LoggerFactory.getLogger(Utility.class); 30 | private static final Random Random = new Random(); 31 | 32 | public enum DestinationType {Queue, Topic} 33 | 34 | public static void shutdownExecutorService(final ExecutorService es, long timeout, TimeUnit timeUnit) throws InterruptedException { 35 | es.shutdown(); 36 | if (!es.awaitTermination(timeout, timeUnit)) 37 | es.shutdownNow(); 38 | logger.info("Terminated ScheduledExecutorService"); 39 | } 40 | 41 | public static Properties getApplicationProperties(final String propertiesFileName) throws FileNotFoundException, IOException { 42 | final Properties p = new Properties(); 43 | try(final InputStream inputStream = ClassLoader.getSystemResourceAsStream(propertiesFileName)) { 44 | p.load(inputStream); 45 | return p; 46 | } 47 | } 48 | 49 | public static double roundDouble(final double value, final int scale) { 50 | return new BigDecimal(value).setScale(scale, RoundingMode.HALF_UP).doubleValue(); 51 | } 52 | 53 | public static void sendMessage(final SessionID sessionID, final Message message) { 54 | try { 55 | final Session session = Session.lookupSession(sessionID); 56 | if (session == null) { 57 | throw new SessionNotFound(sessionID.toString()); 58 | } 59 | final DataDictionaryProvider dataDictionaryProvider = session.getDataDictionaryProvider(); 60 | try { 61 | dataDictionaryProvider.getApplicationDataDictionary(getApplVerID(session, message)).validate(message, true); 62 | } catch (Exception e) { 63 | LogUtil.logThrowable(sessionID, "Outgoing message failed validation: "+ e.getMessage(), e); 64 | return; 65 | } 66 | session.send(message); //thread safe. 67 | } catch (final SessionNotFound e) { 68 | logger.error(e.getMessage(), e); 69 | } 70 | } 71 | 72 | private static ApplVerID getApplVerID(final Session session, final Message message) { 73 | final String beginString = session.getSessionID().getBeginString(); 74 | if (FixVersions.BEGINSTRING_FIXT11.equals(beginString)) { 75 | return new ApplVerID(ApplVerID.FIX50); 76 | } else { 77 | return MessageUtils.toApplVerID(beginString); 78 | } 79 | } 80 | 81 | public static MarketData buildRandomMarketDataItem(final String symbol) { 82 | return new MarketData(symbol, roundDouble(Random.nextDouble() * 100, 2), 83 | Utility.roundDouble(Random.nextDouble() * 100, 2), Random.nextInt(1000), Random.nextInt(1000)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/database/OrdersBackEndStore.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services.database; 2 | 3 | import java.sql.SQLException; 4 | import java.util.Properties; 5 | import java.util.Random; 6 | 7 | import javax.jms.JMSException; 8 | import javax.jms.Message; 9 | import javax.jms.MessageListener; 10 | import javax.jms.ObjectMessage; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import com.projects.tradingMachine.services.database.noSql.MongoDBConnection; 16 | import com.projects.tradingMachine.services.database.noSql.MongoDBManager; 17 | import com.projects.tradingMachine.services.database.sql.MySqlManager; 18 | import com.projects.tradingMachine.utility.ServiceLifeCycle; 19 | import com.projects.tradingMachine.utility.TradingMachineMessageConsumer; 20 | import com.projects.tradingMachine.utility.Utility; 21 | import com.projects.tradingMachine.utility.Utility.DestinationType; 22 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 23 | import com.projects.tradingMachine.utility.database.MySqlConnection; 24 | import com.projects.tradingMachine.utility.database.creditCheck.CreditCheck; 25 | import com.projects.tradingMachine.utility.database.creditCheck.ICreditCheck; 26 | import com.projects.tradingMachine.utility.order.SimpleOrder; 27 | 28 | public final class OrdersBackEndStore implements MessageListener, ServiceLifeCycle 29 | { 30 | private static Logger logger = LoggerFactory.getLogger(OrdersBackEndStore.class); 31 | private static final Random randomGenerator = new Random(); 32 | 33 | private final TradingMachineMessageConsumer ordersConsumer; 34 | private final DataManager mongoDBManager; 35 | private final DataManager mySqlManager; 36 | private final ICreditCheck creditCheck; 37 | 38 | public OrdersBackEndStore(final Properties p) throws JMSException, ClassNotFoundException, SQLException { 39 | ordersConsumer = new TradingMachineMessageConsumer(p.getProperty("activeMQ.url"), p.getProperty("activeMQ.executedOrdersTopic"), DestinationType.Topic, this, "BackEnd", null, null); 40 | mongoDBManager = new MongoDBManager(new MongoDBConnection(new DatabaseProperties(p.getProperty("mongoDB.host"), 41 | Integer.valueOf(p.getProperty("mongoDB.port")), p.getProperty("mongoDB.database"))), p.getProperty("mongoDB.executedOrdersCollection")); 42 | final MySqlConnection mySqlConnection = new MySqlConnection(new DatabaseProperties(p.getProperty("mySQL.host"), 43 | Integer.valueOf(p.getProperty("mySQL.port")), p.getProperty("mySQL.database"), 44 | p.getProperty("mySQL.userName"), p.getProperty("mySQL.password"))); 45 | mySqlManager = new MySqlManager(mySqlConnection); 46 | creditCheck = new CreditCheck(mySqlConnection.getConnection()); 47 | } 48 | 49 | @Override 50 | public void start() throws JMSException { 51 | ordersConsumer.start(); 52 | } 53 | 54 | @Override 55 | public void stop() throws Exception { 56 | ordersConsumer.stop(); 57 | mongoDBManager.close(); 58 | mySqlManager.close(); 59 | } 60 | 61 | @Override 62 | public void onMessage(final Message message) { 63 | try { 64 | final SimpleOrder order = (SimpleOrder)((ObjectMessage)message).getObject(); 65 | if (order.isCreditCheckFailed()) //resets the credit. 66 | creditCheck.setCredit(Utility.roundDouble(randomGenerator.nextDouble() * 99999, 2)); 67 | mongoDBManager.storeOrder(order); 68 | mySqlManager.storeOrder(order); 69 | 70 | } catch (final JMSException e) { 71 | logger.warn("Failed to persist order, due to "+e.getMessage()); 72 | } 73 | } 74 | 75 | public static void main(final String[] args) throws Exception { 76 | OrdersBackEndStore f = new OrdersBackEndStore(Utility.getApplicationProperties("tradingMachineServices.properties")); 77 | f.start(); 78 | Thread.sleep(10000); 79 | f.stop(); 80 | } 81 | } -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/simulation/marketData/MarketDataProducer.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services.simulation.marketData; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Properties; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.Future; 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.stream.Collectors; 12 | 13 | import javax.jms.JMSException; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import com.projects.tradingMachine.services.database.noSql.MongoDBConnection; 19 | import com.projects.tradingMachine.services.database.noSql.MongoDBManager; 20 | import com.projects.tradingMachine.utility.TradingMachineMessageProducer; 21 | import com.projects.tradingMachine.utility.Utility; 22 | import com.projects.tradingMachine.utility.Utility.DestinationType; 23 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 24 | import com.projects.tradingMachine.utility.marketData.MarketData; 25 | 26 | /** 27 | * Randomly builds market data items, publishes them to activeMQ.marketDataTopic and stores them mongoDB.marketDataCollection. 28 | * */ 29 | public final class MarketDataProducer implements Runnable { 30 | private static Logger logger = LoggerFactory.getLogger(MarketDataProducer.class); 31 | 32 | private final TradingMachineMessageProducer marketDataProducer; 33 | private final MongoDBManager mongoDBManager; 34 | private final ExecutorService executorService; 35 | private final Properties properties; 36 | 37 | public MarketDataProducer(final Properties properties) throws JMSException { 38 | this.properties = properties; 39 | marketDataProducer = new TradingMachineMessageProducer(properties.getProperty("activeMQ.url"), properties.getProperty("activeMQ.marketDataTopic"), DestinationType.Topic, "MarketDataProducer", null); 40 | marketDataProducer.start(); 41 | mongoDBManager = new MongoDBManager(new MongoDBConnection(new DatabaseProperties(properties.getProperty("mongoDB.host"), 42 | Integer.valueOf(properties.getProperty("mongoDB.port")), properties.getProperty("mongoDB.database"))), 43 | properties.getProperty("mongoDB.executedOrdersCollection"), properties.getProperty("mongoDB.marketDataCollection")); 44 | executorService = Executors.newSingleThreadExecutor(); 45 | } 46 | 47 | @Override 48 | public void run() { 49 | final List allowedSymbols = Arrays.stream(properties.getProperty("allowedSymbols").split(",")).collect(Collectors.toList()); 50 | while (!Thread.currentThread().isInterrupted()) { 51 | final ArrayList marketDataItems = new ArrayList(allowedSymbols.size()); 52 | allowedSymbols.forEach(symbol -> marketDataItems.add(Utility.buildRandomMarketDataItem(symbol))); 53 | try { 54 | marketDataProducer.getProducer(). 55 | send(marketDataProducer.getSession().createObjectMessage(marketDataItems)); 56 | executorService.execute(() -> mongoDBManager.storeMarketDataItems(marketDataItems, false)); 57 | TimeUnit.SECONDS.sleep(Integer.valueOf(properties.getProperty("marketDataPublishingPeriod"))); 58 | } 59 | catch(final InterruptedException ex) { 60 | Thread.currentThread().interrupt(); 61 | } 62 | catch (final Exception e) { 63 | logger.warn("Unable to produce marked data, due to: "+e.getMessage()); 64 | } 65 | } 66 | cleanUp(); 67 | } 68 | 69 | private void cleanUp() { 70 | try { 71 | marketDataProducer.stop(); 72 | mongoDBManager.close(); 73 | Utility.shutdownExecutorService(executorService, 1, TimeUnit.SECONDS); 74 | } 75 | catch(final Exception ex) { 76 | logger.warn(ex.getMessage()); 77 | throw new RuntimeException(ex); 78 | } 79 | } 80 | 81 | public static void main(final String[] args) throws JMSException, Exception { 82 | final ExecutorService es = Executors.newFixedThreadPool(1); 83 | final Future f = es.submit(new MarketDataProducer(Utility.getApplicationProperties("tradingMachineServices.properties"))); 84 | TimeUnit.SECONDS.sleep(100000); 85 | f.cancel(true); 86 | Utility.shutdownExecutorService(es, 1, TimeUnit.SECONDS); 87 | } 88 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/test/java/com/projects/tradingMachine/utility/database/DatabasePropertiesTest.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.database; 2 | 3 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 4 | import org.junit.Assert; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | import org.junit.rules.Timeout; 9 | 10 | public class DatabasePropertiesTest { 11 | @Rule 12 | public final Timeout globalTimeout = new Timeout(10000); 13 | 14 | @Rule 15 | public final ExpectedException thrown = ExpectedException.none(); 16 | 17 | @Test 18 | public void getPortOutputZero() { 19 | final DatabaseProperties databaseProperties = new DatabaseProperties("foo", 0, "foo"); 20 | Assert.assertEquals(0, databaseProperties.getPort()); 21 | } 22 | 23 | @Test 24 | public void getUserNameOutputVoid() { 25 | final DatabaseProperties thisObj = new DatabaseProperties("aaaaa", 1, "aaaaa"); 26 | final String actual = thisObj.getUserName(); 27 | } 28 | 29 | @Test 30 | public void getUserNameOutputNull() { 31 | final DatabaseProperties databaseProperties = new DatabaseProperties("foo", 0, "foo"); 32 | Assert.assertNull(databaseProperties.getUserName()); 33 | } 34 | 35 | @Test 36 | public void getPasswordOutputVoid() { 37 | final DatabaseProperties thisObj = new DatabaseProperties("aaaaa", 1, "aaaaa"); 38 | final String actual = thisObj.getPassword(); 39 | } 40 | 41 | @Test 42 | public void getPasswordOutputNull() { 43 | final DatabaseProperties databaseProperties = new DatabaseProperties("foo", 0, "foo"); 44 | Assert.assertNull(databaseProperties.getPassword()); 45 | } 46 | 47 | @Test 48 | public void testConstructor() { 49 | final String arg0 = "aaaaa"; 50 | final int arg1 = 1; 51 | final String arg2 = "aaaaa"; 52 | final DatabaseProperties actual = new DatabaseProperties(arg0, arg1, arg2); 53 | Assert.assertNotNull(actual); 54 | Assert.assertEquals("aaaaa", actual.getHost()); 55 | Assert.assertEquals("aaaaa", actual.getDatabaseName()); 56 | Assert.assertEquals(1, actual.getPort()); 57 | Assert.assertNull(actual.getPassword()); 58 | Assert.assertNull(actual.getUserName()); 59 | } 60 | 61 | @Test 62 | public void testConstructor2() { 63 | final String arg0 = "aaaaa"; 64 | final int arg1 = 1; 65 | final String arg2 = "aaaaa"; 66 | final String arg3 = "aaaaa"; 67 | final String arg4 = "aaaaa"; 68 | final DatabaseProperties actual = new DatabaseProperties(arg0, arg1, arg2, arg3, arg4); 69 | Assert.assertNotNull(actual); 70 | Assert.assertEquals("aaaaa", actual.getHost()); 71 | Assert.assertEquals("aaaaa", actual.getDatabaseName()); 72 | Assert.assertEquals(1, actual.getPort()); 73 | Assert.assertEquals("aaaaa", actual.getPassword()); 74 | Assert.assertEquals("aaaaa", actual.getUserName()); 75 | } 76 | 77 | @Test 78 | public void getHostOutputNotNull() { 79 | final DatabaseProperties thisObj = new DatabaseProperties("aaaaa", 1, "aaaaa"); 80 | final String actual = thisObj.getHost(); 81 | Assert.assertEquals("aaaaa", actual); 82 | } 83 | 84 | @Test 85 | public void getHostOutputNotNull2() { 86 | final DatabaseProperties databaseProperties = new DatabaseProperties("foo", 0, "foo"); 87 | Assert.assertEquals("foo", databaseProperties.getHost()); 88 | } 89 | 90 | @Test 91 | public void getDatabaseNameOutputNotNull1() { 92 | final DatabaseProperties thisObj = new DatabaseProperties("aaaaa", 1, "aaaaa"); 93 | final String actual = thisObj.getDatabaseName(); 94 | Assert.assertEquals("aaaaa", actual); 95 | } 96 | 97 | @Test 98 | public void getPortOutputPositive() { 99 | final DatabaseProperties thisObj = new DatabaseProperties("aaaaa", 1, "aaaaa"); 100 | final int actual = thisObj.getPort(); 101 | Assert.assertEquals(1, actual); 102 | } 103 | 104 | @Test 105 | public void getDatabaseNameOutputNotNull() { 106 | final DatabaseProperties databaseProperties = new DatabaseProperties("foo", 0, "foo"); 107 | Assert.assertEquals("foo", databaseProperties.getDatabaseName()); 108 | } 109 | } -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/MarketDataPanel.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Color; 5 | import java.awt.Component; 6 | import java.awt.Dimension; 7 | import java.io.FileNotFoundException; 8 | import java.io.IOException; 9 | import java.text.SimpleDateFormat; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Map.Entry; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.ScheduledExecutorService; 16 | import java.util.concurrent.TimeUnit; 17 | import java.util.stream.Collectors; 18 | 19 | import javax.jms.JMSException; 20 | import javax.swing.BorderFactory; 21 | import javax.swing.JPanel; 22 | import javax.swing.JScrollPane; 23 | import javax.swing.JTable; 24 | import javax.swing.SortOrder; 25 | import javax.swing.border.EtchedBorder; 26 | import javax.swing.table.AbstractTableModel; 27 | import javax.swing.table.TableCellRenderer; 28 | 29 | import com.projects.tradingMachine.tradeMonitor.util.DatetimeTableCellRenderer; 30 | import com.projects.tradingMachine.tradeMonitor.util.MarketDataSummary; 31 | import com.projects.tradingMachine.tradeMonitor.util.PanelCleanUp; 32 | import com.projects.tradingMachine.tradeMonitor.util.SwingUtility; 33 | import com.projects.tradingMachine.utility.Utility; 34 | import com.projects.tradingMachine.utility.marketData.MarketData; 35 | 36 | /** 37 | * Market data panel with a table as main content. 38 | * */ 39 | public final class MarketDataPanel extends JPanel implements PanelCleanUp { 40 | private static final long serialVersionUID = 1L; 41 | private final List marketDataItems; 42 | private final List marketDataSummaryItems = new ArrayList<>(); 43 | private final JTable marketDataTable; 44 | private final JTable marketDataSummaryTable; 45 | private final ScheduledExecutorService es = Executors.newScheduledThreadPool(1); 46 | 47 | public MarketDataPanel(final List marketDataItems) throws FileNotFoundException, IOException, JMSException { 48 | super(new BorderLayout(10, 20)); 49 | this.marketDataItems = marketDataItems; 50 | buildMarketDataSummary(); 51 | marketDataTable = buildMarketDataTable(false); 52 | marketDataSummaryTable = buildMarketDataTable(true); 53 | final JScrollPane marketDataSummaryScrollPanel = new JScrollPane(marketDataSummaryTable); 54 | marketDataSummaryScrollPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Market Data Summary")); 55 | add(marketDataSummaryScrollPanel, BorderLayout.NORTH); 56 | final JScrollPane marketDataScrollPanel = new JScrollPane(marketDataTable); 57 | marketDataScrollPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Market Data")); 58 | add(marketDataScrollPanel, BorderLayout.CENTER); 59 | es.scheduleWithFixedDelay(() -> { 60 | buildMarketDataSummary(); 61 | ((AbstractTableModel)marketDataSummaryTable.getModel()).fireTableDataChanged(); 62 | }, 1, 1, TimeUnit.SECONDS); 63 | } 64 | 65 | private void buildMarketDataSummary() { 66 | marketDataSummaryItems.clear(); 67 | final ArrayList marketDataItemsCopy= new ArrayList<>(marketDataItems); 68 | final Map itemsNumber = marketDataItemsCopy.parallelStream(). 69 | collect(Collectors.groupingBy(MarketData::getSymbol, Collectors.counting())); 70 | final Map avgBid = marketDataItemsCopy.parallelStream(). 71 | collect(Collectors.groupingBy(MarketData::getSymbol, Collectors.averagingDouble(MarketData::getBid))); 72 | final Map avgAsk = marketDataItemsCopy.parallelStream(). 73 | collect(Collectors.groupingBy(MarketData::getSymbol, Collectors.averagingDouble(MarketData::getAsk))); 74 | final Map avgBidSize = marketDataItemsCopy.parallelStream(). 75 | collect(Collectors.groupingBy(MarketData::getSymbol, Collectors.averagingDouble(MarketData::getBidSize))); 76 | final Map avgAskSize = marketDataItemsCopy.parallelStream(). 77 | collect(Collectors.groupingBy(MarketData::getSymbol, Collectors.averagingDouble(MarketData::getAskSize))); 78 | for(final Entry entry : itemsNumber.entrySet()) 79 | marketDataSummaryItems.add(new MarketDataSummary(entry.getKey(), avgBid.get(entry.getKey()), avgAsk.get(entry.getKey()), avgBidSize.get(entry.getKey()), avgAskSize.get(entry.getKey()), entry.getValue())); 80 | } 81 | 82 | private JTable buildMarketDataTable(boolean isSummaryTable) throws FileNotFoundException, IOException, JMSException { 83 | final JTable marketDataTable = new JTable(isSummaryTable ? new MarketDataSummaryTableModel(marketDataSummaryItems) : new MarketDataTableModel(marketDataItems)) { 84 | private static final long serialVersionUID = 1L; 85 | public Component prepareRenderer(final TableCellRenderer renderer, final int row, final int column) 86 | { 87 | final Component component = super.prepareRenderer(renderer, row, column); 88 | if (row % 2 == 0) { 89 | component.setBackground(Color.WHITE); 90 | component.setForeground(Color.BLACK); 91 | } 92 | else { 93 | component.setBackground(Color.GREEN); 94 | component.setForeground(Color.RED); 95 | } 96 | return component; 97 | } 98 | }; 99 | if (isSummaryTable) 100 | SwingUtility.setTableSorter(marketDataTable, 0, SortOrder.ASCENDING); 101 | else {//sort on quote time --> column 6. 102 | marketDataTable.getColumnModel().getColumn(6).setCellRenderer(new DatetimeTableCellRenderer(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"))); 103 | SwingUtility.setTableSorter(marketDataTable, 6, SortOrder.DESCENDING); 104 | } 105 | marketDataTable.setPreferredScrollableViewportSize(new Dimension(500, 70)); 106 | marketDataTable.setFillsViewportHeight(true); 107 | return marketDataTable; 108 | } 109 | 110 | public JTable getMarketDataTable() { 111 | return marketDataTable; 112 | } 113 | 114 | public JTable getMarketDataSummaryTable() { 115 | return marketDataSummaryTable; 116 | } 117 | 118 | @Override 119 | public void cleanUp() throws Exception { 120 | Utility.shutdownExecutorService(es, 5, TimeUnit.SECONDS); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/database/sql/MySqlManager.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services.database.sql; 2 | 3 | import java.sql.CallableStatement; 4 | import java.sql.ResultSet; 5 | import java.sql.SQLException; 6 | import java.sql.Types; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Optional; 10 | import java.util.Properties; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import com.projects.tradingMachine.services.database.DataManager; 16 | import com.projects.tradingMachine.utility.Utility; 17 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 18 | import com.projects.tradingMachine.utility.database.MySqlConnection; 19 | import com.projects.tradingMachine.utility.marketData.MarketData; 20 | import com.projects.tradingMachine.utility.order.OrderSide; 21 | import com.projects.tradingMachine.utility.order.OrderTimeInForce; 22 | import com.projects.tradingMachine.utility.order.OrderType; 23 | import com.projects.tradingMachine.utility.order.SimpleOrder; 24 | 25 | public class MySqlManager implements DataManager { 26 | private static Logger logger = LoggerFactory.getLogger(MySqlManager.class); 27 | 28 | private final MySqlConnection mySqlConnection; 29 | 30 | public MySqlManager(final MySqlConnection mySqlConnection) { 31 | this.mySqlConnection = mySqlConnection; 32 | } 33 | 34 | @Override 35 | public void storeOrder(final SimpleOrder order) { 36 | try(final CallableStatement stm = mySqlConnection.getConnection().prepareCall("{call addOrder(?,?,?,?,?,?,?,?,?,?,?)}")) { 37 | stm.setString(1, order.getID()); 38 | stm.setString(2, order.getSymbol()); 39 | stm.setInt(3, order.getQuantity()); 40 | stm.setString(4, order.getSide().toString()); 41 | stm.setString(5, order.getType().toString()); 42 | stm.setString(6, order.getTimeInForce().toString()); 43 | switch(order.getType()) { 44 | case LIMIT: 45 | stm.setDouble(7, order.getLimit()); 46 | stm.setNull(8, java.sql.Types.DOUBLE); 47 | break; 48 | case STOP: 49 | stm.setNull(7, java.sql.Types.DOUBLE); 50 | stm.setDouble(8, order.getStop()); 51 | break; 52 | default: 53 | stm.setNull(7, java.sql.Types.DOUBLE); 54 | stm.setNull(8, java.sql.Types.DOUBLE); 55 | } 56 | stm.setDouble(9, order.getAvgPx()); 57 | stm.setString(10, order.isRejected() ? "Y":"N"); 58 | stm.setString(11, order.isCreditCheckFailed() ? "Y":"N"); 59 | stm.execute(); 60 | } 61 | catch(final Exception ex) { 62 | logger.warn("Failed to store order "+order+", due to: "+ex.getMessage()); 63 | throw new RuntimeException(ex); 64 | } 65 | } 66 | 67 | @Override 68 | public List getOrders(final Optional orderType) { 69 | logger.info("Starting to get orders data..."); 70 | final List result = new ArrayList(); 71 | try(final CallableStatement stm = mySqlConnection.getConnection().prepareCall("{call getOrders (?)}")) { 72 | if (orderType.isPresent()) 73 | stm.setString(1, orderType.get().toString()); 74 | else 75 | stm.setNull(1, java.sql.Types.VARCHAR); 76 | final ResultSet rs = stm.executeQuery(); 77 | while (rs.next()) 78 | result.add(new SimpleOrder(rs.getString("id"), rs.getString("symbol"), rs.getInt("quantity"), OrderSide.fromString(rs.getString("side")), 79 | OrderType.fromString(rs.getString("type")), OrderTimeInForce.fromString(rs.getString("time_in_force")), 80 | rs.getDouble("limit_price"), rs.getDouble("stop_price"), rs.getDouble("price"), rs.getString("original_id"), rs.getDate("fill_date"), rs.getString("rejected").equals("Y"), rs.getString("market_data_id"), rs.getString("credit_check_failed").equals("Y"))); 81 | } 82 | catch(final SQLException e) { 83 | throw new RuntimeException(e); 84 | } 85 | logger.info("Number of orders retrieved: "+result.size()); 86 | return result; 87 | } 88 | 89 | public boolean hasEnoughCredit(final String counterpartyId, final double credit) { 90 | try(final CallableStatement stm = mySqlConnection.getConnection().prepareCall("{? = call hasEnoughCredit (?,?)}")) { 91 | stm.registerOutParameter(1, Types.DOUBLE); 92 | stm.setString(2, counterpartyId); 93 | stm.setDouble(3, credit); 94 | stm.execute(); 95 | final boolean result = stm.getBoolean(1); 96 | return result; 97 | } 98 | catch(final SQLException e) { 99 | throw new RuntimeException(e); 100 | } 101 | } 102 | 103 | public void setCredit(final String counterpartyId, final double credit) { 104 | try(final CallableStatement stm = mySqlConnection.getConnection().prepareCall("{call setCredit (?,?)}")) { 105 | stm.setString(1, counterpartyId); 106 | stm.setDouble(2, credit); 107 | stm.execute(); 108 | } 109 | catch(final SQLException e) { 110 | throw new RuntimeException(e); 111 | } 112 | } 113 | 114 | @Override 115 | public void storeMarketDataItems(final List marketDataItems, final boolean deleteFirst) { 116 | throw new UnsupportedOperationException("Not implemented yet."); 117 | } 118 | 119 | @Override 120 | public List getMarketData(Optional symbol) { 121 | throw new UnsupportedOperationException("Not implemented yet."); 122 | } 123 | 124 | @Override 125 | public void close() throws Exception { 126 | mySqlConnection.close(); 127 | } 128 | 129 | public static void main(final String[] args) throws NumberFormatException, ClassNotFoundException, SQLException, Exception { 130 | final Properties p = Utility.getApplicationProperties("tradingMachineServices.properties"); 131 | try(final DataManager mySqlManager = new MySqlManager(new MySqlConnection(new DatabaseProperties(p.getProperty("mySQL.host"), Integer.valueOf(p.getProperty("mySQL.port")), p.getProperty("mySQL.database"), 132 | p.getProperty("mySQL.userName"), p.getProperty("mySQL.password"))))) { 133 | //System.out.println(mySqlManager.getOrders(Optional.of(OrderType.STOP)).stream().mapToDouble(SimpleOrder::getAvgPx).summaryStatistics()); 134 | System.out.println(mySqlManager.getOrders(Optional.ofNullable(null)).stream().mapToDouble(SimpleOrder::getAvgPx).summaryStatistics()); 135 | //mongoDBManager.getOrders(Optional.of(OrderType.LIMIT)).stream().map(SimpleOrder::getAvgPx).forEach(System.out::println); 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /TradingMachineServer/src/main/java/com/projects/tradingMachine/server/TradingMachineFixAcceptorApplication.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.server; 2 | 3 | import java.sql.SQLException; 4 | import java.util.Properties; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ScheduledExecutorService; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import org.apache.commons.dbcp2.BasicDataSource; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import com.projects.tradingMachine.utility.Utility; 15 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 16 | import com.projects.tradingMachine.utility.database.PooledDataSourceBuilder; 17 | 18 | import quickfix.ConfigError; 19 | import quickfix.Dictionary; 20 | import quickfix.DoNotSend; 21 | import quickfix.FieldConvertError; 22 | import quickfix.FieldNotFound; 23 | import quickfix.IncorrectDataFormat; 24 | import quickfix.IncorrectTagValue; 25 | import quickfix.RejectLogon; 26 | import quickfix.Session; 27 | import quickfix.SessionID; 28 | import quickfix.SessionSettings; 29 | import quickfix.UnsupportedMessageType; 30 | import quickfix.fixt11.Logon; 31 | 32 | /** 33 | * QuickFIX/J acceptor with following key features: 34 | *
    35 | *
  • Does log-on check.
  • 36 | *
  • FIX 5.0 message cracking. Once a message is received, it then gets forwarded, in a separate thread, to a matching engine.
  • 37 | *
  • The degree of parallelism can be set by a configuration parameter.
  • 38 | *
  • Receives market data from a given queue.
  • 39 | *
40 | * */ 41 | public class TradingMachineFixAcceptorApplication extends quickfix.MessageCracker implements quickfix.Application { 42 | private final static Logger logger = LoggerFactory.getLogger(TradingMachineFixAcceptorApplication.class); 43 | 44 | private final MarketDataManager marketDataManager; 45 | private final ExecutorService executor; 46 | private final ScheduledExecutorService scheduledExecutorService; 47 | private final SessionSettings settings; 48 | private final BasicDataSource creditCheckConnectionPool; 49 | 50 | public TradingMachineFixAcceptorApplication(final SessionSettings settings) throws Exception { 51 | this.settings = settings; 52 | final Properties applicationProperties = Utility.getApplicationProperties("tradingMachine.properties"); 53 | marketDataManager = new MarketDataManager(applicationProperties); 54 | marketDataManager.start(); 55 | executor = Executors.newFixedThreadPool(Integer.valueOf(applicationProperties.getProperty("numberProcessingOrderThreads"))); 56 | creditCheckConnectionPool = PooledDataSourceBuilder.getDataSource(new DatabaseProperties(applicationProperties.getProperty("mySQL.host"), 57 | Integer.valueOf(applicationProperties.getProperty("mySQL.port")), applicationProperties.getProperty("mySQL.database"), 58 | applicationProperties.getProperty("mySQL.userName"), applicationProperties.getProperty("mySQL.password")), 59 | Integer.valueOf(applicationProperties.getProperty("creditCheckDatabasePoolConnections"))); 60 | scheduledExecutorService = Executors.newScheduledThreadPool(1); 61 | scheduledExecutorService.scheduleWithFixedDelay(() -> { 62 | logger.debug("Credit check database pool, idle: "+creditCheckConnectionPool.getNumIdle()+", active: "+creditCheckConnectionPool.getNumActive()); 63 | }, 1, 10, TimeUnit.SECONDS); 64 | } 65 | 66 | @Override 67 | public void onCreate(final SessionID sessionID) { 68 | Session.lookupSession(sessionID).getLog().onEvent("Session "+sessionID+" created."); 69 | } 70 | 71 | @Override 72 | public void onLogon(final SessionID sessionID) { 73 | 74 | } 75 | 76 | @Override 77 | public void onLogout(final SessionID sessionID) { 78 | } 79 | 80 | @Override 81 | public void toAdmin(quickfix.Message message, SessionID sessionID) { 82 | } 83 | 84 | @Override 85 | public void toApp(quickfix.Message message, SessionID sessionID) throws DoNotSend { 86 | } 87 | 88 | @Override 89 | public void fromAdmin(final quickfix.Message message, final SessionID sessionID) throws FieldNotFound, IncorrectDataFormat, 90 | IncorrectTagValue, RejectLogon { 91 | logonCheck(message, sessionID); 92 | } 93 | 94 | private void logonCheck(final quickfix.Message message, final SessionID sessionID) throws RejectLogon { 95 | if (message instanceof Logon) { 96 | final Logon logon = (Logon)message; 97 | try { 98 | final Dictionary sessionSettings = settings.get(sessionID); 99 | final String userName = logon.getUsername().getValue(); 100 | final String password = logon.getPassword().getValue(); 101 | final String configuredUserName = sessionSettings.getString("UserName"); 102 | final String configuredPassword = sessionSettings.getString("Password"); 103 | if (!configuredUserName.equals(userName)) 104 | throw new RejectLogon("Username "+userName+" doesn't match the excepted one: "+configuredUserName); 105 | if (!configuredPassword.equals(password)) 106 | throw new RejectLogon("Password mismatch."); 107 | } 108 | catch(final Exception ex) { 109 | if (ex instanceof RejectLogon) 110 | throw new RejectLogon(ex.getMessage()); 111 | throw new RejectLogon("Unable to check logon credentials, due to: "+ex.getMessage()); 112 | } 113 | } 114 | } 115 | 116 | public void fromApp(quickfix.Message message, SessionID sessionID) throws FieldNotFound, IncorrectDataFormat, 117 | IncorrectTagValue, UnsupportedMessageType { 118 | crack(message, sessionID); 119 | } 120 | 121 | public void onMessage(final quickfix.fix50.NewOrderSingle order, final SessionID sessionID) 122 | throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue, NumberFormatException, ClassNotFoundException, SQLException, ConfigError, FieldConvertError { 123 | executor.execute(new MatchingEngine(creditCheckConnectionPool, marketDataManager, order, sessionID)); 124 | } 125 | 126 | public void cleanUp() { 127 | try { 128 | Utility.shutdownExecutorService(executor, 5, TimeUnit.SECONDS); 129 | } 130 | catch(final InterruptedException ex) { 131 | logger.warn("Exception while shutting down the matching engine executor service."); 132 | } 133 | try { 134 | Utility.shutdownExecutorService(scheduledExecutorService, 5, TimeUnit.SECONDS); 135 | } 136 | catch(final InterruptedException ex) { 137 | logger.warn("Exception while shutting down utility scheduled executor service."); 138 | } 139 | try { 140 | creditCheckConnectionPool.close(); 141 | } catch (final SQLException ex) { 142 | logger.warn("Exception while closing database connection pool."); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/OrdersPanel.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Color; 5 | import java.awt.Component; 6 | import java.awt.Dimension; 7 | import java.io.FileNotFoundException; 8 | import java.io.IOException; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.ScheduledExecutorService; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.stream.Collectors; 17 | 18 | import javax.jms.JMSException; 19 | import javax.swing.BorderFactory; 20 | import javax.swing.JPanel; 21 | import javax.swing.JScrollPane; 22 | import javax.swing.JTable; 23 | import javax.swing.SortOrder; 24 | import javax.swing.border.EtchedBorder; 25 | import javax.swing.table.AbstractTableModel; 26 | import javax.swing.table.TableCellRenderer; 27 | 28 | import com.projects.tradingMachine.tradeMonitor.util.DatetimeTableCellRenderer; 29 | import com.projects.tradingMachine.tradeMonitor.util.OrdersStats; 30 | import com.projects.tradingMachine.tradeMonitor.util.PanelCleanUp; 31 | import com.projects.tradingMachine.tradeMonitor.util.SwingUtility; 32 | import com.projects.tradingMachine.utility.Utility; 33 | import com.projects.tradingMachine.utility.order.OrderSide; 34 | import com.projects.tradingMachine.utility.order.OrderType; 35 | import com.projects.tradingMachine.utility.order.SimpleOrder; 36 | 37 | /** 38 | * It creates a panel with a executed and rejected orders tables and: 39 | *
    40 | *
  • Sets a column renderer on the FilledDate column.
  • 41 | *
  • Overrides JTable.prepareRenderer in order to show BUY and SELL order rows with different colors.
  • 42 | *
  • Sets a TableRowSorter with default sorting enabled on the FilledDate column.
  • 43 | *
  • As a side content, North to the tables, it shows statistics about the orders, which get updated every 1 second.
  • 44 | *
45 | * */ 46 | public final class OrdersPanel extends JPanel implements PanelCleanUp { 47 | private static final long serialVersionUID = 1L; 48 | private final List ordersStats; 49 | private JTable filledOrdersTable, rejectedOrdersTable, ordersStatsTable; 50 | private final ScheduledExecutorService es = Executors.newScheduledThreadPool(1); 51 | 52 | private static OrdersStats buildOrdersStats(final List allOrders) { 53 | final Map groupedByRjectionStatus = allOrders.parallelStream(). 54 | collect(Collectors.partitioningBy(SimpleOrder::isRejected, Collectors.counting())); 55 | final Map groupedBySide = allOrders.parallelStream(). 56 | collect(Collectors.groupingBy(SimpleOrder::getSide, Collectors.counting())); 57 | final Map groupedByType = allOrders.parallelStream(). 58 | collect(Collectors.groupingBy(SimpleOrder::getType, Collectors.counting())); 59 | return new OrdersStats(allOrders.size(), groupedByRjectionStatus.get(false), groupedByRjectionStatus.get(true), 60 | groupedBySide.get(OrderSide.BUY), groupedBySide.get(OrderSide.SELL), groupedByType.get(OrderType.MARKET), 61 | groupedByType.get(OrderType.LIMIT), groupedByType.get(OrderType.STOP)); 62 | } 63 | 64 | public OrdersPanel(final List filledOrders, final List rejectedOrders) throws FileNotFoundException, IOException, JMSException { 65 | super(new BorderLayout(10, 20)); 66 | filledOrdersTable = buildOrdersTable(filledOrders, true); 67 | rejectedOrdersTable = buildOrdersTable(rejectedOrders, false); 68 | ordersStats = Arrays.asList(buildOrdersStats(java.util.stream.Stream.concat(filledOrders.stream(), rejectedOrders.stream()).collect(Collectors.toList()))); 69 | ordersStatsTable = new JTable(new OrdersStatsTableModel(ordersStats)); 70 | ordersStatsTable.setPreferredScrollableViewportSize(new Dimension(500, 70)); 71 | ordersStatsTable.setFillsViewportHeight(true); 72 | add(ordersStatsTable, BorderLayout.NORTH); 73 | final JScrollPane filledOrdersScrollPane = new JScrollPane(filledOrdersTable); 74 | filledOrdersScrollPane.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Filled Orders")); 75 | add(filledOrdersScrollPane, BorderLayout.CENTER); 76 | final JScrollPane rejectedOrdersScrollPane = new JScrollPane(rejectedOrdersTable); 77 | rejectedOrdersScrollPane.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Rejected Orders")); 78 | add(rejectedOrdersScrollPane, BorderLayout.SOUTH); 79 | //Orders stats get updated every 1 sec. 80 | es.scheduleWithFixedDelay(() -> { 81 | ordersStats.clear(); 82 | ordersStats.add(buildOrdersStats(java.util.stream.Stream.concat(filledOrders.stream(), rejectedOrders.stream()).collect(Collectors.toList()))); 83 | ((AbstractTableModel)ordersStatsTable.getModel()).fireTableDataChanged(); 84 | }, 1, 1, TimeUnit.SECONDS); 85 | } 86 | 87 | private static JTable buildOrdersTable(final List orders, boolean filled) throws FileNotFoundException, IOException, JMSException { 88 | final JTable ordersTable = new JTable(filled ? new FilledOrdersTableModel(orders) : new RejectedOrdersTableModel(orders)) { 89 | private static final long serialVersionUID = 1L; 90 | public Component prepareRenderer(final TableCellRenderer renderer, final int row, final int column) 91 | { 92 | final Component component = super.prepareRenderer(renderer, row, column); 93 | switch ((OrderSide)getModel().getValueAt(convertRowIndexToModel(row), 3)) { 94 | case BUY: 95 | component.setBackground(Color.BLACK); 96 | component.setForeground(Color.WHITE); 97 | break; 98 | case SELL: 99 | component.setBackground(Color.RED); 100 | component.setForeground(Color.YELLOW); 101 | break; 102 | default: 103 | break; 104 | } 105 | return component; 106 | } 107 | }; 108 | ordersTable.getColumnModel().getColumn(filled ? 9 : 8).setCellRenderer(new DatetimeTableCellRenderer(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"))); 109 | //pre-set sorter enabled on the FilledDate column. 110 | SwingUtility.setTableSorter(ordersTable, filled ? 9 : 8, SortOrder.DESCENDING); 111 | ordersTable.setPreferredScrollableViewportSize(new Dimension(500, 70)); 112 | ordersTable.setFillsViewportHeight(true); 113 | return ordersTable; 114 | } 115 | 116 | public JTable getFilledOrdersTable() { 117 | return filledOrdersTable; 118 | } 119 | 120 | public JTable getRejectedOrdersTable() { 121 | return rejectedOrdersTable; 122 | } 123 | 124 | @Override 125 | public void cleanUp() throws Exception { 126 | Utility.shutdownExecutorService(es, 5, TimeUnit.SECONDS); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/database/noSql/MongoDBManager.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services.database.noSql; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | import java.util.Optional; 7 | import java.util.Properties; 8 | 9 | import org.bson.Document; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import com.mongodb.client.MongoCollection; 14 | import com.mongodb.client.MongoCursor; 15 | import com.mongodb.client.model.UpdateOptions; 16 | import com.projects.tradingMachine.services.database.DataManager; 17 | import com.projects.tradingMachine.utility.Utility; 18 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 19 | import com.projects.tradingMachine.utility.marketData.MarketData; 20 | import com.projects.tradingMachine.utility.order.OrderSide; 21 | import com.projects.tradingMachine.utility.order.OrderTimeInForce; 22 | import com.projects.tradingMachine.utility.order.OrderType; 23 | import com.projects.tradingMachine.utility.order.SimpleOrder; 24 | 25 | public final class MongoDBManager implements DataManager { 26 | private static Logger logger = LoggerFactory.getLogger(MongoDBManager.class); 27 | 28 | private final MongoDBConnection mongoDBConnection; 29 | private final MongoCollection executedOrdersCollection; 30 | private final MongoCollection marketDataCollection; 31 | 32 | public MongoDBManager(final MongoDBConnection mongoDBConnection, final String executedOrdersCollectionName, final String marketDataCollectionName) { 33 | this.mongoDBConnection = mongoDBConnection; 34 | executedOrdersCollection = mongoDBConnection.getMongoDatabase().getCollection(executedOrdersCollectionName); 35 | marketDataCollection = marketDataCollectionName == null ? null : mongoDBConnection.getMongoDatabase().getCollection(marketDataCollectionName); 36 | } 37 | 38 | public MongoDBManager(final MongoDBConnection mongoDBConnection, final String executedOrdersCollectionName) { 39 | this(mongoDBConnection, executedOrdersCollectionName, null); 40 | } 41 | 42 | @Override 43 | public void storeOrder(final SimpleOrder order) { 44 | executedOrdersCollection.replaceOne(new Document("FilledOrder", order.getID()), ConvertSimpleOrderToBSONDocument(order), 45 | new UpdateOptions().upsert(true)); 46 | logger.debug(order+" added to collection: "+executedOrdersCollection.toString()); 47 | } 48 | 49 | @Override 50 | public List getOrders(final Optional orderType) { 51 | long startTime = System.currentTimeMillis(); 52 | final List result = new ArrayList(); 53 | final MongoCursor cursor = orderType.isPresent() ? executedOrdersCollection.find(new Document("Type", orderType.get().toString())).iterator() : executedOrdersCollection.find().iterator(); 54 | try { 55 | while (cursor.hasNext()) { 56 | final Document doc = cursor.next(); 57 | result.add(new SimpleOrder(doc.getString("ID"), doc.getString("Symbol"), doc.getInteger("Quantity"), 58 | OrderSide.fromString(doc.getString("Side")), OrderType.fromString(doc.getString("Type")), OrderTimeInForce.fromString(doc.getString("TimeInForce")), 59 | doc.getDouble("LimitPrice"), doc.getDouble("StopPrice"), doc.getDouble("Price"), doc.getString("OriginalID"), doc.getDate("StoreDate"), 60 | doc.getBoolean("IsRejected"), doc.getString("MarketDataID"), doc.getBoolean("IsCreditCheckFailed", false))); 61 | } 62 | } finally { 63 | cursor.close(); 64 | } 65 | logger.info("Time taken to retrieve orders: "+(startTime - System.currentTimeMillis())+" ms."); 66 | return result; 67 | } 68 | 69 | @Override 70 | public void storeMarketDataItems(final List marketDataItems, final boolean deleteFirst) { 71 | logger.debug("Starting to store "+ marketDataItems.size()+" MarketData items..."); 72 | if (deleteFirst) 73 | marketDataCollection.deleteMany(new Document()); 74 | final List docs = new ArrayList(); 75 | marketDataItems.forEach(marketDataItem -> docs.add(ConvertMarketDataToBSONDocument(marketDataItem))); 76 | marketDataCollection.insertMany(docs); 77 | logger.debug("Data stored successfully"); 78 | } 79 | 80 | @Override 81 | public List getMarketData(final Optional symbol) { 82 | long startTime = System.currentTimeMillis(); 83 | final List result = new ArrayList(); 84 | final MongoCursor cursor = symbol.isPresent() ? marketDataCollection.find(new Document("Symbol", symbol.get())).iterator() : marketDataCollection.find().iterator(); 85 | try { 86 | while (cursor.hasNext()) { 87 | final Document doc = cursor.next(); 88 | result.add(new MarketData(doc.getString("ID"), doc.getString("Symbol"), doc.getDouble("Ask"), 89 | doc.getDouble("Bid"), doc.getInteger("AskSize"), doc.getInteger("BidSize"), doc.getDate("QuoteTime"))); 90 | } 91 | } finally { 92 | cursor.close(); 93 | } 94 | logger.info("Time taken to retrieve orders: "+(startTime - System.currentTimeMillis())+" ms."); 95 | return result; 96 | } 97 | 98 | @Override 99 | public void close() throws Exception { 100 | mongoDBConnection.close(); 101 | } 102 | 103 | private static Document ConvertSimpleOrderToBSONDocument(final SimpleOrder order) { 104 | return new Document("ID", order.getID()) 105 | .append("Symbol", order.getSymbol()) 106 | .append("Quantity",order.getQuantity()) 107 | .append("Side", order.getSide().toString()) 108 | .append("Type", order.getType().toString()) 109 | .append("TimeInForce", order.getTimeInForce().toString()) 110 | .append("LimitPrice", order.getLimit()) 111 | .append("StopPrice", order.getStop()) 112 | .append("Price", order.getAvgPx()) 113 | .append("OriginalID", order.getOriginalID()) 114 | .append("StoreDate", new Date()) 115 | .append("IsRejected", order.isRejected()) 116 | .append("MarketDataID", order.getMarketDataID()) 117 | .append("IsCreditCheckFailed", order.isCreditCheckFailed()); 118 | } 119 | 120 | private static Document ConvertMarketDataToBSONDocument(final MarketData marketData) { 121 | return new Document("ID", marketData.getID()). 122 | append("Symbol", marketData.getSymbol()) 123 | .append("Ask", marketData.getAsk()) 124 | .append("Bid",marketData.getBid()) 125 | .append("AskSize", marketData.getAskSize()) 126 | .append("BidSize", marketData.getBidSize()) 127 | .append("QuoteTime", marketData.getQuoteTime()); 128 | } 129 | 130 | public static void main(final String[] args) throws NumberFormatException, Exception { 131 | final Properties p = Utility.getApplicationProperties("tradingMachineServices.properties"); 132 | try(final DataManager mongoDBManager = new MongoDBManager(new MongoDBConnection(new DatabaseProperties(p.getProperty("mongoDB.host"), 133 | Integer.valueOf(p.getProperty("mongoDB.port")), p.getProperty("mongoDB.database"))), p.getProperty("mongoDB.executedOrdersCollection"))) { 134 | //System.out.println(mongoDBManager.getOrders(Optional.of(OrderType.STOP)).stream().mapToDouble(SimpleOrder::getAvgPx).summaryStatistics()); 135 | //mongoDBManager.getOrders(Optional.of(OrderType.LIMIT)).stream().map(SimpleOrder::getAvgPx).forEach(System.out::println); 136 | //System.out.println(mongoDBManager.getOrders(Optional.ofNullable(null)).stream().collect(Collectors.groupingBy(SimpleOrder::getType, Collectors.counting()))); 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /TradingMachineMonitorUI/src/main/java/com/projects/tradingMachine/tradeMonitor/TradeMonitorUI.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.tradeMonitor; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Point; 5 | import java.awt.event.WindowAdapter; 6 | import java.awt.event.WindowEvent; 7 | import java.io.FileNotFoundException; 8 | import java.io.IOException; 9 | import java.io.Serializable; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.Comparator; 13 | import java.util.List; 14 | import java.util.Optional; 15 | import java.util.Properties; 16 | import java.util.concurrent.Executors; 17 | import java.util.stream.Collectors; 18 | import java.util.stream.IntStream; 19 | 20 | import javax.jms.JMSException; 21 | import javax.jms.Message; 22 | import javax.jms.MessageListener; 23 | import javax.jms.ObjectMessage; 24 | import javax.swing.JFrame; 25 | import javax.swing.JTabbedPane; 26 | import javax.swing.table.AbstractTableModel; 27 | 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | import com.projects.tradingMachine.services.database.noSql.MongoDBConnection; 32 | import com.projects.tradingMachine.services.database.noSql.MongoDBManager; 33 | import com.projects.tradingMachine.services.simulation.orders.RandomOrdersBuilder; 34 | import com.projects.tradingMachine.utility.TradingMachineMessageConsumer; 35 | import com.projects.tradingMachine.utility.Utility; 36 | import com.projects.tradingMachine.utility.Utility.DestinationType; 37 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 38 | import com.projects.tradingMachine.utility.marketData.MarketData; 39 | import com.projects.tradingMachine.utility.order.SimpleOrder; 40 | 41 | /** 42 | * Creates and shows the application, which displays filled and rejected orders. 43 | * Initially, the respective JTables are filled with orders coming a MongoDB repository. Then, they get live updates from the executedOrdersTopic. 44 | * Upon application shutdown, it closes MongoDB and topic subscriber connections. 45 | * */ 46 | public final class TradeMonitorUI implements MessageListener { 47 | private static Logger logger = LoggerFactory.getLogger(TradeMonitorUI.class); 48 | private final TradingMachineMessageConsumer executedOrdersConsumer; 49 | private final TradingMachineMessageConsumer marketDataConsumer; 50 | private final MongoDBManager mongoDBManager; 51 | private final Comparator dateComparator = (c1, c2) -> c1.getStoreDate().compareTo(c2.getStoreDate()); 52 | private final List filledOrders; 53 | private final List rejectedOrders; 54 | private final List marketDataItems; 55 | private final OrdersPanel ordersPanel; 56 | private final MarketDataPanel marketDataPanel; 57 | private static final boolean isWithoutLiveFeed = false; 58 | 59 | public TradeMonitorUI(final Properties p) throws JMSException, FileNotFoundException, IOException { 60 | mongoDBManager = new MongoDBManager(new MongoDBConnection(new DatabaseProperties(p.getProperty("mongoDB.host"), 61 | Integer.valueOf(p.getProperty("mongoDB.port")), p.getProperty("mongoDB.database"))), p.getProperty("mongoDB.executedOrdersCollection"), p.getProperty("mongoDB.marketDataCollection")); 62 | executedOrdersConsumer = new TradingMachineMessageConsumer(p.getProperty("activeMQ.url"), 63 | p.getProperty("activeMQ.executedOrdersTopic"), DestinationType.Topic, this, "TradeMonitorExecutedOrdersConsumer", null, null); 64 | marketDataConsumer = new TradingMachineMessageConsumer(p.getProperty("activeMQ.url"), 65 | p.getProperty("activeMQ.marketDataTopic"), DestinationType.Topic, this, "TradeMonitorMarketDataConsumer", null, null); 66 | marketDataItems = mongoDBManager.getMarketData(Optional.ofNullable(null)); 67 | final List backEndOrders = mongoDBManager.getOrders(Optional.ofNullable(null)); 68 | filledOrders = backEndOrders.stream().filter(o -> !o.isRejected()).sorted(dateComparator.reversed()).collect(Collectors.toList());//sorting isn't strictly needed because it'd have been done by the table sorter. 69 | rejectedOrders = backEndOrders.stream().filter(o -> o.isRejected()).sorted(dateComparator.reversed()).collect(Collectors.toList()); 70 | ordersPanel = new OrdersPanel(filledOrders, rejectedOrders); 71 | //ordersPanel.getFilledOrdersTable().getColumnModel().getColumn(10).setCellRenderer(new TooltipCellRenderer(marketDataItems)); 72 | marketDataPanel = new MarketDataPanel(marketDataItems); 73 | executedOrdersConsumer.start(); 74 | marketDataConsumer.start(); 75 | if (isWithoutLiveFeed) 76 | simulationWithoutLiveFeed(); 77 | } 78 | 79 | public void show() throws JMSException, FileNotFoundException, IOException { 80 | final JFrame frame = new JFrame("Trade Monitor"); 81 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 82 | final JTabbedPane tb = new JTabbedPane(); 83 | tb.addTab("Orders", ordersPanel); 84 | tb.addTab("Market Data", marketDataPanel); 85 | tb.setSelectedIndex(0); 86 | tb.setOpaque(true); 87 | frame.setContentPane(tb); 88 | frame.addWindowListener(new WindowAdapter(){ 89 | public void windowClosing(WindowEvent e){ 90 | try { 91 | mongoDBManager.close(); 92 | } catch (final Exception e1) { 93 | logger.warn("Unable to close the MongoDB connection.\n"+e1.getMessage()); 94 | } 95 | try { 96 | executedOrdersConsumer.stop(); 97 | } catch (final Exception e1) { 98 | logger.warn("Unable to close the executedOrdersConsumer topic subscriber.\n"+e1.getMessage()); 99 | } 100 | try { 101 | marketDataConsumer.stop(); 102 | } catch (final Exception e1) { 103 | logger.warn("Unable to close the marketDataConsumer topic subscriber.\n"+e1.getMessage()); 104 | } 105 | try { 106 | ordersPanel.cleanUp(); 107 | marketDataPanel.cleanUp(); 108 | } catch (final Exception e1) { 109 | logger.warn("Unable to clean up OrdersPanel resources.\n"+e1.getMessage()); 110 | } 111 | } 112 | }); 113 | frame.setLocation(new Point(300, 300)); 114 | frame.setSize(new Dimension(1500, 700)); 115 | frame.setVisible(true); 116 | } 117 | 118 | @SuppressWarnings("unchecked") 119 | @Override 120 | public void onMessage(final Message message) { 121 | try { 122 | final Serializable objectMessage = ((ObjectMessage)message).getObject(); 123 | if (objectMessage instanceof SimpleOrder) { 124 | //gets the filled order and updates and notifies the table model accordingly. 125 | final SimpleOrder order = (SimpleOrder)objectMessage; 126 | if (order.isRejected()) { 127 | rejectedOrders.add(order); 128 | ((AbstractTableModel)ordersPanel.getRejectedOrdersTable().getModel()).fireTableDataChanged(); 129 | } 130 | else { 131 | filledOrders.add(order); 132 | ((AbstractTableModel)ordersPanel.getFilledOrdersTable().getModel()).fireTableDataChanged(); 133 | } 134 | } 135 | else if (objectMessage instanceof ArrayList) { 136 | marketDataItems.addAll((ArrayList)objectMessage); 137 | ((AbstractTableModel)marketDataPanel.getMarketDataTable().getModel()).fireTableDataChanged(); 138 | } 139 | 140 | } catch (final JMSException e) { 141 | logger.warn("Failed to process object message, due to "+e.getMessage()); 142 | } 143 | } 144 | 145 | public static void main(final String[] args) { 146 | javax.swing.SwingUtilities.invokeLater(() -> { 147 | try { 148 | new TradeMonitorUI(Utility.getApplicationProperties("tradeMonitor.properties")).show(); 149 | } catch (final Exception e) { 150 | throw new RuntimeException(e); 151 | } 152 | }); 153 | } 154 | 155 | private void simulationWithoutLiveFeed() { 156 | Executors.newSingleThreadScheduledExecutor().execute(() -> { 157 | IntStream.range(1, 100000).forEach(i -> 158 | { 159 | Arrays.asList("ABBN","BION","HBMN","AEVS","SAHN","RO","RIEN","UBSN","CSGN").stream().forEach(a -> { 160 | marketDataItems.add(Utility.buildRandomMarketDataItem(a)); 161 | ((AbstractTableModel)marketDataPanel.getMarketDataTable().getModel()).fireTableDataChanged(); 162 | }); 163 | try { 164 | Thread.sleep(1000); 165 | } catch (final Exception e) { 166 | e.printStackTrace(); 167 | } 168 | }); 169 | }); 170 | Executors.newSingleThreadScheduledExecutor().execute(() -> { 171 | IntStream.range(0, 10000).forEach(i -> { 172 | final SimpleOrder randomOrder = RandomOrdersBuilder.build(Arrays.asList("RIEN", "UBSN", "CSGN")); 173 | if (randomOrder.isRejected()) { 174 | rejectedOrders.add(randomOrder); 175 | ((AbstractTableModel)ordersPanel.getRejectedOrdersTable().getModel()).fireTableDataChanged(); 176 | } 177 | else { 178 | filledOrders.add(randomOrder); 179 | ((AbstractTableModel)ordersPanel.getFilledOrdersTable().getModel()).fireTableDataChanged(); 180 | } 181 | try { 182 | Thread.sleep(500); 183 | } catch (final Exception e) { 184 | e.printStackTrace(); 185 | } 186 | }); 187 | }); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /TradingMachineServices/src/main/java/com/projects/tradingMachine/services/StatsRunner.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.services; 2 | 3 | import static java.util.stream.Collectors.groupingBy; 4 | 5 | import java.util.Comparator; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Optional; 9 | import java.util.Properties; 10 | import java.util.concurrent.Executors; 11 | import java.util.concurrent.Future; 12 | import java.util.concurrent.ScheduledExecutorService; 13 | import java.util.concurrent.TimeUnit; 14 | import java.util.stream.Collectors; 15 | 16 | import javax.jms.JMSException; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import com.projects.tradingMachine.services.database.noSql.MongoDBConnection; 22 | import com.projects.tradingMachine.services.database.noSql.MongoDBManager; 23 | import com.projects.tradingMachine.utility.Utility; 24 | import com.projects.tradingMachine.utility.database.DatabaseProperties; 25 | import com.projects.tradingMachine.utility.order.OrderSide; 26 | import com.projects.tradingMachine.utility.order.OrderTimeInForce; 27 | import com.projects.tradingMachine.utility.order.OrderType; 28 | import com.projects.tradingMachine.utility.order.SimpleOrder; 29 | 30 | /** 31 | * Extracts statistics out of the filled orders stored to the MongoDB database. 32 | * 33 | * */ 34 | public final class StatsRunner implements Runnable { 35 | private static Logger logger = LoggerFactory.getLogger(StatsRunner.class); 36 | 37 | private final Properties properties; 38 | private final MongoDBManager mongoDBManager; 39 | 40 | public StatsRunner(final Properties properties) { 41 | this.properties = properties; 42 | mongoDBManager = new MongoDBManager(new MongoDBConnection(new DatabaseProperties(properties.getProperty("mongoDB.host"), 43 | Integer.valueOf(properties.getProperty("mongoDB.port")), properties.getProperty("mongoDB.database"))), properties.getProperty("mongoDB.executedOrdersCollection")); 44 | } 45 | 46 | @Override 47 | public void run() { 48 | while (!Thread.currentThread().isInterrupted()) { 49 | try { 50 | final List orders = mongoDBManager.getOrders(Optional.ofNullable(null)); 51 | logger.info(groupOrdersByType(orders)+groupOrdersBySide(orders, 5)+groupOrdersBySideTypeTimeInForce(orders)+groupOrdersWithAndWithoutMarketData(orders)); 52 | //testReduction(orders); 53 | TimeUnit.SECONDS.sleep(Integer.valueOf(properties.getProperty("statsPublishingPeriod"))); 54 | } 55 | catch(final InterruptedException ex) { 56 | Thread.currentThread().interrupt(); 57 | } 58 | catch (final Exception e) { 59 | logger.warn("Unable to produce marked data, due to: "+e.getMessage()); 60 | } 61 | } 62 | try { 63 | mongoDBManager.close(); 64 | } catch (final Exception e) { 65 | logger.warn("Unable to close the MongoDB connection: "+e.getMessage()); 66 | throw new RuntimeException(e); 67 | } 68 | } 69 | 70 | /** 71 | * Statistics based on the order type. 72 | * */ 73 | private String groupOrdersByType(final List orders) { 74 | final StringBuilder sb = new StringBuilder("\n"); 75 | final Map> groupedOrders = orders.parallelStream().collect(Collectors.groupingBy(SimpleOrder::getType)); 76 | for (final Map.Entry> ot : groupedOrders.entrySet()) { 77 | final String avgMarketPrice = String.valueOf(Utility.roundDouble(ot.getValue().stream().mapToDouble(SimpleOrder::getAvgPx).average().getAsDouble(), 2)); 78 | switch(ot.getKey()) { 79 | case MARKET: 80 | sb.append("Market Orders: \n\t").append("Average price: ").append(avgMarketPrice).append("\n\t"); 81 | break; 82 | case LIMIT: 83 | sb.append("Limit Orders: \n\t").append("Average market price: ").append(avgMarketPrice).append("\n\t"). 84 | append("Average limit price: ").append(String.valueOf(Utility.roundDouble(ot.getValue().stream().mapToDouble(SimpleOrder::getLimit).average().getAsDouble(), 2))).append("\n\t"); 85 | break; 86 | case STOP: 87 | sb.append("Stop Orders: \n\t").append("Average market price: ").append(avgMarketPrice).append("\n\t"). 88 | append("Average stop price: ").append(String.valueOf(Utility.roundDouble(ot.getValue().stream().mapToDouble(SimpleOrder::getStop).average().getAsDouble(), 2))).append("\n\t"); 89 | break; 90 | } 91 | sb.append("Average quantity: ").append(String.valueOf(Utility.roundDouble(ot.getValue().stream().mapToDouble(SimpleOrder::getQuantity).average().getAsDouble(), 2))).append("\n\t"); 92 | sb.append("Orders number: ").append(String.valueOf(ot.getValue().size())).append("\n\n"); 93 | } 94 | return sb.toString(); 95 | } 96 | 97 | /** 98 | * Statistics based on the order side. 99 | * */ 100 | private String groupOrdersBySide(final List orders, int topOrdersLimit) { 101 | final StringBuilder sb = new StringBuilder("\n"); 102 | final Map> groupedOrders = orders.parallelStream().collect(Collectors.groupingBy(SimpleOrder::getSide)); 103 | final Comparator ordersQuantityComparator = (c1, c2) -> Integer.compare(c1.getQuantity(), c2.getQuantity()); 104 | for (final Map.Entry> ot : groupedOrders.entrySet()) { 105 | switch(ot.getKey()) { 106 | case BUY: 107 | sb.append("BUY orders number: ").append(String.valueOf(ot.getValue().size())).append("\n"); 108 | sb.append("Top "+topOrdersLimit+" biggest quantity BUY orders: \n\t").append(ot.getValue().stream().sorted(ordersQuantityComparator.reversed()).limit(topOrdersLimit).map(so -> so.getSymbol()+"/ "+so.getQuantity()+"/ "+so.getStoreDate()).collect(Collectors.joining("\n\t"))).append("\n\n"); 109 | break; 110 | case SELL: 111 | sb.append("SELL orders number: ").append(String.valueOf(ot.getValue().size())).append("\n"); 112 | sb.append("Top "+topOrdersLimit+" smallest quantity SELL orders: \n\t").append(ot.getValue().stream(). 113 | sorted(Comparator.comparingInt(SimpleOrder::getQuantity)).limit(topOrdersLimit).map(so -> so.getSymbol()+"/ "+so.getQuantity()+"/ "+so.getStoreDate()).collect(Collectors.joining("\n\t"))).append("\n\n"); 114 | //Comparator.comparingInt(SimpleOrder::getQuantity) --> the method reference comparator way. 115 | break; 116 | } 117 | } 118 | return sb.toString(); 119 | } 120 | 121 | /** 122 | * Does a three-levels grouping based on side, type and time in force. 123 | * */ 124 | private String groupOrdersBySideTypeTimeInForce(final List orders) { 125 | final StringBuilder sb = new StringBuilder("Orders split based on side, type and time in force.\n umber of\n"); 126 | final Map>> threeLeveslGroupedOrders = orders.parallelStream(). 127 | collect(groupingBy(SimpleOrder::getSide, groupingBy(SimpleOrder::getType, groupingBy(SimpleOrder::getTimeInForce, Collectors.counting())))); 128 | for (final Map.Entry>> threeLeveslGroupedOrdersEntry : threeLeveslGroupedOrders.entrySet()) { 129 | for (final Map.Entry> groupedByOrderTypeAndTimeInForceEntry : threeLeveslGroupedOrdersEntry.getValue().entrySet()) 130 | for (final Map.Entry groupedByTimeInForceEntry : groupedByOrderTypeAndTimeInForceEntry.getValue().entrySet()) 131 | sb.append("\t"+threeLeveslGroupedOrdersEntry.getKey()+"/ "+ groupedByOrderTypeAndTimeInForceEntry.getKey()+ "/ "+groupedByTimeInForceEntry.getKey()+ " orders: "+groupedByTimeInForceEntry.getValue()+"\n"); 132 | } 133 | sb.append("\n"); 134 | return sb.toString(); 135 | } 136 | 137 | private String groupOrdersWithAndWithoutMarketData(final List orders) { 138 | final Map groupByMarketDataID = orders.parallelStream(). 139 | collect(Collectors.partitioningBy(so -> so.getMarketDataID() != null, Collectors.counting())); 140 | return "\nOrders with market data id: "+groupByMarketDataID.get(true)+", without: "+groupByMarketDataID.get(false)+"\n"; 141 | } 142 | 143 | public static void main(final String[] args) throws JMSException, Exception { 144 | final ScheduledExecutorService es = Executors.newScheduledThreadPool(1); 145 | final Future f = es.submit(new StatsRunner(Utility.getApplicationProperties("tradingMachineServices.properties"))); 146 | TimeUnit.SECONDS.sleep(60); 147 | f.cancel(true); 148 | Utility.shutdownExecutorService(es, 1, TimeUnit.SECONDS); 149 | } 150 | 151 | /** 152 | * Tests different ways to do the same reduction. 153 | */ 154 | /*private void testReduction(final List orders) { 155 | final int quantitySum1 = orders.parallelStream().mapToInt(SimpleOrder::getQuantity).reduce(0, (so1, so2) -> so1 + so2); 156 | final int quantitySum2 = orders.parallelStream().mapToInt(SimpleOrder::getQuantity).reduce(0, Integer::sum); 157 | final int quantitySum3 = orders.parallelStream().collect(Collectors.reducing(0, SimpleOrder::getQuantity, Integer::sum)); 158 | final Integer quantitySum4 = orders.parallelStream().collect(Collectors.summingInt(SimpleOrder::getQuantity)); 159 | final int quantitySum5 = orders.parallelStream().mapToInt(SimpleOrder::getQuantity).sum(); 160 | System.out.println(quantitySum1+"/ "+quantitySum2+"/ "+quantitySum3+"/"+quantitySum4+"/ "+quantitySum5); 161 | }*/ 162 | } -------------------------------------------------------------------------------- /TradingMachineOrderRouter/src/main/java/com/projects/tradingMachine/orderRouter/TradingMachineFixInitiatorApplication.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.orderRouter; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.math.BigDecimal; 6 | import java.util.HashSet; 7 | import java.util.Properties; 8 | import java.util.Set; 9 | 10 | import javax.jms.ExceptionListener; 11 | import javax.jms.JMSException; 12 | import javax.jms.MessageListener; 13 | import javax.jms.ObjectMessage; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import com.projects.tradingMachine.utility.TradingMachineMessageConsumer; 19 | import com.projects.tradingMachine.utility.TradingMachineMessageProducer; 20 | import com.projects.tradingMachine.utility.Utility; 21 | import com.projects.tradingMachine.utility.Utility.DestinationType; 22 | import com.projects.tradingMachine.utility.order.SimpleOrder; 23 | 24 | import quickfix.Application; 25 | import quickfix.Dictionary; 26 | import quickfix.DoNotSend; 27 | import quickfix.FieldNotFound; 28 | import quickfix.IncorrectDataFormat; 29 | import quickfix.IncorrectTagValue; 30 | import quickfix.Message; 31 | import quickfix.RejectLogon; 32 | import quickfix.Session; 33 | import quickfix.SessionID; 34 | import quickfix.SessionNotFound; 35 | import quickfix.SessionSettings; 36 | import quickfix.UnsupportedMessageType; 37 | import quickfix.field.Account; 38 | import quickfix.field.AvgPx; 39 | import quickfix.field.ClOrdID; 40 | import quickfix.field.CumQty; 41 | import quickfix.field.HandlInst; 42 | import quickfix.field.LeavesQty; 43 | import quickfix.field.MsgType; 44 | import quickfix.field.OrdStatus; 45 | import quickfix.field.OrderQty; 46 | import quickfix.field.Price; 47 | import quickfix.field.StopPx; 48 | import quickfix.field.Symbol; 49 | import quickfix.field.Text; 50 | import quickfix.field.TransactTime; 51 | 52 | /** 53 | * FIX initiator application implementor. It listens to the OrdersQueue for orders to send to the FIX executor. 54 | * It finally publishes filled orders to the FilledOrdersTopic. 55 | * */ 56 | public class TradingMachineFixInitiatorApplication implements Application, MessageListener, ExceptionListener { 57 | private static final Logger logger = LoggerFactory.getLogger(TradingMachineFixInitiatorApplication.class); 58 | 59 | private final SessionSettings settings; 60 | private final OrderManager orderManager; 61 | private final TradingMachineMessageConsumer ordersConsumer; 62 | private final TradingMachineMessageProducer executedOrdersProducer; 63 | private final Set loggedOnSessions; 64 | 65 | public TradingMachineFixInitiatorApplication(final SessionSettings settings) throws JMSException, FileNotFoundException, IOException { 66 | this.settings = settings; 67 | final Properties p = Utility.getApplicationProperties("tradingMachineOrderRouter.properties"); 68 | orderManager = new OrderManager(); 69 | loggedOnSessions = new HashSet(); 70 | ordersConsumer = new TradingMachineMessageConsumer(p.getProperty("activeMQ.url"), p.getProperty("activeMQ.ordersQueue"), DestinationType.Queue, this, "FixInitiatorApplication", null, this); 71 | ordersConsumer.start(); 72 | executedOrdersProducer = new TradingMachineMessageProducer(p.getProperty("activeMQ.url"), p.getProperty("activeMQ.executedOrdersTopic"), DestinationType.Topic, "FixInitiatorApplication", null); 73 | executedOrdersProducer.start(); 74 | } 75 | 76 | @Override 77 | public void onCreate(final SessionID sessionId) { 78 | logger.info("Session created: "+sessionId); 79 | } 80 | 81 | @Override 82 | public void onLogon(final SessionID sessionId) { 83 | logger.info("Logon: "+sessionId); 84 | loggedOnSessions.add(sessionId); 85 | } 86 | 87 | @Override 88 | public void onLogout(final SessionID sessionId) { 89 | logger.info("Logon: "+sessionId); 90 | loggedOnSessions.remove(sessionId); 91 | } 92 | 93 | @Override 94 | public void toAdmin(final Message message, final SessionID sessionId) { 95 | try { 96 | if(MsgType.LOGON.compareTo(message.getHeader().getString(MsgType.FIELD)) == 0) 97 | { 98 | final Dictionary dict = settings.get(sessionId); 99 | message.setString(quickfix.field.Username.FIELD, dict.getString("UserName")); 100 | message.setString(quickfix.field.Password.FIELD, dict.getString("Password")); 101 | } 102 | } 103 | catch(final Exception e) { 104 | logger.warn("Error setting user/ password."); 105 | e.printStackTrace(); 106 | } 107 | } 108 | 109 | @Override 110 | public void fromAdmin(final Message message, final SessionID sessionId) 111 | throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon { 112 | 113 | } 114 | 115 | @Override 116 | public void toApp(final Message message, final SessionID sessionId) throws DoNotSend { 117 | } 118 | 119 | @Override 120 | public void fromApp(final Message message, final SessionID sessionId) 121 | throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType { 122 | try { 123 | final MsgType msgType = new MsgType(); 124 | if (message.getHeader().getField(msgType).valueEquals("8")) { 125 | executionReport(message, sessionId); 126 | } 127 | } catch (final Exception e) { 128 | logger.warn(e.getMessage()); 129 | e.printStackTrace(); 130 | } 131 | 132 | } 133 | 134 | public void send(final SimpleOrder order) { 135 | final quickfix.fix50.NewOrderSingle newOrderSingle = new quickfix.fix50.NewOrderSingle( 136 | new ClOrdID(order.getID()), order.getSide().toFIXSide(), 137 | new TransactTime(), order.getType().toFIXOrderType()); 138 | newOrderSingle.set(new OrderQty(order.getQuantity())); 139 | newOrderSingle.set(new Symbol(order.getSymbol())); 140 | newOrderSingle.set(new HandlInst('1')); 141 | switch(order.getType()) { 142 | case LIMIT: 143 | newOrderSingle.setField(new Price(order.getLimit())); 144 | break; 145 | case STOP: 146 | newOrderSingle.setField(new StopPx(order.getStop())); 147 | break; 148 | default: 149 | break; 150 | } 151 | //else market order. 152 | newOrderSingle.setField(order.getTimeInForce().toFIXTimeInForce()); 153 | try { 154 | Session.sendToTarget(newOrderSingle, order.getSessionID()); 155 | orderManager.add(order); 156 | logger.info("Sent "+order); 157 | } catch (final SessionNotFound e) { 158 | logger.warn("Unable to send order", e); 159 | } 160 | } 161 | 162 | private void executionReport(final Message message, final SessionID sessionID) throws FieldNotFound, JMSException { 163 | final SimpleOrder order = orderManager.getOrder(message.getField(new ClOrdID()).getValue()); 164 | if (order == null) 165 | return; 166 | try { 167 | order.setMessage(message.getField(new Text()).getValue()); 168 | } 169 | catch (final FieldNotFound e) {} 170 | BigDecimal fillSize; 171 | final LeavesQty leavesQty = new LeavesQty(); 172 | message.getField(leavesQty); 173 | fillSize = new BigDecimal(order.getQuantity()).subtract(new BigDecimal(leavesQty.getValue())); 174 | 175 | if (fillSize.compareTo(BigDecimal.ZERO) > 0) { 176 | //execution. 177 | order.setOpen(order.getOpen() - (int) Double.parseDouble(fillSize.toPlainString())); 178 | order.setExecuted(new Integer(message.getString(CumQty.FIELD))); 179 | order.setAvgPx(new Double(message.getString(AvgPx.FIELD))); 180 | } 181 | final char ordStatus = ((OrdStatus) message.getField(new OrdStatus())).getValue(); 182 | switch(ordStatus) { 183 | case OrdStatus.REJECTED: 184 | order.setRejected(true); 185 | order.setOpen(0); 186 | if (message.isSetField(new Account())) 187 | order.setCreditCheckFailed(true); 188 | final ObjectMessage m = executedOrdersProducer.getSession().createObjectMessage(order); 189 | m.setStringProperty("Status", "REJECTED"); 190 | executedOrdersProducer.getProducer().send(m); 191 | break; 192 | case OrdStatus.CANCELED: 193 | case OrdStatus.DONE_FOR_DAY: 194 | order.setCanceled(true); 195 | order.setOpen(0); 196 | break; 197 | case OrdStatus.NEW: 198 | if (order.isNew()) 199 | order.setNew(false); 200 | break; 201 | case OrdStatus.FILLED: 202 | order.setMarketDataId(message.getField(new Text()).getValue()); 203 | final ObjectMessage m1 = executedOrdersProducer.getSession().createObjectMessage(order); 204 | m1.setStringProperty("Status", "FILLED"); 205 | executedOrdersProducer.getProducer().send(m1); 206 | break; 207 | } 208 | orderManager.updateOrder(order); 209 | } 210 | 211 | @Override 212 | public void onMessage(final javax.jms.Message message) { 213 | if (message instanceof ObjectMessage) 214 | try { 215 | loggedOnSessions.forEach(sessionID -> { 216 | try { 217 | final SimpleOrder order = (SimpleOrder)((ObjectMessage)message).getObject(); 218 | order.setSessionID(sessionID); 219 | send(order); 220 | } catch (final Exception e) { 221 | throw new RuntimeException(e); 222 | } 223 | }); 224 | } catch (final Exception e) { 225 | logger.warn("Error receiving order.\n"+e.getMessage()); 226 | } 227 | } 228 | 229 | @Override 230 | public void onException(final JMSException jmsEx) { 231 | logger.warn(jmsEx.getMessage()); 232 | } 233 | 234 | public void closeOrdersConsumer() throws JMSException { 235 | ordersConsumer.stop(); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /TradingMachineServer/src/main/java/com/projects/tradingMachine/server/MatchingEngine.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.server; 2 | 3 | import java.sql.SQLException; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | import org.apache.commons.dbcp2.BasicDataSource; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import com.projects.tradingMachine.utility.Utility; 11 | import com.projects.tradingMachine.utility.database.creditCheck.CreditCheck; 12 | import com.projects.tradingMachine.utility.database.creditCheck.ICreditCheck; 13 | import com.projects.tradingMachine.utility.marketData.MarketData; 14 | 15 | import quickfix.FieldNotFound; 16 | import quickfix.LogUtil; 17 | import quickfix.Message; 18 | import quickfix.SessionID; 19 | import quickfix.field.Account; 20 | import quickfix.field.AvgPx; 21 | import quickfix.field.CumQty; 22 | import quickfix.field.ExecID; 23 | import quickfix.field.ExecType; 24 | import quickfix.field.LastPx; 25 | import quickfix.field.LastQty; 26 | import quickfix.field.LeavesQty; 27 | import quickfix.field.OrdStatus; 28 | import quickfix.field.OrdType; 29 | import quickfix.field.OrderID; 30 | import quickfix.field.Price; 31 | import quickfix.field.Side; 32 | import quickfix.field.StopPx; 33 | import quickfix.field.Symbol; 34 | import quickfix.field.Text; 35 | import quickfix.field.TimeInForce; 36 | 37 | /** 38 | * Given an order and a link to the market data manager, it tries to match limit and stop orders based on the current market data. 39 | * It always fills market orders unless they are FOK and no immediate fill is possible. 40 | * The market data provides bid, ask prices and sizes for a given symbol. 41 | * */ 42 | public final class MatchingEngine implements Runnable { 43 | private static final Logger log = LoggerFactory.getLogger(MatchingEngine.class); 44 | 45 | private static final AtomicInteger orderIdSequence = new AtomicInteger(0); 46 | private static final AtomicInteger execIdSequence = new AtomicInteger(0); 47 | private final MarketDataManager marketDataManager; 48 | private final quickfix.fix50.NewOrderSingle order; 49 | private final SessionID sessionID; 50 | private final ICreditCheck creditCheck; 51 | 52 | public MatchingEngine(final BasicDataSource creditCheckConnectionPool, final MarketDataManager marketDataManager, final quickfix.fix50.NewOrderSingle order, final SessionID sessionID) throws NumberFormatException, ClassNotFoundException, SQLException { 53 | this.marketDataManager = marketDataManager; 54 | this.order = order; 55 | this.sessionID = sessionID; 56 | creditCheck = new CreditCheck(creditCheckConnectionPool.getConnection()); 57 | } 58 | 59 | @Override 60 | public void run() { 61 | try { 62 | final quickfix.fix50.ExecutionReport accept = new quickfix.fix50.ExecutionReport( 63 | buildOrderID(), buildExecID(), new ExecType(ExecType.FILL), new OrdStatus(OrdStatus.NEW), order.getSide(), 64 | new LeavesQty(order.getOrderQty().getValue()), new CumQty(0)); 65 | accept.set(order.getClOrdID()); 66 | accept.set(order.getSymbol()); 67 | Utility.sendMessage(sessionID, accept); 68 | //try to fill now. 69 | final PriceQuantity priceQuantity = findPriceAndQuantity(order, 100); 70 | if (priceQuantity != null) { 71 | if (!creditCheck.hasEnoughCredit(priceQuantity.getValue())) 72 | sendReject(order, sessionID, true); 73 | else { 74 | final quickfix.fix50.ExecutionReport executionReport = new quickfix.fix50.ExecutionReport( 75 | buildOrderID(), buildExecID(), new ExecType(ExecType.FILL), new OrdStatus( 76 | OrdStatus.FILLED), order.getSide(), new LeavesQty(0), new CumQty(priceQuantity.getQuantity())); 77 | executionReport.set(order.getClOrdID()); 78 | executionReport.set(order.getSymbol()); 79 | executionReport.set(order.getOrderQty()); 80 | executionReport.set(new Text(String.valueOf(priceQuantity.getMarketDataId()))); 81 | executionReport.set(new LastQty(priceQuantity.getQuantity())); 82 | executionReport.set(new LastPx(priceQuantity.getPrice())); 83 | executionReport.set(new AvgPx(priceQuantity.getPrice())); 84 | creditCheck.setCredit(-priceQuantity.getValue()); 85 | Utility.sendMessage(sessionID, executionReport); 86 | } 87 | } 88 | else //order rejected. 89 | sendReject(order, sessionID, false); 90 | } catch (final Exception e) { 91 | LogUtil.logThrowable(sessionID, e.getMessage(), e); 92 | } 93 | finally { 94 | try { 95 | creditCheck.closeConnection(); 96 | } catch (final SQLException e) { 97 | log.warn("Unable to close credit check database connection:\n"+e.getMessage()); 98 | } 99 | } 100 | } 101 | 102 | private static void sendReject(final quickfix.fix50.NewOrderSingle order, final SessionID sessionID, final boolean creditCheckFailed) throws FieldNotFound { 103 | final quickfix.fix50.ExecutionReport executionReport = new quickfix.fix50.ExecutionReport( 104 | buildOrderID(), buildExecID(), new ExecType(ExecType.REJECTED), new OrdStatus(OrdStatus.REJECTED), 105 | order.getSide(), new LeavesQty(order.getOrderQty().getValue()), new CumQty(0)); 106 | executionReport.set(order.getClOrdID()); 107 | if (creditCheckFailed) 108 | executionReport.set(new Account("Failed Credit Check"));//indicates not enough credit. 109 | Utility.sendMessage(sessionID, executionReport); 110 | } 111 | 112 | private PriceQuantity findPriceAndQuantity(final quickfix.fix50.NewOrderSingle order, final int maxNrTrials) throws FieldNotFound, InterruptedException { 113 | int counter = 0; 114 | final int orderQuantity = (int)(order.getOrderQty().getValue()); 115 | switch(order.getChar(OrdType.FIELD)) { 116 | case OrdType.LIMIT: 117 | final double limitPrice = order.getDouble(Price.FIELD); 118 | //loop until the limit order price is executable. 119 | final char limitOrderSide = order.getChar(Side.FIELD); 120 | while(counter < maxNrTrials) { 121 | final PriceQuantity marketPriceQuantity = getMarketPriceQuantity(order); 122 | if (((limitOrderSide == Side.BUY && Double.compare(marketPriceQuantity.getPrice(), limitPrice) <= 0) 123 | || (limitOrderSide == Side.SELL && Double.compare(marketPriceQuantity.getPrice(), limitPrice) >= 0)) && (orderQuantity >= marketPriceQuantity.getQuantity())) { 124 | log.info("Found filling price/ quantity for limit order, market price: "+marketPriceQuantity.getPrice()+", limit price: "+limitPrice+", quantity: "+orderQuantity); 125 | return new PriceQuantity(marketPriceQuantity.getPrice(), orderQuantity, marketPriceQuantity.getMarketDataId()); 126 | } 127 | else { 128 | if (order.getChar(TimeInForce.FIELD) == TimeInForce.FILL_OR_KILL) 129 | return null; 130 | log.debug("Looping to find filling price for limit order, market price: "+marketPriceQuantity.getQuantity()+", limit price: "+limitPrice); 131 | Thread.sleep(500); 132 | counter++; 133 | } 134 | } 135 | return null; //price/ quantity not found. 136 | case OrdType.STOP: 137 | final double stopPrice = order.getDouble(StopPx.FIELD); 138 | //loop until the stop order price is executable. 139 | final char stopOrderSide = order.getChar(Side.FIELD); 140 | while(counter < maxNrTrials) { 141 | final PriceQuantity marketPriceQuantity = getMarketPriceQuantity(order); 142 | if (((stopOrderSide == Side.BUY && Double.compare(marketPriceQuantity.getPrice(), stopPrice) > 0) 143 | || (stopOrderSide == Side.SELL && Double.compare(marketPriceQuantity.getPrice(), stopPrice) < 0)) && (orderQuantity >= marketPriceQuantity.getQuantity())) { 144 | log.info("Found filling price for stop order, market price: "+marketPriceQuantity.getPrice()+", stop price: "+stopPrice); 145 | return new PriceQuantity(marketPriceQuantity.getPrice(), orderQuantity, marketPriceQuantity.getMarketDataId()); 146 | } 147 | else { 148 | log.debug("Looping to find filling price for stop order, market price: "+marketPriceQuantity.getPrice()+", stop price: "+stopPrice); 149 | Thread.sleep(500); 150 | counter++; 151 | } 152 | } 153 | return null; //price/ quantity not found. 154 | default: 155 | final PriceQuantity marketPriceQuantity = getMarketPriceQuantity(order); 156 | if (marketPriceQuantity.getQuantity() < orderQuantity && order.getChar(TimeInForce.FIELD) == TimeInForce.FILL_OR_KILL) 157 | return null; 158 | return new PriceQuantity(marketPriceQuantity.getPrice(), orderQuantity, marketPriceQuantity.getMarketDataId()); 159 | } 160 | } 161 | 162 | private static class PriceQuantity { 163 | private final double price; 164 | private final double quantity; 165 | private final String marketDataId; 166 | 167 | public PriceQuantity(final double price, final double quantity, final String marketDataId) { 168 | this.price = price; 169 | this.quantity = quantity; 170 | this.marketDataId = marketDataId; 171 | } 172 | 173 | public double getPrice() { 174 | return price; 175 | } 176 | 177 | public double getQuantity() { 178 | return quantity; 179 | } 180 | 181 | public String getMarketDataId() { 182 | return marketDataId; 183 | } 184 | 185 | public double getValue() { 186 | return price * quantity; 187 | } 188 | 189 | } 190 | 191 | private PriceQuantity getMarketPriceQuantity(final Message message) throws FieldNotFound { 192 | final MarketData marketData = marketDataManager.get(message.getString(Symbol.FIELD)); 193 | switch (message.getChar(Side.FIELD)) { 194 | case Side.BUY: 195 | return new PriceQuantity(marketData.getAsk(), marketData.getAskSize(), marketData.getID()); 196 | case Side.SELL: 197 | return new PriceQuantity(marketData.getBid(), marketData.getBidSize(), marketData.getID()); 198 | default: 199 | throw new RuntimeException("Invalid order side: " + message.getChar(Side.FIELD)); 200 | } 201 | } 202 | 203 | private static OrderID buildOrderID() { 204 | return new OrderID(String.valueOf(orderIdSequence.incrementAndGet())); 205 | } 206 | 207 | private static ExecID buildExecID() { 208 | return new ExecID(String.valueOf(execIdSequence.incrementAndGet())); 209 | } 210 | } -------------------------------------------------------------------------------- /TradingMachineUtility/src/main/java/com/projects/tradingMachine/utility/order/SimpleOrder.java: -------------------------------------------------------------------------------- 1 | package com.projects.tradingMachine.utility.order; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | import quickfix.SessionID; 7 | 8 | public class SimpleOrder implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | private static int nextID = 1; 12 | private SessionID sessionID = null; 13 | private String symbol = null; 14 | private int quantity = 0; 15 | private int open = 0; 16 | private int executed = 0; 17 | private OrderSide side = OrderSide.BUY; 18 | private OrderType type = OrderType.MARKET; 19 | private OrderTimeInForce timeInForce = OrderTimeInForce.DAY; 20 | private Double limitPrice = null; 21 | private Double stopPrice = null; 22 | private double avgPx = 0.0; 23 | private boolean rejected; 24 | private boolean canceled; 25 | private boolean isNew = true; 26 | private String message = null; 27 | private String ID = null; 28 | private String originalID = null; 29 | private boolean creditCheckFailed; 30 | 31 | private Date storeDate; 32 | private String marketDataID; 33 | 34 | public SimpleOrder() { 35 | ID = Long.valueOf(System.currentTimeMillis() + (nextID++)).toString(); 36 | } 37 | 38 | /** 39 | * This constructor is used when the order gets retrieved from the back-end. 40 | * */ 41 | public SimpleOrder(final String ID, final String symbol, final int quantity, final OrderSide side, final OrderType type, 42 | final OrderTimeInForce timeInForce, final Double limitPrice, final Double stopPrice, final Double price, final String originalID, 43 | final Date storeDate, final boolean rejected, final String marketDataID, final boolean creditCheckFailed) { 44 | this.ID = ID; 45 | this.symbol = symbol; 46 | this.quantity = quantity; 47 | this.side = side; 48 | this.type = type; 49 | this.timeInForce = timeInForce; 50 | this.limitPrice = limitPrice; 51 | this.stopPrice = stopPrice; 52 | this.avgPx = price; 53 | this.originalID = originalID; 54 | this.storeDate = storeDate; 55 | this.rejected = rejected; 56 | this.marketDataID = marketDataID; 57 | this.creditCheckFailed = creditCheckFailed; 58 | } 59 | 60 | public SimpleOrder(final String ID) { 61 | this.ID = ID; 62 | } 63 | 64 | public SessionID getSessionID() { 65 | return sessionID; 66 | } 67 | 68 | public void setSessionID(final SessionID sessionID) { 69 | this.sessionID = sessionID; 70 | } 71 | 72 | public String getSymbol() { 73 | return symbol; 74 | } 75 | 76 | public void setSymbol(final String symbol) { 77 | this.symbol = symbol; 78 | } 79 | 80 | public int getQuantity() { 81 | return quantity; 82 | } 83 | 84 | public void setQuantity(final int quantity) { 85 | this.quantity = quantity; 86 | } 87 | 88 | public int getOpen() { 89 | return open; 90 | } 91 | 92 | public void setOpen(final int open) { 93 | this.open = open; 94 | } 95 | 96 | public int getExecuted() { 97 | return executed; 98 | } 99 | 100 | public void setExecuted(final int executed) { 101 | this.executed = executed; 102 | } 103 | 104 | public OrderSide getSide() { 105 | return side; 106 | } 107 | 108 | public void setSide(final OrderSide side) { 109 | this.side = side; 110 | } 111 | 112 | public OrderType getType() { 113 | return type; 114 | } 115 | 116 | public void setType(final OrderType type) { 117 | this.type = type; 118 | } 119 | 120 | public OrderTimeInForce getTimeInForce() { 121 | return timeInForce; 122 | } 123 | 124 | public void setTimeInForce(final OrderTimeInForce timeInForce) { 125 | this.timeInForce = timeInForce; 126 | } 127 | 128 | public Double getLimit() { 129 | return limitPrice; 130 | } 131 | 132 | public void setLimit(final Double limit) { 133 | this.limitPrice = limit; 134 | } 135 | 136 | public void setLimit(final String limit) { 137 | if (limit == null || limit.equals("")) { 138 | this.limitPrice = null; 139 | } else { 140 | this.limitPrice = new Double(limit); 141 | } 142 | } 143 | 144 | public Double getStop() { 145 | return stopPrice; 146 | } 147 | 148 | public void setStop(final Double stop) { 149 | this.stopPrice = stop; 150 | } 151 | 152 | public void setStop(final String stop) { 153 | if (stop == null || stop.equals("")) { 154 | this.stopPrice = null; 155 | } else { 156 | this.stopPrice = new Double(stop); 157 | } 158 | } 159 | 160 | public void setAvgPx(final double avgPx) { 161 | this.avgPx = avgPx; 162 | } 163 | 164 | public double getAvgPx() { 165 | return avgPx; 166 | } 167 | 168 | public void setRejected(final boolean rejected) { 169 | this.rejected = rejected; 170 | } 171 | 172 | public boolean isRejected() { 173 | return rejected; 174 | } 175 | 176 | public void setCanceled(final boolean canceled) { 177 | this.canceled = canceled; 178 | } 179 | 180 | public boolean getCanceled() { 181 | return canceled; 182 | } 183 | 184 | public void setNew(final boolean isNew) { 185 | this.isNew = isNew; 186 | } 187 | 188 | public boolean isNew() { 189 | return isNew; 190 | } 191 | 192 | public void setMessage(final String message) { 193 | this.message = message; 194 | } 195 | 196 | public String getMessage() { 197 | return message; 198 | } 199 | 200 | public void setID(final String ID) { 201 | this.ID = ID; 202 | } 203 | 204 | public String getID() { 205 | return ID; 206 | } 207 | 208 | public void setOriginalID(final String originalID) { 209 | this.originalID = originalID; 210 | } 211 | 212 | public String getOriginalID() { 213 | return originalID; 214 | } 215 | 216 | public Date getStoreDate() { 217 | return storeDate; 218 | } 219 | 220 | public void SetStoreDate(final Date storeDate) { 221 | this.storeDate = storeDate; 222 | } 223 | 224 | public String getMarketDataID() { 225 | return marketDataID; 226 | } 227 | 228 | public void setMarketDataId(final String marketDataID) { 229 | this.marketDataID = marketDataID; 230 | } 231 | 232 | public boolean isCreditCheckFailed() { 233 | return creditCheckFailed; 234 | } 235 | 236 | public void setCreditCheckFailed(final boolean creditCheckFailed) { 237 | this.creditCheckFailed = creditCheckFailed; 238 | } 239 | 240 | @Override 241 | public int hashCode() { 242 | final int prime = 31; 243 | int result = 1; 244 | result = prime * result + ((ID == null) ? 0 : ID.hashCode()); 245 | long temp; 246 | temp = Double.doubleToLongBits(avgPx); 247 | result = prime * result + (int) (temp ^ (temp >>> 32)); 248 | result = prime * result + (canceled ? 1231 : 1237); 249 | result = prime * result + executed; 250 | result = prime * result + (isNew ? 1231 : 1237); 251 | result = prime * result + ((limitPrice == null) ? 0 : limitPrice.hashCode()); 252 | result = prime * result + ((message == null) ? 0 : message.hashCode()); 253 | result = prime * result + open; 254 | result = prime * result + ((originalID == null) ? 0 : originalID.hashCode()); 255 | result = prime * result + quantity; 256 | result = prime * result + (rejected ? 1231 : 1237); 257 | result = prime * result + ((sessionID == null) ? 0 : sessionID.hashCode()); 258 | result = prime * result + ((side == null) ? 0 : side.hashCode()); 259 | result = prime * result + ((stopPrice == null) ? 0 : stopPrice.hashCode()); 260 | result = prime * result + ((symbol == null) ? 0 : symbol.hashCode()); 261 | result = prime * result + ((timeInForce == null) ? 0 : timeInForce.hashCode()); 262 | result = prime * result + ((type == null) ? 0 : type.hashCode()); 263 | return result; 264 | } 265 | 266 | @Override 267 | public boolean equals(Object obj) { 268 | if (this == obj) 269 | return true; 270 | if (obj == null) 271 | return false; 272 | if (getClass() != obj.getClass()) 273 | return false; 274 | SimpleOrder other = (SimpleOrder) obj; 275 | if (ID == null) { 276 | if (other.ID != null) 277 | return false; 278 | } else if (!ID.equals(other.ID)) 279 | return false; 280 | if (Double.doubleToLongBits(avgPx) != Double.doubleToLongBits(other.avgPx)) 281 | return false; 282 | if (canceled != other.canceled) 283 | return false; 284 | if (executed != other.executed) 285 | return false; 286 | if (isNew != other.isNew) 287 | return false; 288 | if (limitPrice == null) { 289 | if (other.limitPrice != null) 290 | return false; 291 | } else if (!limitPrice.equals(other.limitPrice)) 292 | return false; 293 | if (message == null) { 294 | if (other.message != null) 295 | return false; 296 | } else if (!message.equals(other.message)) 297 | return false; 298 | if (open != other.open) 299 | return false; 300 | if (originalID == null) { 301 | if (other.originalID != null) 302 | return false; 303 | } else if (!originalID.equals(other.originalID)) 304 | return false; 305 | if (quantity != other.quantity) 306 | return false; 307 | if (rejected != other.rejected) 308 | return false; 309 | if (sessionID == null) { 310 | if (other.sessionID != null) 311 | return false; 312 | } else if (!sessionID.equals(other.sessionID)) 313 | return false; 314 | if (side != other.side) 315 | return false; 316 | if (stopPrice == null) { 317 | if (other.stopPrice != null) 318 | return false; 319 | } else if (!stopPrice.equals(other.stopPrice)) 320 | return false; 321 | if (symbol == null) { 322 | if (other.symbol != null) 323 | return false; 324 | } else if (!symbol.equals(other.symbol)) 325 | return false; 326 | if (timeInForce != other.timeInForce) 327 | return false; 328 | if (type != other.type) 329 | return false; 330 | return true; 331 | } 332 | 333 | @Override 334 | public String toString() { 335 | return "Order [sessionID=" + sessionID + ", symbol=" + symbol + ", quantity=" + quantity + ", open=" + open 336 | + ", executed=" + executed + ", side=" + side + ", type=" + type + ", timeInForce=" + timeInForce 337 | + ", limit=" + limitPrice + ", stop=" + stopPrice + ", avgPx=" + avgPx + ", rejected=" + rejected + ", canceled=" 338 | + canceled + ", isNew=" + isNew + ", message=" + message + ", ID=" + ID + ", originalID=" + originalID 339 | + "]"; 340 | } 341 | } --------------------------------------------------------------------------------