BeginTransaction(); if ($status) { $quote = GetQuoteForUpdate($order->symbol, $db); 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, $db); if ($holdingID != INVALID_ID) { $totalPrice = $order->quantity * $order->price + $order->orderFee; } } if ($order->orderType == ORDER_TYPE_SELL) { $holdingID = SellHolding($order, $db); if ($holdingID != INVALID_ID) { $totalPrice = -1 * $order->quantity * $order->price + $order->orderFee; } } if ($holdingID != INVALID_ID) { $status = UpdateSystemStatus($order, $quote, $holdingID, $totalPrice, $db); } 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.*/ $db->CommitTransaction(); } else { /*Transaction is not successfull, we can safely rollback the transaction without commiting to the database.*/ $db->RollbackTransaction(); } $db->CloseDatabase(); } /** * 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, $db) { $query = "SELECT symbol, companyname, volume, price, ". "open1, low, high, change1 FROM quote WHERE symbol ". "= '$symbol'"; /*Get the tuple corresponding to the particular symbol*/ $result = $db->ExecuteQuery($query); if ($result) { $quote = new Quote(); $quote->symbol = $db->GetSQLValue($result, 0, 0); //Get the symbol. $quote->price = $db->GetSQLValue($result, 0, 3); //Get the price. $quote->low = $db->GetSQLValue($result, 0, 5); //Get the low value. $quote->high = $db->GetSQLValue($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, $db) { $status = STATUS_SUCCESS; $accountID = GetAccountIDFromOrder($order, $db); if ($accountID != INVALID_ID) { if(UpdateAccountBalance($accountID, $totalPrice, $db)) { if(UpdateStockPriceVolume($order->quantity, $quote, $db)) { if(!CloseOrder($order, $holdingID, $db)) { 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, $db) { $holding = GetHoldingForUpdate($order, $db); if ($holding) { $accountID = $holding->accountID; $quantity = $order->quantity; $holdingQuantity = $holding->quantity; if ($order->quantity < $holding->quantity) { if(!UpdateHolding($holding, $order->quantity, $db)) { error_log ("Cannot update holding with holding id ".$holding->holdingID. " \n"); $holding->holdingID = INVALID_ID; } } else if ($order->quantity == $holding->quantity) { if(!DeleteHolding($holding, $db)) { error_log ("Cannot delete holding with holding id ".$holding->holdingID. " \n"); $holding->holdingID = INVALID_ID; } } else { if(!DeleteHolding($holding, $db)) { error_log ("Cannot delete holding with holding id ".$holding->holdingID. " \n"); $holding->holdingID = INVALID_ID; } else { $order->quantity = $holding->quantity; if(!UpdateOrder($order, $db)) { 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, $db) { $query = "UPDATE orders SET quantity='$order->quantity' WHERE". " orderid='$order->orderID'"; return $db->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, $db) { $query = "UPDATE holding SET quantity=quantity-'$quantity'". " WHERE holdingid='$holding->holdingID'"; return $db->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, $db) { $query = "DELETE FROM holding WHERE ". "holdingid='$holding->holdingID'"; return $db->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, $db) { $query = "SELECT holding.holdingid, holding.account_accountid,". " holding.quantity, holding.purchaseprice, holding.purchasedate,". " holding.quote_symbol FROM holding 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 = $db->ExecuteQuery($query); if ($result) { $holding = new Holding(); $holding->holdingID = $db->GetSQLValue($result, 0, 0); //Get the holdingID. $holding->accountID = $db->GetSQLValue($result, 0, 1); //Get the accountID. $holding->quantity = $db->GetSQLValue($result, 0, 2); //Get the quantity. $holding->purchasePrice = $db->GetSQLValue($result, 0, 3); //Get the price. $holding->purchaseDate = $db->GetSQLValue($result, 0, 4); //Get the date. $holding->quoteSymbol = $db->GetSQLValue($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, $db) { $order->orderStatus = ORDER_STATUS_CLOSED; if ($order->orderType == ORDER_TYPE_SELL) { $holdingID = "NULL"; } $query = "UPDATE orders SET ". "orderstatus='".ORDER_STATUS_CLOSED."',". " completiondate=CURRENT_TIMESTAMP, holding_holdingid=$holdingID,". " price='$order->price' WHERE orderid='$order->orderID'"; return $db->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, $db) { $accountID = GetAccountIDFromOrder($order, $db); if ($accountID != INVALID_ID) { $query = "INSERT INTO holding (purchaseprice, quantity, purchasedate,". " account_accountid, quote_symbol) VALUES ('$order->price',". " '$order->quantity', CURRENT_TIMESTAMP, '$accountID', '$order->symbol')"; $result = $db->ExecuteQuery($query); $holdingID = $db->GetInsertID(); if ($holdingID == -1) { 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, $db) { $query = "SELECT account_accountid FROM orders ". " WHERE orderid='$order->orderID'"; /*Get a tuple including accountID for a particular order*/ $result = $db->ExecuteQuery($query); if ($result != NULL) { $accountID = $db->GetSQLValue($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, $db) { $query = "UPDATE account SET balance=(balance - '$amount')". " WHERE accountid = '$accountID'"; return $db->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, $db) { 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 SET price='$quote->price', ". "low='$quote->low', high='$quote->high', change1='$quote->price' - ". "open1, volume=volume+'$quantity' WHERE symbol='$quote->symbol'"; $status = $db->ExecuteQuery($query); } return $status; } ?>