This example is more complex even because it handles and trace errors and exceptions
in a sophisticated way. In addition all LifeCycle activities are handled at two levels:
at the higher level all errors and common actions are handled, at the lower level
the specific actions for the specific activity is taken.
/* Description =========== This application example open two connections with a MetaMarket service of a given FastTrack server, and then: - Two subscriptions will be started on the first connection: - The first one is a non masked subscription on FT_C_ORDER EntityClass: - All received entities will be written on standard output. - All received entities matching a given operator will be written on files. This subscription is never stopped. - The second one is a partial and masked subscription on FT_C_TRADING_STATE EntityClass: - Only the ExchangeID and Phase fields are subscribed and written on standard output when received. - In addition many refreshEntity will be requested for each received entity with Phase field that matches a given phase. This is done in order to discover and print all fields of these entities. This subscription is close when all received non masked entities match the corresponding requested refreshEntity. - All files *.order and *.order.pending of a given directory are listed. Each *.order file contains all 14 mandatory fields of FT_C_ORDER EntityClass. Foreach *.order file (if there is not a corresponding *.order.pending file) a transaction is sent to the server in order to add a FT_C_ORDER. Once the transaction is sent its TransactionID is stored on a corresponding *.order.pending file and this file remains untouched until the transaction is committed or aborted. Once the transaction is sent and it's still flying the command-line option "-x" controls if the transaction must be immediately queried for its status or if this query must be postponed to the next run of this application. Foreach *.order file that is couple with a corresponding *.order.pending file a query for the pending transaction is sent to the server in order to discover if it is still pending or else it is committed or aborted. In the first case the application retry the query after a while. In the two latter cases the file *.order.pending is renamed *.order.done.blabla because the corresponding transaction is finished. There are convenient command-line options to control the delay between two subsequents queries and two subsequents scan of *.order files. The application terminates when all *.order files are been analyzed. Please note that this may happens before the two subscriptions terminate the print of their historical data: i.e. not all data may been received. */
/* Example Usage ============= To compile this example remember to put in the classpath: - The path of JDK 1.4.x (or following) - The path of your library JFTApi.jar - The path of the directory where the metamarket package reside To launch this example type: java Example2 options... where options are: [-h host] # FastTrack server TCP/IP host [-p port] # FastTrack server TCP/IP port [-n serviceName] # FastTrack service name [-o opName] # operator's name [-w opPassword] # operator's password [-l licPathName] # license file pathname [-d dirPathName] # directory with xxx.order[.pending] files [-t traceLevel] # 0<= traceLevel <= 5 [-v ON/OFF] # trace verbose [-s scanDelay] # scan delay (in seconds) [-q queryDelay] # query delay (in seconds) [-x ON/OFF] # request to make a query after a send E.g.: java Example2 -h 194.91.195.1 -p 1234 -o dario -w dario -d c:\tmp -s 5 -q 15 -x ON requests - to talk with FastTrack server on host 194.91.195.1 and port 1234. - without specifying any service name (there is no "-n ..." option). - with operator name and password both equals to dario. - not using any license file (there is no "-l ..." option). - listing the directory C:\tmp. - using the default trace level TRACE_LEVEL_FATAL=5 (there is no "-t ..." option). - using the default non verbose trace (there is no "-v ..." option). - reading a new *.order and/or *.order.pending file every 5 seconds: i.e. every 5 seconds a send (if there is not a *.order.pending file) or a query (if there is a *.order.pending file) will be sent to the server. - re-sending other query for the same transaction 15 seconds after a preceding flying (i.e. no commit and no abort) result. - requesting to automatically send a query after a send. Additional Classes (in metamarket package) ========================================== In order to profitably use this example there is need for some additional Java classes in the metamarket package. These classes are: - MetaMarket It contains global constants (entityClassIDs, keyIDs, etc...) for all data structures of FastTrack MetaMarket service. - FT_C_ORDER Specific EntityClass for orders handled by MetaMarket. You can see below a skeleton of this class. - FT_C_TRADING_STATE Specific EntityClass for the handled trading states. You can see below a skeleton of this class. - FT_C_ERROR_INFO Specific EntityClass for the transaction errors communications between the server and the application. - FT_C_TRADING_PHASE The various enumeration values for fields of FT_C_TRADING_STATE etc... */
/* MetaMarket ========== MetaMarket is the Java class that contains global constants for all data structures of FastTrack MetaMarket service. This class contains - a lot of constants (for all entytClassIDs, keyIDs, etc...) that may be used to access all data handled by MetaMarket service. - a method registerAll() that may be used to register all the EntityClasses of MetaMarket. In this example we use only 3 EntityClasses: FT_C_TRADING_STATE, FT_C_ORDER and FT_C_ERROR_INFO so the only used members of the class MetaMarket are: public static final int FT_C_TRADING_STATE_ID = 30010; // FT_C_TRADING_STATE id public static final int FT_C_TRADING_STATEKey = 1; // FT_C_TRADING_STATE prim. key public static final int FT_C_ORDER_ID = 30014; // FT_C_ORDER id public static final int FT_C_ORDERKey = 1; // FT_C_ORDER primary key public static void registerAll(); // to register all EntityClasses of MetaMarket The FT_C_TRADING_STATE EntityClass ================================== Its entityClassID is FT_C_TRADING_STATE_ID = 30010. Its primary keyID is FT_C_TRADING_STATEKey = 1 and it includes ExchangeID, MarketID and SectionID fields. Its structure is something like: class FT_C_TRADING_STATE { String ExchangeID; // ID of the market place String MarketID; // ID of the market String SectionID; // ID of the section int Phase; // Phase of the security: // 0 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_UNDEF // 1 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_CLOSURE // 2 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_PRE_ISSUE // 3 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_ISSUE // 4 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_PRE_AUCTION // 5 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_AUCTION // 6 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_POST_AUCTION // 7 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_PRE_TRADING // 8 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_TRADING // 9 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_POST_TRADING //10 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_TRADING_AT_LAST //11 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_TRADING_AFTER_HOUR //12 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_FAST_MARKET //13 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_MANAGEMENT //14 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_NO_OPERATION int Status; // Status of the section // 0 or FT_C_TRADING_STATUS.FT_C_TRADING_STATUS_Active // 1 or FT_C_TRADING_STATUS.FT_C_TRADING_STATUS_Suspended // 2 or FT_C_TRADING_STATUS.FT_C_TRADING_STATUS_Frozen String PhaseDescription; // Description of phase int Time; // Time (format: HHMMSSmmm) of last change } */
/* The FT_C_ORDER EntityClass ========================== Its entityClassID is FT_C_ORDER_ID = 30014. Its primary keyID is FT_C_ORDERKey = 1 and it includes FTSecID and OrderID fields. Its structure is something like: class FT_C_ORDER { String FTSecID; // ID of the security String OrderID; // ID of the order given by the market String OperatorID; // Operator ID String MrkOperatorID; // ID of the operator on the destination market int Verb; // Verb of the order: // 0 or FT_C_VERB.FT_C_VERB_Buy // 1 or FT_C_VERB.FT_C_VERB_Sell int OrderType; // Type of the order // 0 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Limit // 1 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Market // 2 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Market_to_limit // 3 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Stop_market // 4 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_OpeningPrice // 5 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Stop_limit // 6 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Subscription int QtyParameter; // Parameter to choose if the whole quantity of the // quantity must be matched at the same time // 0 or FT_C_QTY_PARAMETER.FT_C_QTY_PARAMETER_Default // 1 or FT_C_QTY_PARAMETER.FT_C_QTY_PARAMETER_All_or_None int TimeInForce; // Parameter to determine the life of the order // 0 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Default // 1 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Day // 2 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Good_till_Date // 3 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Good_till_Cancel // 4 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Immediate_or_Cancel // 5 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Good_till_Maturity // 6 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Good_till_Hour // 7 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Cancel_after_Filled int ValidityDate; // Date (format: AAAAMMDD) of the end of the order's validity int Status; // Status of the order // 0 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Active // 1 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_PartFilled // 2 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_CompFilled // 3 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Cancelled // 4 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Suspended // 5 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_CancelledByGov // 6 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Stopped // 7 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Submitted // 8 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Rejected // 9 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_DeletedByEdit double Price; // Limit price of the order double Qty; // Quantity of the order double StopPrice; // Price that triggers a stop order int TriggerMechanism;// Activaction rule for stop orders // 0 or FT_C_STOP_TRIGGER_MECHANISM // .FT_C_STOP_TRIGGER_MECHANISM_BestPrice // 1 or FT_C_STOP_TRIGGER_MECHANISM // .FT_C_STOP_TRIGGER_MECHANISM_LastPrice } */
/* The FT_C_ERROR_INFO EntityClass =============================== Its entityClassID is FT_C_ERROR_INFO_ID = 30050. It does not have any key. Its structure is something like: class FT_C_ERROR_INFO { int ReasonCode; String ErrorString; } The FT_C_TRADING_PHASE class ============================ This java class is not an EntityClass. This class contains - all enumeration value for the trading phase, - a method enumAsString that returns a displayable String for a given value. Its structure is something like: class FT_C_TRADING_PHASE { public static final int FT_C_TRADING_PHASE_UNDEF = 0; public static final int FT_C_TRADING_PHASE_CLOSURE = 1; public static final int FT_C_TRADING_PHASE_PRE_ISSUE = 2; public static final int FT_C_TRADING_PHASE_ISSUE = 3; public static final int FT_C_TRADING_PHASE_PRE_AUCTION = 4; public static final int FT_C_TRADING_PHASE_AUCTION = 5; public static final int FT_C_TRADING_PHASE_POST_AUCTION = 6; public static final int FT_C_TRADING_PHASE_PRE_TRADING = 7; public static final int FT_C_TRADING_PHASE_TRADING = 8; public static final int FT_C_TRADING_PHASE_POST_TRADING = 9; public static final int FT_C_TRADING_PHASE_TRADING_AT_LAST = 10; public static final int FT_C_TRADING_PHASE_TRADING_AFTER_HOUR = 11; public static final int FT_C_TRADING_PHASE_FAST_MARKET = 12; public static final int FT_C_TRADING_PHASE_MANAGEMENT = 13; public static final int FT_C_TRADING_PHASE_NO_OPERATION = 14; public static final String enumAsString(int value) { return "an appropriate value"; } } */
// Effective source-code Example2 starts here. import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.FileReader; import java.io.File; import it.list.jft.*; // to use the JFT/Api library import it.list.jft.event.*; // to use the JFT/Api library import metamarket.*; // to use MetaMarket, FT_C_ORDER, FT_C_TRADING_STATE, etc... class Example2 extends UtilityExample implements Runnable { static final String O_SUFFIX = ".order"; static final String O_SUFFIX_READ = O_SUFFIX + ".read"; static final String T_SUFFIX = ".pending"; static final String T_SUFFIX_DONE = ".done." + System.currentTimeMillis(); static final String EXCHANGE_ID = "HDAT"; static final int SUBS_CLIENT_ID = 67890; static final int TRANS_CLIENT_ID= 67891; static final int PHASE_TRADING = FT_C_TRADING_PHASE. FT_C_TRADING_PHASE_TRADING; static final ThreadGroup THREAD_GROUP = new ThreadGroupExample(); static String licPath = null; static String dirPath = "c:\\"; static String myOperatorID = "dario"; static String myOperatorPass = "*"; static String host = "metamarket.fasttrack.com"; static String service = null; static int port = 1234; static int delayScanSecs = 10; static int delayQuerySecs = 3; static int traceLevel = JFT.TRACE_LEVEL_FATAL; static boolean queryAfterSend = false; static boolean verbose = false; static Context context = null;
public static void main(String[]args) { handleArgs(args); new Thread(THREAD_GROUP, new Example2(), "main").start(); } public void run() { JFT.THIS.init(JFT.MODE_MULTI_THREAD); JFT.THIS.setTrace(true); JFT.THIS.setTraceLevel(traceLevel); JFT.THIS.setTraceMode(this); if(true) { JFT.THIS.register(new FT_C_ORDER()); JFT.THIS.register(new FT_C_TRADING_STATE()); JFT.THIS.register(new FT_C_ERROR_INFO()); } else // as an expensive alternative MetaMarket.registerAll(); JFT.THIS.start(); context = JFT.THIS.makeContext(); new ConnectionForSubscriptions(); new ConnectionForTransactions(); } // Just a remind on usage... static void usage() { final String[]usage= {"Usage: java Example options...", "options: [-h host] # FastTrack server TCP/IP host", " [-p port] # FastTrack server TCP/IP port", " [-n serviceName] # FastTrack service name", " [-o opName] # operator's name", " [-w opPassword] # operator's password", " [-l licPathName] # license file pathname", " [-d dirPathName] # directory with xxx.order[.trans] files", " [-t traceLevel] # 0 <= traceLevel <= 5", " [-v ON/OFF] # trace verbose", " [-s scanDelay] # scan delay (in seconds)", " [-q queryDelay] # query delay (in seconds)", " [-x ON/OFF] # request to make a query after a send"}; for(int i=0; i<usage.length; i++) System.out.println(usage[i]); System.exit(0); }
// Parse command-line options. static void handleArgs(String[]args) { try { for(int i=0; i<args.length; i+=2) if(args[i].equals("-h")) host = args[i+1]; else if(args[i].equals("-p")) port = Integer.parseInt(args[i+1]); else if(args[i].equals("-n")) service = args[i+1]; else if(args[i].equals("-o")) myOperatorID = args[i+1]; else if(args[i].equals("-w")) myOperatorPass = args[i+1]; else if(args[i].equals("-l")) licPath = args[i+1]; else if(args[i].equals("-t")) traceLevel = Integer.parseInt(args[i+1]); else if(args[i].equals("-d")) dirPath = args[i+1]; else if(args[i].equals("-v")) verbose = args[i+1].compareToIgnoreCase("ON") == 0; else if(args[i].equals("-s")) delayScanSecs = Integer.parseInt(args[i+1]); else if(args[i].equals("-q")) delayQuerySecs = Integer.parseInt(args[i+1]); else if(args[i].equals("-x")) queryAfterSend = args[i+1].compareToIgnoreCase("ON") == 0; else throw new IllegalArgumentException(); if( args.length == 0 || host.length() == 0 || port <= 0 || service != null && service.length() == 0 || myOperatorID.length() == 0 || myOperatorPass.length() == 0 || traceLevel < JFT.TRACE_LEVEL_DEBUG || traceLevel > JFT.TRACE_LEVEL_FATAL || licPath != null && ! new File(licPath).canRead() || ! new File(dirPath).isDirectory() || delayScanSecs < 0 || delayQuerySecs < 0) throw new IllegalArgumentException(); } catch(Exception e) { usage(); } } }
// Common superclass for all connections. abstract class ConnectionExample extends UtilityExample implements ConnectionListener { final Connection connection; ConnectionExample(int connectionUserType, int clientID) { ConnectionParam cp = Example2.context.makeConnectionParam(); cp.setHost(Example2.host); cp.setPort(Example2.port); cp.setService(Example2.service); cp.setApplRevision(new int[]{0,0,0}); cp.setApplSignature(12345); cp.setAuthFile(Example2.licPath == null ? null : new File(Example2.licPath)); cp.setClientID(clientID); cp.setConnType(ConnectionParam.CONN_TYPE_TCP); cp.setUserName(Example2.myOperatorID); cp.setPassword(Example2.myOperatorPass); cp.setUserType(connectionUserType); connection = Example2.context.makeConnection(cp, this); int res = connection.open(); trace(res); if(res != Connection.RESULT_OK) connection.release(); // good practice } public void onConnectionOpen(ConnectionOpenEvent ev) { int res = ev.getResult(); trace(res); if(res == ev.RESULT_OK) { int[]mrkRev = ev.getMarketRevision(); trace("csID: " + ev.getClientServiceID() + " bsID: " + ev.getBusinessServiceID() + " date: " + UtilityExample.sdf.format(ev.getSystemDateTime()) + " FTID: " + ev.getFTID() + " env: " + ev.getEnvironment() + " mrkRev: " + mrkRev[0] + "." + mrkRev[1] + "." + mrkRev[2]); } else connection.release(); // good practice } public void onConnectionClose(ConnectionCloseEvent ev) { trace(ev.getResult()); connection.release(); // good practice } public void onConnectionLost(ConnectionLostEvent ev) { trace(ev.getResult()); connection.release(); // good practice } }
// A specific connection: it handles many subscriptions. class ConnectionForSubscriptions extends ConnectionExample { ConnectionForSubscriptions() { super(ConnectionParam.USER_TYPE_VIEW, Example2.SUBS_CLIENT_ID); } public void onConnectionOpen(ConnectionOpenEvent ev) { super.onConnectionOpen(ev); // call the overridden method if(ev.getResult() == ev.RESULT_OK) { new SubscriptionOrder(connection); new SubscriptionTradingState(connection); } } }
// Common superclass for all subscriptions. abstract class SubscriptionExample extends UtilityExample implements SubscriptionListener { final Subscription subscription; SubscriptionExample(Connection connection) { subscription = Example2.context.makeSubscription(connection, makeSubscriptionParam(), this); int res = subscription.start(); trace(res); if(res != Subscription.RESULT_OK) subscription.release(); // good practice } abstract SubscriptionParam makeSubscriptionParam(); abstract String entityAsString(Entity e); public void onSubscriptionStart(SubscriptionStartEvent ev){ trace("Result=" + ev.getResult() + (ev.getResult() == ev.RESULT_OK ? " version: " + ev.getEntityClassVersionOnServer() + " reset: " + ev.isEntityClassReset() : "")); if(ev.getResult() != ev.RESULT_OK) subscription.release(); // good practice } public void onSubscriptionIdle(SubscriptionIdleEvent ev){ trace(ev.getResult()); } public void onSubscriptionNotify(SubscriptionNotifyEvent ev){ switch(ev.getAction()) { case SubscriptionNotifyEvent.ACTION_ENTITY_ADD: case SubscriptionNotifyEvent.ACTION_ENTITY_RWT: trace((ev.getAction() == ev.ACTION_ENTITY_ADD ? "ADD" : "RWT") + " Masked: " + ev.isMasked() + " " + entityAsString(ev.getEntity())); break; case SubscriptionNotifyEvent.ACTION_ENTITY_DEL: trace("DEL KeyID: " + ev.getKeyID()); break; case SubscriptionNotifyEvent.ACTION_ENTITY_KIL: if(ev.getKeyID() <= 0) trace("KIL ClassReset - New Version: " + ev.getTimeStamp().getDateTime()); else trace("KIL KeyID: " + ev.getKeyID()); break; } } public void onSubscriptionStop(SubscriptionStopEvent ev){ trace(ev.getResult()); subscription.release(); // good practice } }
// A specific subscription: it handles FT_C_ORDER. class SubscriptionOrder extends SubscriptionExample { SubscriptionOrder(Connection connection) { super(connection); } SubscriptionParam makeSubscriptionParam() { SubscriptionParam sp = Example2.context.makeSubscriptionParam(); sp.setEntityClassID(MetaMarket.FT_C_ORDER_ID); return sp; } String entityAsString(Entity e) { FT_C_ORDER o = (FT_C_ORDER) e; return (o.OperatorID.equals(Example2.myOperatorID) ? "" : "NO_OWNER") + " OrderID: " + o.OrderID + " FTSecID: " + o.FTSecID + " OperatorID: " + o.OperatorID + " Price: " + o.Price + " Qty: " + o.Qty + " Verb: " + FT_C_VERB.enumAsString(o.Verb) + " ValidityDate: " + o.ValidityDate; } public void onSubscriptionNotify(SubscriptionNotifyEvent ev){ super.onSubscriptionNotify(ev); // call the overridden method FT_C_ORDER o = (FT_C_ORDER) ev.getEntity(); if(! o.OperatorID.equals(Example2.myOperatorID)) writeOrder(o); } void writeOrder(FT_C_ORDER o) { PrintWriter pw = null; try { pw = new PrintWriter(new FileOutputStream(new File( Example2.dirPath, o.FTSecID + Example2.O_SUFFIX_READ))); pw.println(o.FTSecID); pw.println(o.OrderID); pw.println(o.OperatorID); pw.println(o.MrkOperatorID); pw.println(o.Verb); pw.println(o.OrderType); pw.println(o.QtyParameter); pw.println(o.TimeInForce); pw.println(o.ValidityDate); pw.println(o.Status); pw.println(o.Price); pw.println(o.Qty); pw.println(o.StopPrice); pw.println(o.TriggerMechanism); trace("order file written."); } catch(Exception e) { trace(e); } finally { try {pw.close();} catch(Exception e) {} } } }
// Another specific subscription: it handles FT_C_TRADING_STATE. class SubscriptionTradingState extends SubscriptionExample { int counter; // count the # of refreshEntity requested SubscriptionTradingState(Connection connection) { super(connection); } SubscriptionParam makeSubscriptionParam() { Mask m = JFT.THIS.makeEmptyMask(MetaMarket.FT_C_TRADING_STATE_ID); m.addFieldByName("ExchangeID"); // primary key field m.addFieldByName("MarketID"); // primary key field m.addFieldByName("SectionID"); // primary key field m.addFieldByName("Phase"); // what I'm searching! SubscriptionParam sp = Example2.context.makeSubscriptionParam(); sp.setEntityClassID(MetaMarket.FT_C_TRADING_STATE_ID); sp.setMask(m); sp.setQueryType(sp.QUERY_TYPE_SET); FT_C_TRADING_STATE ts = new FT_C_TRADING_STATE(); ts.ExchangeID = Example2.EXCHANGE_ID; sp.setEntityKey(ts.getPartialEntityKey(MetaMarket.FT_C_TRADING_STATEKey, 1)); return sp; } String entityAsString(Entity e) { FT_C_TRADING_STATE ts = (FT_C_TRADING_STATE) e; return "ExchangeID: " + ts.ExchangeID // in mask and partial key subscribed + " MarketID: " + ts.MarketID // in mask + " SectionID: " + ts.SectionID // in mask + " Phase: " + FT_C_TRADING_PHASE.enumAsString(ts.Phase)// in mask + " Status: " + FT_C_TRADING_STATUS.enumAsString(ts.Status) + " PhaseDesc: " + ts.PhaseDescription + " Time: " + ts.Time; } public void onSubscriptionIdle(SubscriptionIdleEvent ev){ super.onSubscriptionIdle(ev); // call the overridden method checkStop(); } public void onSubscriptionNotify(SubscriptionNotifyEvent ev){ super.onSubscriptionNotify(ev); // call the overridden method if(ev.isMasked()) { FT_C_TRADING_STATE ts = (FT_C_TRADING_STATE) ev.getEntity(); if(ts.Phase == Example2.PHASE_TRADING) { int res = subscription.refreshEntity(ts.getFullEntityKey(ev.getKeyID())); trace(res); if(res == subscription.RESULT_OK) counter++; } } else { counter--; checkStop(); } }
void checkStop() { if(counter <= 0) { // Now I'm no more interested in this subscription int res = subscription.stop(); trace(res); subscription.release(); // good practice } } } // Another specific connection: it handles many transactions. class ConnectionForTransactions extends ConnectionExample implements FilenameFilter, Runnable{ String[]files; ConnectionForTransactions() { super(ConnectionParam.USER_TYPE_TRADER, Example2.TRANS_CLIENT_ID); } public void onConnectionOpen(ConnectionOpenEvent ev) { super.onConnectionOpen(ev); // call the overridden method if(ev.getResult() == ev.RESULT_OK) { files = new File(Example2.dirPath).list(this); int n = (files == null) ? 0 : files.length; trace(n + " orders to be sent or monitored"); if(n > 1) Arrays.sort(files); new Thread(Example2.THREAD_GROUP, this, "listing").start(); } } public boolean accept(File dir, String name) { return name.endsWith(Example2.O_SUFFIX) && ! name.startsWith(Example2.O_SUFFIX); } public void run() { sleep(Example2.delayScanSecs); if(files != null) for(int i=0; i<files.length; i++) try { String filename = files[i] + Example2.T_SUFFIX; File f = new File(Example2.dirPath, filename); if(f.canRead()) { trace("analyzing file " + filename + ": pending trans. to be monitored"); new TransactionPending(connection, files[i]); } else { trace("analyzing file " + files[i] + ": new transaction to be created"); new TransactionNew(connection, files[i]); } sleep(Example2.delayScanSecs); } catch(Exception e) { trace(i + " -> " + e); } JFT.THIS.release(); trace("JFT library released -> application going to die"); // no need to explicitly call System.exit() here ! } }
// Common superclass for all transactions. abstract class TransactionExample extends UtilityExample implements TransactionListener, Runnable { final Transaction transaction; final String filename; int counter; TransactionExample(Connection connection, String f) { super(f); filename = f; transaction = Example2.context.makeTransaction(connection, makeTransactionParam(), this); tidWrite(); } abstract TransactionParam makeTransactionParam(); String reasonAsString(TransactionEvent ev) { int reason = ev.getReasonCode(); FT_C_ERROR_INFO ei = (FT_C_ERROR_INFO) ev.getEntity(); return reason == 0 ? "" : (" reason: " + reason + (ei == null ? "" : " -> " + ei.ErrorString)); } void onTransaction(TransactionEvent ev, String whoami) { int st = transaction.getStatus(); trace(whoami + ev.getResult() + (st == transaction.STATUS_FLYING ? " FLYING" : (st == transaction.STATUS_ABORTED ? " ABORTED" : (st == transaction.STATUS_COMMITTED ? " COMMITTED" : " " + st))) + reasonAsString(ev)); if(st == transaction.STATUS_FLYING) if(Example2.queryAfterSend) queryDelayed(); else transaction.release(); // Now I'm no more interested in this transaction else destroy(); } public void onTransactionSend(TransactionSendEvent ev) { onTransaction(ev, "onTransactionSend -> "); } public void onTransactionQuery(TransactionQueryEvent ev) { onTransaction(ev, "onTransactionQuery -> "); } void destroy() { tidRemove(); transaction.release(); // Now I'm no more interested in this transaction } void query() { int res = transaction.query(); trace(res); if(res != transaction.RESULT_OK) destroy(); }
void queryDelayed() { new Thread(Example2.THREAD_GROUP, this, "query " + (++counter) + " on " + filename).start(); } TransactionID tidRead() { BufferedReader in = null; try { in = new BufferedReader(new FileReader(new File(Example2.dirPath, filename + Example2.T_SUFFIX))); TransactionID tid = JFT.THIS.makeTransactionID( Integer.parseInt(in.readLine()), Integer.parseInt(in.readLine()), Integer.parseInt(in.readLine()), JFT.THIS.makeTimeStamp(Integer.parseInt(in.readLine()), Integer.parseInt(in.readLine()))); trace("transaction file read."); return tid; } catch(Exception e) { trace(e); return null; } finally { try {in.close();} catch(Exception e) {} } } void tidWrite() { TransactionID tid = transaction.getTransactionID(); PrintWriter pw = null; try { pw = new PrintWriter(new FileOutputStream(new File(Example2.dirPath, filename+Example2.T_SUFFIX))); pw.println(tid.getClientID()); pw.println(tid.getClientServiceID()); pw.println(tid.getBusinessServiceID()); pw.println(tid.getTimeStamp().getDateTime()); pw.println(tid.getTimeStamp().getProg()); trace("transaction file written."); } catch(Exception e) { trace(e); } finally { try {pw.close();} catch(Exception e) {} } } void tidRemove() { File oldF = new File(Example2.dirPath, filename + Example2.T_SUFFIX); File newF = new File(Example2.dirPath, filename + Example2.T_SUFFIX_DONE); boolean ok = oldF.renameTo(newF); trace("transaction file renamed: " + ok); } public void run(){ try { sleep(Example2.delayQuerySecs); query(); } catch(Exception e) { trace(e); } } }
class TransactionNew extends TransactionExample { // specific new transaction TransactionNew(Connection connection, String f) { super(connection, f); int res = transaction.send(); trace(res); if(res != transaction.RESULT_OK) destroy(); } TransactionParam makeTransactionParam() { TransactionParam tp = Example2.context.makeTransactionParam(); tp.setAction(TransactionParam.ACTION_ENTITY_ADD); tp.setEntity(readOrder()); tp.setKeyID(MetaMarket.FT_C_ORDERKey); return tp; } FT_C_ORDER readOrder() { BufferedReader in = null; try { FT_C_ORDER order = new FT_C_ORDER(); in = new BufferedReader(new FileReader(new File(Example2.dirPath, filename))); order.FTSecID = in.readLine(); order.OrderID = in.readLine(); order.OperatorID = in.readLine(); // overwritten below ! order.MrkOperatorID = in.readLine(); order.Verb = Integer.parseInt(in.readLine()); order.OrderType = Integer.parseInt(in.readLine()); order.QtyParameter = Integer.parseInt(in.readLine()); order.TimeInForce = Integer.parseInt(in.readLine()); order.ValidityDate = Integer.parseInt(in.readLine()); order.Status = Integer.parseInt(in.readLine()); order.Price = Double.parseDouble(in.readLine()); order.Qty = Double.parseDouble(in.readLine()); order.StopPrice = Double.parseDouble(in.readLine()); order.TriggerMechanism = Integer.parseInt(in.readLine()); order.OperatorID = Example2.myOperatorID; // overwritten with the right value ! trace("file read"); return order; } catch(Exception e) { trace(e); return null; } finally { try {in.close();} catch(Exception e) {} } } } class TransactionPending extends TransactionExample { // specific past pending transact. TransactionPending(Connection connection, String f) { super(connection, f); query(); } TransactionParam makeTransactionParam() { TransactionParam tp = Example2.context.makeTransactionParam(); tp.setPendingTransactionID(tidRead()); return tp; } }
// Common superclass for all classes of this example. abstract class UtilityExample implements Tracer { static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); final String specificName; UtilityExample() { specificName = ""; } UtilityExample(String name) { specificName = "<" + name + ">"; } void internalTrace(String line) { String m = getClass().getName() + specificName; Throwable t = new Throwable(); StackTraceElement[] ste = t.getStackTrace(); if(ste != null && ste.length >= 3) { m += "." + ste[2].getMethodName(); if(Example2.verbose) { String fn = ste[2].getFileName(); int ln = ste[2].getLineNumber(); m += "(" + (fn == null ? "" : fn) + (ln < 0 ? "" : ":" + ln) + ")"; } } JFT.THIS.trace(m, JFT.TRACE_LEVEL_FATAL, line); // In this simple example the level is always TRACE_LEVEL_FATAL // so we will see our application trace // whichever "-t ..." command-line option was choosen. } void trace(String message) { internalTrace(message); } void trace(int res) { internalTrace("Result=" + res); } void trace(Exception e) { internalTrace("Exception: " + e); } public void onTrace(Date t, String m, int l, String ms) { String v = Example2.verbose ? sdf.format(t) + " " + l + " [" + Thread.currentThread().getName() + "] " : ""; System.out.println(v + "[" + m + "] " + ms); } void sleep(int intervalSecs) { try { Thread.sleep(intervalSecs * 1000L); } catch(InterruptedException ie) { trace(ie); } } }
// Specific ThreadGroup to handle in an uniform way all generated exceptions. class ThreadGroupExample extends ThreadGroup { ThreadGroupExample() { super("Example"); } public void uncaughtException(Thread t, Throwable e) { if(e instanceof ThreadDeath) super.uncaughtException(t, e); // call the overridden method else { System.out.println("Uncaught Exception in thread " + t.getName()); e.printStackTrace(System.out); System.exit(0); } } }