<?php
/* Functions specific to the MT4Analysis project.
   Copyright (C) 2009-2010, Future Lab.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, get a copy via the Internet at
   http://gnu.org/copyleft/gpl.html or write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307 USA

   You can contact the author via email at rsmereka@future-lab.com

   Original version Apr/2009, Rick Smereka

   Added function roi and tablerowtag. Aug/2010, Rick Smereka */

// report all errors except notices
error_reporting (E_ALL ^ E_NOTICE);

function puts($mes)
{
   /* Put a message using 'echo' and also write a carriage return
      at the end of the message. */

   echo $mes . "\n";
}

function tableputd($dat, $align = 'L')
{
   /* Output a 'HTML' table data tag and value. Output 'nbsp' if
      the data ('dat') is empty. The parameter 'align' can be used
      to align the data in the table cell. Default alignment is
      left. */

   $celdat = (trim($dat) == '') ? '&nbsp;' : $dat;

   switch(strtoupper($align[0]))
      {
      case 'L':
         echo "<TD ALIGN='LEFT'>$celdat</TD>\n";
         break;

      case 'R':
         echo "<TD ALIGN='RIGHT'>$celdat</TD>\n";
         break;

      case 'C':
         echo "<TD ALIGN='CENTER'>$celdat</TD>\n";
         break;

      default:
         die("tableputd:unknown alignment '$align'");
      }
}

function mt4a_dbname()
{
   /* Build the trade database name based on whether we are running
      from http or the command line (php cli). Returns the full
      database name which should be used in the 'new PDO'
      statement. */
   $mname = 'mt4a_dbname';

   /* if run from http, session var will be used but session var
      contains only the database file name, not the path
      (this applies to Sqlite only) */
   if (!iscli())
      {
      /* on my web hoster, I have to use 'DOCUMENT_ROOT_OLD' to get the
         full path (eg: /mnt/w0306/d35/s29/b024ca87) to the root
         directory (not to the 'www' directory), I never store the
         database files in a 'www' directory, check your 'phpinfo()'
         environment section */
      $spath = (IN_PRODUCTION) ? $_SERVER['DOCUMENT_ROOT_OLD'] : 
                                 $_SERVER['DOCUMENT_ROOT'];
      $dpath = (IN_PRODUCTION) ? TRADE_PATH_PRODUCTION : TRADE_PATH_LOCAL;
 
      if (isset($_SESSION['dname']) && strlen($_SESSION['dname']))
         $d = $spath . $dpath . '/' . $_SESSION['dname'];
      else
         die("$mname:database name not set(http session)");
      }
   else
      {
      /* if run from command line (php cli), global will be used which must
         be set to the full path and file name by the caller */
      global $g_dbname;

      if (isset($g_dbname) && strlen($g_dbname))
         $d = $g_dbname;
      else
         die("$mname:database name not set(cli global)");
      }

   if ($d == '')
      die("$mname:database name not set");

   return($d);
}

