symbol); if ($quote) { $order->price = $quote->price; /*Buy or sell orders either first creates a holding or sell a holding. The return value is the holdingID upon success. If NULL is returned, then the tranaction can not be completed. This is due to either problem of accessing the database or if there is no maching holding.*/ /*upon success, total price should be deducted from account's balance. In case of sell, balance should increase. So, in sell case, total price will be negative value */ if ($order->orderType == ORDER_TYPE_BUY) { $holdingID = CreateHolding($order); if ($holdingID != INVALID_ID) { $totalPrice = $order->quantity * $order->price + $order->orderFee; } } if ($order->orderType == ORDER_TYPE_SELL) { $holdingID = SellHolding($order); if ($holdingID != INVALID_ID) { $totalPrice = -1 * $order->quantity * $order->price + $order->orderFee; } } if ($holdingID != INVALID_ID) { $status = UpdateSystemStatus($order, $quote, $holdingID, $totalPrice); } else { error_log ("Holding id for order with order id ".$order->orderID. " is not valid\n"); $status = STATUS_FAILURE; } } else { error_log ("Cannot get quote with symbol ".$order->symbol. "\n"); $status = STATUS_FAILURE; } } if ($status == STATUS_SUCCESS) { /*Transaction is successfull, we can safely commit the transaction into the database.*/ ExecuteQuery("COMMIT TRAN"); } else { /*Transaction is not successfull, we can safely rollback the transaction without commiting to the database.*/ ExecuteQuery("ROLLBACK TRAN"); } CloseDatabase($dbhandle); } /** * This method, retrieves all the information related to a particular symbol * from the QUOTE table. * @param symbol is the symbol we are looking for * @return quote object filled with symbol information upons success and else * NULL is returned. */ function GetQuoteForUpdate($symbol) { $query = "Set NOCOUNT ON; SELECT SYMBOL, COMPANYNAME, VOLUME, PRICE, ". "OPEN1, LOW, HIGH, CHANGE1 FROM QUOTE WITH (NOLOCK) WHERE SYMBOL ". "= '$symbol'"; /*Get the tuple corresponding to the particular symbol*/ $result = ExecuteQuery($query); if ($result) { $quote = new Quote(); $quote->symbol = GetMSSQLValue($result, 0, 0); //Get the symbol. $quote->price = GetMSSQLValue($result, 0, 3); //Get the price. $quote->low = GetMSSQLValue($result, 0, 5); //Get the low value. $quote->high = GetMSSQLValue($result, 0, 6); //Get the high value. } return $quote; } /** * This method updates all the system information relates to a particular * buy or sell operation. * @param order order object * @param quote quote object * @param holdingID holdingID of the holding which relates to the current order * @param totalPrice the price change (amount of money the flows in/out of a * user's account). * @return STATUS_SUCCESS upon success and STATUS_FAILURE otherwise. */ function UpdateSystemStatus($order, $quote, $holdingID, $totalPrice) { $status = STATUS_SUCCESS; $accountID = GetAccountIDFromOrder($order); if ($accountID != INVALID_ID) { if(UpdateAccountBalance($accountID, $totalPrice)) { if(UpdateStockPriceVolume($order->quantity, $quote)) { if(!CloseOrder($order, $holdingID)) { error_log ("Cannot close order for order id ". $order->orderID. " \n"); $status = STATUS_FAILURE; } } else { error_log ("Cannot update stock price volume for symbol ". $quote->symbol. "\n"); $status = STATUS_FAILURE; } } else { error_log ("Cannot update account balace for account id ".$accountID. "\n"); $status = STATUS_FAILURE; } } else { error_log ("Account id for order id " .$order->orderID. " is not valid \n"); $status = STATUS_FAILURE; } return $status; } /** * This function corresponds to a sell operation. It matches a particular * holding related to a particular order (symbol) and then do the transaction * @param order order object * @return a non NULL holdingID upon success and NULL otherwise. */ function SellHolding($order) { $holding = GetHoldingForUpdate($order); if ($holding) { $accountID = $holding->accountID; $quantity = $order->quantity; $holdingQuantity = $holding->quantity; if ($order->quantity < $holding->quantity) { if(!UpdateHolding($holding, $order->quantity)) { error_log ("Cannot update holding with holding id ".$holding->holdingID. " \n"); $holding->holdingID = INVALID_ID; } } else if ($order->quantity == $holding->quantity) { if(!DeleteHolding($holding)) { error_log ("Cannot delete holding with holding id ".$holding->holdingID. " \n"); $holding->holdingID = INVALID_ID; } } else { if(!DeleteHolding($holding)) { error_log ("Cannot delete holding with holding id ".$holding->holdingID. " \n"); $holding->holdingID = INVALID_ID; } else { $order->quantity = $holding->quantity; if(!UpdateOrder($order)) { error_log ("Cannot update order with order id ".$order->orderID. " \n"); $holding->holdingID = INVALID_ID; } } } } else { error_log ("Holding for order id ".$order->orderID. " is not valid \n"); return INVALID_ID; } return $holding->holdingID; } /** * This method updates the status of an order as a result of a buy or sell * operation. * @param order order object * @return NON-NULL on success and NULL on failure. */ function UpdateOrder($order) { $query = "UPDATE ORDERS WITH (ROWLOCK) SET QUANTITY='$order->quantity' WHERE". " ORDERID='$order->orderID'"; return ExecuteQuery($query); } /** * This method updates a particular Holing entry in the HOLDING table. * @param holding is the holding object. * @param quantity is the amount of buy or sell. * $return NON-NULL on success or NULL otherwise. */ function UpdateHolding($holding, $quantity) { $query = "UPDATE HOLDING WITH (ROWLOCK) SET QUANTITY=QUANTITY-'$quantity'". " WHERE HOLDINGID='$holding->holdingID'"; return ExecuteQuery($query); } /** * Removes an entry from the HOLDING table when a matching order is received. * @param holding is a holding object. * @return NON-NULL value on success and NULL otherwise. */ function DeleteHolding($holding) { $query = "DELETE FROM HOLDING WITH (ROWLOCK) WHERE ". "HOLDINGID='$holding->holdingID'"; return ExecuteQuery($query); } /** * This method returns a quote object which matches to the particular sell * order. * @param order the order object. * @return a Holding object upon success and NULL otherwise. */ function GetHoldingForUpdate($order) { $query = "Set NOCOUNT ON; SELECT HOLDING.HOLDINGID, HOLDING.ACCOUNT_ACCOUNTID,". " HOLDING.QUANTITY, HOLDING.PURCHASEPRICE, HOLDING.PURCHASEDATE,". " HOLDING.QUOTE_SYMBOL FROM HOLDING WITH (ROWLOCK) INNER JOIN ORDERS". " ON HOLDING.HOLDINGID = ORDERS.HOLDING_HOLDINGID WHERE ". "(ORDERS.ORDERID = '$order->orderID')"; /*Get the machining tuple from HOLDING table, that corresponds to the current sell operation.*/ $result = ExecuteQuery($query); if ($result) { $holding = new Holding(); $holding->holdingID = GetMSSQLValue($result, 0, 0); //Get the holdingID. $holding->accountID = GetMSSQLValue($result, 0, 1); //Get the accountID. $holding->quantity = GetMSSQLValue($result, 0, 2); //Get the quantity. $holding->purchasePrice = GetMSSQLValue($result, 0, 3); //Get the price. $holding->purchaseDate = GetMSSQLValue($result, 0, 4); //Get the date. $holding->quoteSymbol = GetMSSQLValue($result, 0, 5); //Get the symbol. } else { error_log ("Cannot obtain holding for order id ". $order->orderID . "\n"); } return $holding; } /** * This method updates the order status, with newest settings on completion * of processing an order. * @param order order object. * @param holdingID holdingID of the particular holding. * @return NON-NULL on success and NULL on failure. */ function CloseOrder($order, $holdingID) { $order->orderStatus = ORDER_STATUS_CLOSED; if ($order->orderType == ORDER_TYPE_SELL) { $holdingID = NULL; } $query = "UPDATE ORDERS WITH (ROWLOCK) SET ". "ORDERSTATUS='".ORDER_STATUS_CLOSED."',". " COMPLETIONDATE=GetDate(), HOLDING_HOLDINGID='$holdingID',". " PRICE='$order->price' WHERE ORDERID='$order->orderID'"; return ExecuteQuery($query); } /** * Create an entry in the HOLDING table to represent a particular buy order. * @param order order object filled with order information. * @return the holdingID of the created holding upon success and else it * returns NULL */ function CreateHolding($order) { $accountID = GetAccountIDFromOrder($order); if ($accountID != INVALID_ID) { $query = "INSERT INTO HOLDING (PURCHASEPRICE, QUANTITY, PURCHASEDATE,". " ACCOUNT_ACCOUNTID, QUOTE_SYMBOL) VALUES ('$order->price',". " '$order->quantity', GetDate(), '$accountID', '$order->symbol');". " SELECT ID=@@IDENTITY"; $result = ExecuteQuery($query); if ($result) { $holdingID = GetMSSQLValue($result, 0, 0); } else { error_log ("Cannot create holding for order id ". $order->orderID . "\n"); $holdingID = INVALID_ID; } } else { error_log ("Account id for order with order id ".$order->orderID. " is not valid\n"); $holdingID = INVALID_ID; } return $holdingID; } /** * This method retrieves the acccountID from a given order. * @param order the order object. * @return NON-NULL accountID upon success and NULL otherwise. */ function GetAccountIDFromOrder($order) { $query = "Set NOCOUNT ON; SELECT ACCOUNT_ACCOUNTID FROM ORDERS WITH ". "(NOLOCK) WHERE ORDERID='$order->orderID'"; /*Get a tuple including accountID for a particular order*/ $result = ExecuteQuery($query); if ($result != NULL) { $accountID = GetMSSQLValue($result, 0, 0); //Get accountID. } else { error_log ("Cannot obtain account id for order with order id ".$order->orderID. "\n"); $accountID = INVALID_ID; } return $accountID; } /** * This method updates the account balance of the user who buy or sell some * sybmol. * @param accountID is the account to be updated. * @param amount the amount of money by which the account is updated. * @return NON-NULL upon success and NULL on failure. */ function UpdateAccountBalance($accountID, $amount) { $query = "UPDATE ACCOUNT WITH (ROWLOCK) SET BALANCE=(BALANCE - '$amount')". " WHERE ACCOUNTID = '$accountID'"; return ExecuteQuery($query); } /** * This method updates the QUOTE table with the new price values. In here, a * random value is generated which is between 0.1 and 2, and then the quote * price is changed by multiplying it with the generted random value. * @param quantity the quantity of a particular symbol the client buy or sell. * @return STATUS_SUCCESS upon success and STATUS_FAILURE upon failure. */ function UpdateStockPriceVolume($quantity, $quote) { if ($quote) { $rand = rand(1, 20); $priceChangeFactor = ((float)$rand)/10; $quote->price = $quote->price * $priceChangeFactor; if($quote->price < 0.05 || $quote->price > 1000) { $quote->price = 100; } if ($quote->price < $quote->low) { $quote->low = $quote->price; } if ($quote->price > $quote->high) { $quote->high = $quote->price; } $query = "UPDATE QUOTE WITH (ROWLOCK) SET PRICE='$quote->price', ". "LOW='$quote->low', HIGH='$quote->high', CHANGE1='$quote->price' - ". "OPEN1, VOLUME=VOLUME+'$quantity' WHERE SYMBOL='$quote->symbol'"; $status = ExecuteQuery($query); } return $status; } ?>