#include "../shared/LogFile.h" #include "../shared/TclUtil.h" #include "../shared/CurrentRequest.h" #include "LimitAlert.h" #include "ServerSigner.h" #include "ScriptDispatcher.h" #include "TclLimitAlertWrapper.h" /* Here's some test code. This requests a lot of limit alerts. Enter this at the TCL command prompt using the TCL shell app. The results will be displayed the the TCL shell's main window. Notice how we loop using an integer, and divide it by 100 each time. The original code started at 53.50 and added 0.01 each time. This caused roundoff errors. Old: set symbol MSFT foreach direction {0 1} direction_name {below above} { foreach form_t {0 1} form_t_name {normal FormT} { for {set p 5350} {$p <= 5450} {incr p} { set price [expr {$p / 100.0}] ti::create_limit_alert_old $symbol $price $direction $form_t [list ti::reply_to_client [set [ti::get_socket_temp_ns]::tcl_shell::channel] [list $symbol is now at or $direction_name $price $form_t_name]] } } } New: set symbol MSFT foreach direction {long short} direction_name {below above} { foreach form_t {0 1} form_t_name {normal FormT} { for {set p 5100} {$p <= 5200} {incr p} { foreach no_socket {0 1} no_socket_name {normal daemon} { set price [expr {$p / 100.0}] set input [dict create \ price $price \ symbol $symbol \ direction_name $direction_name \ form_t_name $form_t_name \ no_socket_name $no_socket_name] set options [dict create \ include_form_t $form_t \ no_socket $no_socket \ input $input] ti::create_limit_alert $symbol $price $direction $options { ti::log_add {limit alert test} $::ti::limit_alert_input } } } } } Fake data: ti::debug_tos {symbol ORCL price 53.99 form_t 1 updates_last 0 new_print 1 valid 1 set 1} */ ///////////////////////////////////////////////////////////////////// // TclLimitAlertRequest ///////////////////////////////////////////////////////////////////// class TclLimitAlertRequest : public LimitAlertRequest { private: const std::string _script; // TODO it would be nice if we stored the script as a TCL object. However, // we can't always do that. It would be good if we did that when we sent // from the dispatcher thread, but we stored it as a string when sending // from a different thread. public: TclLimitAlertRequest(std::string const &symbol, double price, bool isLong, bool includeFormT, std::string const &script) : LimitAlertRequest(CurrentRequest::getSocketInfo(), symbol, price, isLong, includeFormT), _script(script) { } TclLimitAlertRequest(SocketInfo *socket, std::string const &symbol, double price, bool isLong, bool includeFormT, std::string const &script) : LimitAlertRequest(socket, symbol, price, isLong, includeFormT), _script(script) { } virtual void onFired() { /* TclList msg; msg<runScript(getSocketInfo(), _script); } delete this; } }; /* The following was replaced by ServerSigner class LimitAlertCancelReference : public HasNamedReference { public: const uint64_t id; LimitAlertCancelReference(uint64_t id) : id(id) { } virtual std::string getTypeName() const { return "Limit Alert Cancel"; } }; */ ///////////////////////////////////////////////////////////////////// // installLimitAlertCommands() ///////////////////////////////////////////////////////////////////// static ServerSigner serverSigner; static int cancelLimitAlertCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "id"); return TCL_ERROR; } std::string idString = getString(objv[1]); uint64_t id = serverSigner.decodeInt(idString); if (!id) { Tcl_SetObjResult(interp, makeTclString("Invalid id: " + idString)); return TCL_ERROR; } LimitAlertManager::primary->cancel(id); return TCL_OK; } static int createLimitAlertOldCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 6) { Tcl_WrongNumArgs(interp, 1, objv, "symbol price is_long include_form_t script"); return TCL_ERROR; } std::string symbol = getString(objv[1]); double price; if (Tcl_GetDoubleFromObj(interp, objv[2], &price) != TCL_OK) return TCL_ERROR; int isLong; if (Tcl_GetBooleanFromObj(interp, objv[3], &isLong) != TCL_OK) return TCL_ERROR; int includeFormT; if (Tcl_GetBooleanFromObj(interp, objv[4], &includeFormT) != TCL_OK) return TCL_ERROR; std::string script = getString(objv[5]); LimitAlertRequest *request = new TclLimitAlertRequest(symbol, price, isLong, includeFormT, script); const std::string result = serverSigner.encodeInt(request->getId()); LimitAlertManager::primary->submit(request); Tcl_SetObjResult(interp, makeTclString(result)); return TCL_OK; } static int createLimitAlertCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 6) { Tcl_WrongNumArgs(interp, 1, objv, "symbol price direction options script"); return TCL_ERROR; } std::string symbol = getString(objv[1]); double price; if (Tcl_GetDoubleFromObj(interp, objv[2], &price) != TCL_OK) return TCL_ERROR; static char const *directions[] = { "short", "long", NULL }; int isLong; if (Tcl_GetIndexFromObj(interp, objv[3], directions, "direction", 0, &isLong) != TCL_OK) return TCL_ERROR; // Options. Walk through the list so we don't miss any. An unknown option // should be an error. If we only looked up the options we knew, we couldn't // detect these unknown objects. TclObjHolder includeFormTObj; TclObjHolder noSocketObj; TclObjHolder inputObj; Tcl_DictSearch search; Tcl_Obj *key, *value; int done; if (Tcl_DictObjFirst(interp, objv[4], &search, &key, &value, &done) != TCL_OK) return TCL_ERROR; for (; !done ; Tcl_DictObjNext(&search, &key, &value, &done)) { static char const *options[] = { "include_form_t", "no_socket", "input", NULL }; int optionIndex; if (Tcl_GetIndexFromObj(interp, key, options, "option", 0, &optionIndex) != TCL_OK) { Tcl_DictObjDone(&search); return TCL_ERROR; } switch (optionIndex) { case 0: includeFormTObj = value; break; case 1: noSocketObj = value; break; case 2: inputObj = value; break; default: assert(0); } } Tcl_DictObjDone(&search); int includeFormT; if (!includeFormTObj) includeFormT = true; // Default else if (Tcl_GetBooleanFromObj(interp, includeFormTObj.getObj(), &includeFormT) != TCL_OK) return TCL_ERROR; int noSocket; if (!noSocketObj) noSocket = false; // Default else if (Tcl_GetBooleanFromObj(interp, noSocketObj.getObj(), &noSocket) != TCL_OK) return TCL_ERROR; SocketInfo *const socket = noSocket?NULL:CurrentRequest::getSocketInfo(); std::string script = "unset -nocomplain ::ti::limit_alert_input; "; if (inputObj) script += "set ::ti::limit_alert_input " + tclQuote(getString(inputObj.getObj())) + "; "; script += getString(objv[5]); LimitAlertRequest *request = new TclLimitAlertRequest(socket, symbol, price, isLong, includeFormT, script); const std::string result = serverSigner.encodeInt(request->getId()); LimitAlertManager::primary->submit(request); Tcl_SetObjResult(interp, makeTclString(result)); return TCL_OK; } void installLimitAlertCommands(Tcl_Interp *interp) { Tcl_CreateObjCommand(interp, "ti::create_limit_alert", createLimitAlertCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::create_limit_alert_old", createLimitAlertOldCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::cancel_limit_alert", cancelLimitAlertCmd, NULL, NULL); }