function iscli()
{
   /* Determine whether Php is running from cli.
      Adapted from: http://www.codediesel.com/php/quick-way-to-determine-if-php-is-running-at-the-command-line
   */
   return((php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR'])) ?
           true : false);
}

function price_precision($ticket)
{
   /* Determine the number of digits of precison (how many digits after
      the decimal point) that the currency pair price should have. Returns 
      the precision upon success, zero otherwise. */
   global $dbh, $dbf;
   $mname = 'price_precison';
   $fivedecimals = '';

   if (!iscli())
      {
      if (isset($_SESSION['fiveDecimals']) && 
          strlen($_SESSION['fiveDecimals']))
         $fivedecimals = $_SESSION['fiveDecimals'];
      else
         die("$mname:five decimal indicator not set(http session)");
      }
   else
      {
      global $g_fivedecimals;

      if (isset($g_fivedecimals) && strlen($g_fivedecimals))
         $fivedecimals = $g_fivedecimals;
      else
         die("$mname:five decimal indicator not set(cli global)");
      }

   if ($fivedecimals == '')
      die("$mname:five decimal indicator not set");

   $query = "SELECT currencyPair from closedtrade WHERE ticket = $ticket";

   try
      {
      if (!$stmt = $dbh->prepare($query))
         return(0);

      if ($stmt->execute())
         $row = $stmt->fetch(PDO::FETCH_ASSOC);
      else
         return(0);
      }
   catch(PDOException $e)
      {
      db_error($e->getMessage(), $dbf);
      die();
      }

   if (!isset($row['currencyPair']) || $row['currencyPair'] == '')
      return(0);

   $digits = ($fivedecimals) ? 5 : 4;
   $query = 'SELECT description FROM currencyPair WHERE id = ';
   $query .= $row['currencyPair'];
   $cp_desc = db_get_one($dbh, $query, 'description');

   if ($cp_desc == '')
      return(0);

   // if one of the currencies in the pair is 'jpy', subtract two digits
   if (stristr($cp_desc, 'JPY') != false)
      $digits -= 2;

   return($digits);
}

function adjust_price_precision($ticket, &$openprice, &$closeprice)
{
   /* Verify and adjust if necessary, the number of digits of precison
      in the open and close price. Function returns 'true' upon
      success with the adjusted prices in 'openprice' and 'closeprice'.
      Function returns 'false' otherwise. Trade open price is expected
      in 'openprice' and trade close price is expected in 'closeprice'
      upon entry. */
   // first get the number of digits of precison that the price should have
   $digits = price_precision($ticket);
   $tmp = strval($openprice);

   if (words($tmp, '.') < 2)
      return(false);

   if (!word($tmp, $tmp1, 1, '.'))
      return(false);

   if (!word($tmp, $tmp2, 2, '.'))
      return(false);

   if (strlen($tmp2) < $digits)
      $openprice = floatval($tmp1 . '.' . rpad($tmp2, '0', $digits));

   $tmp = strval($closeprice);

   if (words($tmp, '.') < 2)
      return(false);

   if (!word($tmp, $tmp1, 1, '.'))
      return(false);

   if (!word($tmp, $tmp2, 2, '.'))
      return(false);

   if (strlen($tmp2) < $digits)
      $closeprice = floatval($tmp1 . '.' . rpad($tmp2, '0', $digits));

   // puts("app:open=$openprice,close=$closeprice<BR />");
   return(true);
}

function abort_page($emsg, $sbnew_page = false)
{
   /* Abort any further processing on a page and issue an error message.
      The flag 'dbnew_page' indicates whether a new page should be shown
      for the error message. If a new page should be displayed, the HTML
      output headers are sent, the error message is displayed and the
      standard footer is appended. I considered using the 'headers_sent' 
      php function but it may yield negative when the headers have been 
      sent but not output due to output buffering. If a new page should not 
      be displayed, the error message ('emsg') is displayed and the standard 
      page footer is displayed. */
   if ($sbnew_page)
      {
      puts('<HTML>');
      puts('<HEAD>');
      puts('<TITLE>Fatal Page Error</TITLE>');
      puts('</HEAD>');
      puts('<BODY>');
      }

   puts('</P><H1>Fatal Page Error</H1>');
   p($emsg);
   require '../misc/footer.php';
   puts('</BODY>');
   puts('</HTML>');
   exit;
}

function p($str)
{
   // Output a string enclosed in the HTML 'p' tag.
   puts("<P>$str</P>");
}      

function br($str)
{
   // Output a string followed by the HTML break ('<BR />') tag.
   puts("$str<BR />");
}

function db_mode($sbnew_page = false)
{
   /* Check database for read only mode and display an error message
      if database changes are not permitted. */
   if (!DATABASE_RO)
      return;

   $emsg = 'Database changes are not permitted. Database is open in ';
   $emsg .= 'read only mode.';
   abort_page($emsg, $sbnew_page);
}

function tablerowtag()
{
   /* Output a table row header (HTML 'TR' tag) using alternate
      colours. */

   static $rowcolourswitch = 0;

   // first row, background white
   $rowcolours[0] = '#ffffff';

   // second row, background light green
   $rowcolours[1] = '#caffc4';

   echo "<TR STYLE='background: {$rowcolours[$rowcolourswitch]};'>\n";
   $rowcolourswitch = 1 - $rowcolourswitch;
}

function page_header($title)
{
   /* Display the page title, the last updated database date/time
      (if available) and also a message regarding the database
      is in read-only mode if that is the case. */
   // print_r($_SESSION);
   puts("<H1>Mt4Analysis - $title</H1>");

   if (isset($_SESSION['dname']) && trim($_SESSION['dname']) != '' &&
       $_SESSION['dname'] != DD_NULL)
      {
      $database_driver = DATABASE_DRIVER;
      $db = mt4a_dbname();

      // open database
      try
         {
         $dbh = new PDO("$database_driver:$db");
         }
      catch(PDOException $e)
         {
         db_error($e->getMessage(), $db);
         die();
         }

      $query = "SELECT datetime(value, 'localtime') as t ";
      $query .= "FROM parms WHERE name = '" . DB_UPDATE_DATIME . "'";
      $datime = db_get_one($dbh, $query, 't');
      $tmp = strtotime($datime);
      $datime = date('F d Y H:i:s T', $tmp);
      p("Database <B>{$_SESSION['dname']}</B> as at $datime");
      }
   
   if (DATABASE_RO)
      {
      $mes = 'Database is open in read-only mode. ';
      $mes .= 'The saving of changes is not permitted.';
      p("<B>$mes</B>");
      }
}

function roi($sop, $filter)
{
   /* Calculate the return on investment (roi) for a group of trades. The
      trade group is 'select'ed by the 'filter' (must be a valid SQL
      statement) using the start of period date ('sop'). The roi will be
      calculated by totaling the profit/loss of all the trades selected
      by the period and then finding the last closed trade of the period
      date and calculating the balance at the start of period. The roi
      is the total profit/loss divided by the start of period balance.
      The start of period date must be an acceptable date and time
      (yyyy-mm-dd hh:mm:ss subset of ISO-8601 as specified in
      http://www.sqlite.org/lang_datefunc.html).
      The roi is returned upon success, zero otherwise. */
   global $dbh, $dbf;

   // get account balance ticket as at start of period (local server time)
   $query = 'SELECT max(ticket) AS t FROM closedtrade WHERE ';
   $query .= '(transactiontype = ' . MT4_TRANS_TYPE_BUY . ' OR ';
   $query .= 'transactiontype = ' . MT4_TRANS_TYPE_SELL . ') AND ';
   $query .= "datetime(closedatime, 'localtime') <= ";
   $query .= "'" . $sop . "'";

   if (($ticket = db_get_one($dbh, $query, 't')) == '')
      return(0);

   // puts("roi:ticket=$ticket");

   // get account balnce based on ticket
   $balance = account_balance($ticket);
   // puts("roi:balance=$balance");

   // total the profit/loss based on 'filter'
   $query = 'SELECT total(pl + rollover - commission) AS t FROM closedtrade ';
   $query .= 'WHERE ' . $filter;
   
   if (($pl = db_get_one($dbh, $query, 't')) == '')
      return(0);

   // puts("roi:pl=$pl");
   return(round($pl / $balance * 100, 2));
}

function tbl2($dat, $pos = 'L', $new = false)
{
   /* Place data into an HTML table, two up (two headings and two data).
      'dat' contains the data to display and 'pos' is the position in
      the table cell ('L' is left [default]). Acceptable 'pos' is 'L'
      (left), 'C' (center), 'R' (right). See function 'tableputd'. 
      The flag 'new' is used to indicate the start of a new HTML
      table. */
   static $col = 0;

   if ($new)
      $col = 0;

   $col++;

   if ($col > 4)
      $col = 1;

   switch($col)
      {
      case 1:
         // column 1
         tablerowtag();
         tableputd("<B>$dat</B>");
         break;

      case 2:
         // column 2
         tableputd($dat, $pos);
         break;

      case 3:
         // column 3
         tableputd("<B>$dat</B>");
         break;

      case 4:
         // column 4:
         tableputd($dat, $pos);
         puts('</TR>');
         break;
      }
}
?>
