stockapi-8.x-2.x-dev/stockapi.inc

stockapi.inc
<?php

/**
 * @file
 * A wrapper API for the retreiving stock quotes from Alpha Vantage Realtime and historical equity data.
 */

use Drupal\Core\Url;

define("stockapi_symbol", "0");
define("stockapi_trade_price_only", "1");
define("stockapi_chg", "2");
define("stockapi_pct_chg", "3");
define("stockapi_updated", "4");
define("stockapi_previous_close", "5");
define("stockapi_days_range", "6");
define("stockapi_52_week_range", "7");
define("stockapi_open", "8");
define("stockapi_volume", "9");
define("stockapi_avg_daily_volume", "10");
define("stockapi_market_cap", "11");
define("stockapi_trade_date", "12"); //string
define("stockapi_last_trade_date", "13"); //string
define("stockapi_pe_ratio", "14");
define("stockapi_float_shares", "15");
define("stockapi_chg_and_pct_chg", "16");
define("stockapi_last_trade_time", "17");
define("stockapi_exchange", "18");
define("stockapi_name", "19");
define("stockapi_vol_avg_for_postprocessing", "999");

/**
 * Implement a HTTP request to Alpha Vantage Realtime and historical equity data to retrieve stock quotes.
 *
 * @param $symbols array
 *   An array of ticker symbols to send to Alpha Vantage. There is not limit on the
 *   the request into batches.
 *
 * @return
 *   An array of stocks with stock information from Alpha Vantage. False on failure.
 */
function stockapi_fetch($symbols) {
  // Step 1: Split up larger arrays.
  $stock_lookup_failure = array();
  $mkt = '';
  $stock_results = array();
  $l = 199;
  if (count($symbols) <= $l) {
    $stock_array = $symbols;

    foreach($stock_array as $stock_symbol) {
      $stock_test = '';
      $stock_name = '';
      $result = db_query("SELECT exchange FROM {stockapi} WHERE symbol = :symbol", array(':symbol' => $stock_symbol))->fetchField();
      $resultName = db_query("SELECT name FROM {stockapi} WHERE symbol = :symbol", array(':symbol' => $stock_symbol))->fetchField();
      if (empty($result)) {
        $stock_test = 'TSE';
      }
      else {
        $stock_test = $result;
      }
      if (empty($resultName)) {
        $stock_name = $stock_test . ':' . $stock_symbol;
      }
      else {
        $stock_name = $resultName;
      }
      switch ($stock_test) {
        case 'NYSE':
          $mkt = 'NYSE';
        break;
        case 'TSE':
          $mkt = 'TSE';
        break;
        case 'Toronto Stock Exchange':
          $mkt = 'TSE';
        break;
        case 'New York Stock Exchange':
          $mkt = 'NYSE';
        break;
        case 'NASDAQ':
          $mkt = 'NASDAQ';
        break;
        case '':
          $mkt = 'TSE';
        break;
        case NULL:
          $mkt = 'TSE';
        break;
        default:
          $mkt = 'TSE';
        break;
      }

      if (\Drupal::config('stockapi.settings')->get('stockapi_vantage_apikey') == 'demo' ||
          \Drupal::config('stockapi.settings')->get('stockapi_vantage_apikey') == '') {
        \Drupal::configFactory()->getEditable('stockapi.settings')->set('stockapi_vantage_apikey', 'demo')->save();
        //$stock_symbol = 'AAPL'; //demo api key only works for a few demo stocks like AAPL or MSFT.
        $mkt = 'NYSE';
        $warning_key = t('Using demo key, please register for a free api key' .
         ' at https://www.alphavantage.co/support/#api-key then set the key ' .
         ' at /admin/config/services/stockapi and press save.');
        drupal_set_message($warning_key, $type = 'warning', $repeat = FALSE);
      }
      $sym = $stock_symbol;
      $series = 'TIME_SERIES_INTRADAY';
      $series_m = 'TIME_SERIES_MONTHLY';
      $series_d = 'TIME_SERIES_DAILY';
      $urld = stockapi_alpha_vantage_json_url($mkt, $sym, $series_d, NULL);
      $interval = '15min';
      $url = stockapi_alpha_vantage_json_url($mkt, $sym, $series, $interval);
      // Load JSON.
      $json_intraday = _file_get_contents($url);
      $intraday_array = json_decode($json_intraday, TRUE);
      $daily_array = array();
      $json_daily = NULL;
      if (isset($intraday_array['Information'])) {
        $info = $intraday_array['Information'];
        \Drupal::logger('stockapi_info')->warning('To use this api provider you must login and' .
           ' register for an api key at ' .
           'https://www.alphavantage.co/support/#api-key <pre>@print_r</pre>', array('@print_r' => print_r($intraday_array['Information'], TRUE)
          ));
      }
      if (isset($intraday_array['Error Message'])) {
        // Load JSON.
        try {
          $json_daily = _file_get_contents($urld);
          $daily_array = json_decode($json_daily, TRUE);
          if (isset($daily_array['Error Message'])) {
            \Drupal::logger('stockapi_info')->notice('<pre>@print_debug - @print_url - @print_r</pre>', array('@print_r' =>
                print_r($daily_array['Error Message'], TRUE),
                '@print_debug' => $mkt . ' : ' . $sym . ' series: ' . $series_d,
                '@print_url' => $urld,
              ));
            $json_daily = NULL; //Use this like a flag.
            throw new Exception("no stock data for sym: $sym for mkt: $mkt in" .
            " $series_d " . $daily_array['Error Message']);
          }
          //grab daily
        } catch (Exception $e) {
          if ($mkt == 'NYSE') {
            $mkt = 'TSE';
          } else {
            $mkt = 'NYSE';
          }
          \Drupal::logger('stockapi_info')->warning('<pre>@print_debug - @print_url - @print_r</pre>', array('@print_r' =>
              print_r($intraday_array['Error Message'], TRUE),
              '@print_debug' => $mkt . ' : ' . $sym . ' series: ' . $series,
              '@print_url' => $url,
            ));
          $url = stockapi_alpha_vantage_json_url($mkt, $sym, $series, $interval);
          $json_intraday = _file_get_contents($url);
          $intraday_array = json_decode($json_intraday, TRUE);
          if (isset($intraday_array['Error Message'])) {
            $mkt = 'NASDAQ';
            $url = stockapi_alpha_vantage_json_url($mkt, $sym, $series, $interval);
            $json_intraday = _file_get_contents($url);
            $intraday_array = json_decode($json_intraday, TRUE);
            if (isset($intraday_array['Error Message'])) {
              \Drupal::logger('stockapi_info')->warning('<pre>@print_debug - @print_url - @print_r</pre>', array('@print_r' =>
                  print_r($intraday_array['Error Message'], TRUE),
                  '@print_debug' => $mkt . ' : ' . $sym . ' series: ' . $series,
                  '@print_url' => $url,
                ));
              break; //bail on this
            }
          }
        }
      }
      $urlm = stockapi_alpha_vantage_json_url($mkt, $sym, $series_m, NULL);
      $json_monthly = _file_get_contents($urlm);
      $monthly_array = json_decode($json_monthly, TRUE);
      if (isset($monthly_array['Error Message'])) {
        \Drupal::logger('stockapi_info')->warning('<pre>@print_debug - @print_url - @print_r</pre>', array('@print_r' =>
            print_r($monthly_array['Error Message'], TRUE),
            '@print_debug' => $mkt . ' : ' . $sym . ' series: ' . $series_m,
            '@print_url' => $urlm,
          ));
        break; //bail on this
      }
      $m_key = 'Monthly Time Series';
      $m_key_adj = 'Monthly Adjusted Time Series';
      if (!isset($monthly_array[$m_key])) {
        $m_key = $m_key_adj;
        if (!isset($monthly_array[$m_key])) {
          \Drupal::logger('stockapi_info')->warning('<pre>@print_debug - @print_url - @print_r</pre>', array('@print_r' =>
              print_r('Undefined index: Monthly Adjusted Time Series: monthly_array=' . $monthly_array, TRUE),
              '@print_debug' => $mkt . ' : ' . $sym . ' series: ' . $series_m,
              '@print_url' => $urlm,
            ));
          continue;
        }
      }
      $m_count = 0;
      $m_total = count($monthly_array[$m_key]);
      $m_avg_vol = 0;
      $m_previous_close = 0;
      $m_52_low = 0;
      $m_52_high = 0;
      $m_cum_vol = 0;
      $m_series_array = $monthly_array[$m_key];
      if (isset($m_series_array)) {
        foreach($m_series_array as $key => $monthly_trading) {
          $m_seg_vol = $monthly_trading['5. volume'];
          if ($m_cum_vol == 0) {
            $m_cum_vol = round($m_seg_vol);
          } else {
            $m_cum_vol = $m_cum_vol + round($m_seg_vol);
          }
          $m_seg_high = $monthly_trading['2. high'];
          $m_seg_low = $monthly_trading['3. low'];
          if ($m_count == 0) {
            // Premier.
            $m_cum_vol = 0;
            $m_seg_high = 0;
            $m_seg_low = 0;
            // Skip the first one, because it likely an incomplete month.
            // except close, grab the latest close. (first)
            $m_previous_close = $monthly_trading['4. close'];
          }
          //if ($m_count == $m_total -1) {
          //  // Dernier.
          //}
          if ($m_seg_low <= $m_52_low) {
            $m_52_low = $m_seg_low;
          } else if ($m_52_low == 0) {
            $m_52_low = $m_seg_low;
          }
          if ($m_seg_high >= $m_52_high) {
            $m_52_high = $m_seg_high;
          }

          $m_count++;
          // Do 13 instead of 12, we skip the first month.
          if ($m_count == 13) {
            // 1 year.
            break;
          }
        }
      }
      // The NYSE and NASDAQ average about 252 trading days a year.
      $m_avg_vol = round(floatval($m_cum_vol/252));

    /*watchdog('stockapi_info', '<pre>@print_debug - @print_url - @print_r</pre>',
        array('@print_r' =>
          print_r('test', TRUE),
          '@print_debug' => $m_count . ' : ' . $sym . " m_cum_vol=$m_cum_vol m_avg_vol=$m_avg_vol m_seg_vol=$m_seg_vol series",
          '@print_url' => $urlm,
        ), WATCHDOG_WARNING
      );*/
//      return FALSE;
      /*
       * JSON looks like this:
       *
      Array
      (
        [Meta Data] => Array
            (
                [1. Information] => Intraday (15min) prices and volumes
                [2. Symbol] => TSE:XYZ
                [3. Last Refreshed] => 2018-03-23 16:00:00
                [4. Interval] => 15min
                [5. Output Size] => Compact
                [6. Time Zone] => US/Eastern
            )

        [Time Series (15min)] => Array
            (
                [2018-03-23 16:00:00] => Array
                    (
                        [1. open] => 1.3000
                        [2. high] => 1.3000
                        [3. low] => 1.3000
                        [4. close] => 1.3000
                        [5. volume] => 17400
                    )

                [2018-03-23 15:55:00] => Array
                    (
                        [1. open] => 1.3100
                        [2. high] => 1.3100
      */
      $count5s = 0; //Count for the foreach loop 5 min series.
      $countDs = 0; //Count for the foreach loop 5 min series.
      $totalNum5s = 0;
      if (isset($intraday_array['Time Series (15min)'])) {
        $totalNum5s = count($intraday_array['Time Series (15min)']);
      }
      $last_updated = 0;
      $last_trade_date = 0;
      $last_trade_time = 0;
      $last_refreshed = 0;
      $localtime_assoc = 0;
      $timezone_correct = TRUE;
      try {
        date_default_timezone_set('EST');
      } catch (Exception $e) {
        $timezone_correct = FALSE;
        \Drupal::logger('stockapi_info')->warning('<pre>@print_r</pre>', array('@print_r' =>
          print_r(t('date_default_timezone_set(\'EST\') failed.'), TRUE)
          ));
      }
      if (isset($intraday_array['Meta Data']['3. Last Refreshed'])) {
        $last_refreshed = $intraday_array['Meta Data']['3. Last Refreshed'];
        $last_refreshed = strtotime($last_refreshed);
      } else {
        $last_refreshed = time();
      }
      if ($timezone_correct) {
        try {
          $localtime_assoc = localtime($last_refreshed, true);
          $min = $localtime_assoc['tm_min'];
          if (strlen($min) == 1) {
            $min = '0' . $min;
          }
          $last_trade_time = $localtime_assoc['tm_hour'] . 'h' . $min;
        } catch (Exception $e) {
          $last_trade_time = date('H\hi', $last_refreshed);
        }
      } else {
        $last_trade_time = date('H\hi', $last_refreshed);
      }
      $trade_date = date('M j', $last_refreshed);
      //Initialize more variables
      $dernier = FALSE;
      $date_of_trade = 0;
      $todays_open = 0;
      $current_date_of_trade = 0;
      $previous_day_of_trade = 0;
      $latest_price = 0;
      $totalNumDs = 0;
      $latest_segment_volume = 0;
      $todays_volume = 0;
      $todays_low = 0;
      $todays_high = 0;
      $segment_low = 0;
      $segment_high = 0;
      $previous_close = 0;
      $pct_chg = 0;
      $todays_chg = 0;
      $flag_for_previous = TRUE;
      if (!empty($json_daily)) {
        if (!isset($daily_array['Time Series (Daily)'])) {
          \Drupal::logger('stockapi_info')->warning('<pre>Time Series (Daily) - @print_debug - @print_url - json response: @print_json_daily</pre>', array('@print_debug' =>  $sym,
                    '@print_url' => $url,
                    '@print_json_daily' => $json_daily,
            ));
          //continue to next stock_symbol
          try {
            \Drupal::logger('stockapi_info')->notice('Sleep 20 seconds processing symbol:<pre>@print_sym</pre>', array('@print_sym' =>  $sym));
            sleep(20); //wait 20 seconds, try again.
            $json_daily = _file_get_contents($urld);
            $daily_array = json_decode($json_daily, TRUE);
            if (isset($daily_array['Error Message'])) {
              \Drupal::logger('stockapi_info')->notice('<pre>@print_debug - @print_url - @print_r</pre>', array('@print_r' =>
                  print_r($daily_array['Error Message'], TRUE),
                  '@print_debug' => $mkt . ' : ' . $sym . ' series: ' . $series_d,
                  '@print_url' => $urld,
                ));
              $json_daily = NULL; //Use this like a flag.
              throw new Exception("no stock data for sym: $sym for mkt: $mkt in" .
              " $series_d " . $daily_array['Error Message']);
            }
            if (!isset($daily_array['Time Series (Daily)'])) {
              throw new Exception("no stock data for sym: $sym for mkt: $mkt in" .
              " $series_d " . $daily_array['Error Message']);
            }
          } catch (Exception $e) {
            \Drupal::logger('stockapi_info')->notice('<pre>@print_debug</pre>', array('@print_debug' =>  $e->getMessage()));
            continue;
         }
          //bust out.
          //break;
        }
        $totalNumDs = count($daily_array['Time Series (Daily)']);
        foreach($daily_array['Time Series (Daily)'] as $key => $daily_summary) {
          $day_volume = $daily_summary['5. volume'];
          $date_of_trade = substr($key, 0, strpos($key, ' '));
          if ($countDs == 0) {
            $current_date_of_trade = $date_of_trade;
            $todays_open = $daily_summary['1. open'];
            $todays_high = $daily_summary['2. high'];
            $todays_low = $daily_summary['3. low'];
            $last_updated = strtotime($current_date_of_trade);
            $latest_price = $daily_summary['4. close'];
            $todays_volume = $daily_summary['5. volume'];
            // premier.
          } else if ($countDs == $totalNumDs -1) {
            // dernier.
            $dernier = TRUE;
          }
          if ($todays_open == 0 && $daily_summary['1. open'] != 0 ) {
            $todays_open = $daily_summary['1. open'];
          }
          if ($todays_high == 0 && $daily_summary['2. high'] != 0 ) {
            $todays_high = $daily_summary['2. high'];
          }
          if ($todays_low == 0 && $daily_summary['3. low'] != 0 ) {
            $todays_low = $daily_summary['3. low'];
          }
          if ($latest_price == 0 && $daily_summary['4. close'] != 0 ) {
            $latest_price = $daily_summary['4. close'];
          }
          if ($todays_volume == 0 && $daily_summary['5. volume'] != 0 ) {
            $todays_volume = $daily_summary['5. volume'];
          }
          if ($current_date_of_trade == $date_of_trade) {
            // clean up later
            $test = NULL;
          } else {
            if ($flag_for_previous) {
              $flag_for_previous = FALSE;
              $previous_close = $daily_summary['4. close'];
            }
          }
          /* Debug.
          if ($dernier) {
            watchdog('stockapi_info', '<pre>@print_debug - @print_url - @print_r</pre>',
              array('@print_r' =>
                print_r('test', TRUE),
                '@print_debug' => $countDs . ' : ' . $sym . " day_volume=$day_volume todays_volume=$todays_volume ",
                '@print_url' => $url,
              ), WATCHDOG_WARNING
            );
          }*/
          $countDs++;
        }
      } else {
        if (!isset($intraday_array['Time Series (15min)'])) {
          \Drupal::logger('stockapi_info')->warning('<pre>Time Series (15min) intraday - @print_debug - @print_url - json response: @print_json_intraday</pre>', array('@print_debug' =>  $sym,
                    '@print_url' => $url,
                    '@print_json_intraday' => $json_intraday,
            ));
          //continue to next stock_symbol
          try {
            \Drupal::logger('stockapi_info')->notice('Sleep 15 seconds processing symbol:<pre>@print_sym</pre>', array('@print_sym' =>  $sym));
            sleep(20); //wait 20 seconds, try again.
            $json_intraday = _file_get_contents($url);
            $intraday_array = json_decode($json_intraday, TRUE);
            if (isset($intraday_array['Error Message'])) {
              \Drupal::logger('stockapi_info')->notice('<pre>@print_debug - @print_url - @print_r</pre>', array('@print_r' =>
                  print_r($intraday_array['Error Message'], TRUE),
                  '@print_debug' => $mkt . ' : ' . $sym . ' series: ' . $series,
                  '@print_url' => $url,
                ));
              $json_intraday = NULL; //Use this like a flag.
              throw new Exception("no stock data for sym: $sym for mkt: $mkt in" .
              " $series " . $intraday_array['Error Message']);
            }
            if (!isset($intraday_array['Time Series (15min)'])) {
              throw new Exception("no stock data for sym: $sym for mkt: $mkt in" .
              " $series " . $intraday_array['Error Message']);
            }
          } catch (Exception $e) {
            \Drupal::logger('stockapi_info')->notice('<pre>@print_debug</pre>', array('@print_debug' =>  $e->getMessage()));
            continue;
          }
          //bust out.
          break;
        }
        foreach($intraday_array['Time Series (15min)'] as $key => $five_minutes_trading) {
          $segment_volume = $five_minutes_trading['5. volume'];
          $date_of_trade = substr($key, 0, strpos($key, ' '));
          if ($count5s == 0) {
            $current_date_of_trade = $date_of_trade;
            $todays_high = $five_minutes_trading['2. high'];
            $todays_low = $five_minutes_trading['3. low'];
            $last_updated = strtotime($current_date_of_trade);
            $latest_price = $five_minutes_trading['4. close'];
            $latest_segment_volume = $five_minutes_trading['5. volume'];
            // premier.
          } else if ($count5s == $totalNum5s -1) {
            // dernier.
            $dernier = TRUE;
          }
          if ($current_date_of_trade == $date_of_trade) {
            $segment_high = $five_minutes_trading['2. high'];
            $segment_low = $five_minutes_trading['3. low'];
            if ($count5s > 0) {
              $latest_segment_volume = $five_minutes_trading['5. volume'];
            }
            $todays_volume = $todays_volume + $latest_segment_volume;
            $todays_open = $five_minutes_trading['1. open'];
            if ($segment_low < $todays_low) {
              $todays_low = $segment_low;
            }
            if ($segment_high > $todays_high) {
              $todays_high = $segment_high;
            }
          } else {
            if ($flag_for_previous) {
              $flag_for_previous = FALSE;
              $previous_close = $five_minutes_trading['4. close'];
            }
          }
          $count5s++;
        }
      }
      if ($previous_close == 0 && $m_previous_close != 0) {
        $previous_close = $m_previous_close;
      } else if ($previous_close == 0) {
        // Debug.
        if ($dernier) {
          \Drupal::logger('stockapi_info')->warning('<pre>@print_debug - @print_url - @print_r</pre>', array('@print_r' =>
              print_r('test', TRUE),
              '@print_debug' => 'previous_close =0  : ' . $sym . " todays_volume=$todays_volume ",
              '@print_url' => $url . ' urlm= ' . $urlm . ' urld=' . $urld,
            ));
        }
        break; //bail on this
      }
      $stock_results[$stock_symbol][stockapi_avg_daily_volume] = round($m_avg_vol);
      $stock_results[$stock_symbol][stockapi_trade_date] = $trade_date;
      $stock_results[$stock_symbol][stockapi_last_trade_date] = $trade_date;
      $stock_results[$stock_symbol][stockapi_last_trade_time] = $last_trade_time;
      $stock_results[$stock_symbol][stockapi_updated] = time();
      $stock_results[$stock_symbol][stockapi_name] = $stock_name;
      $stock_results[$stock_symbol][stockapi_exchange] = $mkt;

      //return FALSE;
      //define("stockapi_pct_chg", "3");
      $todays_chg = 0.0;
      $pct_chg = 0;
      $rnd2deci = \Drupal::config('stockapi.settings')->get('stockapi_decimals');
      $todays_chg = (floatval($latest_price) - floatval($previous_close));
      $todays_chg = round($todays_chg, $rnd2deci);
      $todays_len = strlen($todays_chg);
      $todays_format = '';
      if (strpos($todays_chg, '.') == ($todays_len-2)) {
        $todays_format = '0';
      } else if ($todays_len == 1) {
        $todays_format = '.00';
      }
      if ($previous_close == 0) {
        // prevent division by zero in case above code fails.
        $previous_close = $latest_price;
      }
      $tmp_pct = round(floatval($todays_chg * -1 / $previous_close * 100), $rnd2deci);
      $tmp_len = strlen($tmp_pct);
      $tmp_format = '';
      if (strpos($tmp_pct, '.') == ($tmp_len-2)) {
        $tmp_format = '0';
      }
      if ($todays_chg >= 0 && $previous_close != 0) {
        $pct_chg = round(floatval($todays_chg / $previous_close * 100), $rnd2deci) . $tmp_format . '%';
      } else if ($previous_close !=0) {
        $tmp_chg = round(floatval($todays_chg * -1), $rnd2deci);
        $pct_chg = '-' . round(floatval($tmp_chg / $previous_close * 100), $rnd2deci) . $tmp_format . '%';
      } else {
        $pct_chg = '0%';
      }
      $chg_and_pct_chg = $todays_chg . $todays_format . ' (' . $pct_chg . ')';
      $stock_results[$stock_symbol][stockapi_trade_price_only] = $latest_price;
      $stock_results[$stock_symbol][stockapi_chg_and_pct_chg] = $chg_and_pct_chg;
      $stock_results[$stock_symbol][stockapi_pct_chg] = $pct_chg;
      $stock_results[$stock_symbol][stockapi_chg] = $todays_chg;
      $stock_results[$stock_symbol][stockapi_volume] = $todays_volume;
      $stock_results[$stock_symbol][stockapi_open] = $todays_open;
      $stock_results[$stock_symbol][stockapi_previous_close] = $previous_close;
      $stock_results[$stock_symbol][stockapi_52_week_range] = round($m_52_low, $rnd2deci) . ' - ' . round($m_52_high, $rnd2deci);
      $stock_results[$stock_symbol][stockapi_days_range] = $todays_low . ' - ' . $todays_high;

      // Fill out empty values, current implementation doesn't have this info.
      //TODO add more info from other sources later.
      $stock_results[$stock_symbol][stockapi_pe_ratio] = 0;
      $stock_results[$stock_symbol][stockapi_float_shares] = 0;
      $stock_results[$stock_symbol][stockapi_market_cap] = 0;
      $stock_results[$stock_symbol][stockapi_symbol] = $stock_symbol;
      //DEBUG ..
      /*
      $test = "$date_of_trade, $current_date_of_trade, $previous_day_of_trade, " .
              "$last_updated, $latest_price, $latest_segment_volume, " .
              "$todays_volume, $todays_high, $todays_low, $previous_close, " .
              "$todays_chg, $chg_and_pct_chg";
      watchdog('stockapi_info', '<pre>@print_r</pre>', array('@print_r' =>
        print_r($test , TRUE)
        ), WATCHDOG_INFO
      ); */
      if (\Drupal::config('stockapi.settings')->get('stockapi_vantage_apikey') == 'demo' && $stock_symbol != 'AAPL' && $stock_symbol != 'MSFT') {
        //Just finished processing stock symbol AAPL, that is enough of a demo.
        break;
      }

      //DEBUG
      /*watchdog('stockapi_info', '<pre>@print_r</pre>', array('@print_r' =>
        print_r($stock_results, TRUE)
        ), WATCHDOG_INFO
      );*/ //DEBUG
    }
    //watchdog('stockapi_results', '<pre>@print_r</pre>', array('@print_r' => print_r( $stock_results, TRUE)));
    //return FALSE; //DEBUGING
    return $stock_results;
  }
  else {
    $batches = array_chunk($symbols, $l);
    $stocks = array();
    foreach ($batches as $key => $batch) {
      $stocks = array_merge(stockapi_fetch($batch), $stocks);
    }
    return $stocks;
  }
}

/**
 * Generate Alpha Vantage - Free JSON api URL.
 *
 * @param string $mkt
 *   Stock market name, i.e. 'NASDAQ' or 'TSE'.
 * @param string $stock_symbol
 *   Stock symbol.
 *
 * @return string
 *   Alpha Vantage API url for given stock market and symbol.
 */
function stockapi_alpha_vantage_json_url($mkt, $stock_symbol, $time_series, $interval) {
  if ($mkt != 'TSE') {
    $mkt = '';
  } else {
    $mkt = $mkt . ':';
  }
  if (strpos($stock_symbol,'.') >= 1) {
    $stock_symbol = str_replace('.','-',$stock_symbol);
  }
  $symbol_key = 'symbol'; //Currently not used, for BATCH_STOCK_QUOTES.
  if (stripos($stock_symbol,',') >= 1) {
    $symbol_key = 'symbols';//For BATCH_STOCK_QUOTES, not yet used.
    $time_series = 'BATCH_STOCK_QUOTES';
  }
  //$time_series options = TIME_SERIES_DAILY_ADJUSTED
  //$time_series options = TIME_SERIES_INTRADAY
  //$time_series options = TIME_SERIES_WEEKLY
  //$time_series options = TIME_SERIES_WEEKLY_ADJUSTED
  //$time_series options = TIME_SERIES_MONTHLY
  //$time_series options = BATCH_STOCK_QUOTES
  //$interval = 1min
  //$interval = 5min
  //$interval = 10min
  //$interval = 15min
  //$interval = 30min
  //$interval = 60min

  $params_array=array();
  if (empty($interval)) {
    $params_array = array(
      'function' => $time_series,
      $symbol_key => $mkt . $stock_symbol,
      'apikey' => \Drupal::config('stockapi.settings')->get('stockapi_vantage_apikey'),
    );
  } else {
    $params_array = array(
      'function' => $time_series,
      $symbol_key => $mkt . $stock_symbol,
      'interval' => $interval,
      'apikey' => \Drupal::config('stockapi.settings')->get('stockapi_vantage_apikey'),
    );
  }
  $host = 'https://www.alphavantage.co/query';
  $options = array(
    'query' => $params_array,
    'external' => TRUE,
    'absolute' => TRUE,
    'https' => TRUE,
  );

  \Drupal::moduleHandler()->alter('stockapi_alpha_vantage_json_url', $host, $options, $mkt, $stock_symbol, $time_series, $interval);

  return Url::fromUri($host, $options)->toString();

}

function _stockapi_money_to_float($money_string) {
  $money_string = trim($money_string);
  $idx_of_scale = strlen($money_string);
  $idx_of_comma = strpos($money_string, ',');
  if (is_numeric($money_string)) {
    $money_float = floatval($money_string);
  }
  else {
    $scale = substr($money_string, -1);
    if ($idx_of_comma >= 0) {
      $money_string = str_replace(',', '', $money_string);
    }
    switch ($scale) {
      case 'M' :
        $money_float = floatval($money_string) * 1000000;
      break;
      case 'm' :
        $money_float = floatval($money_string) * 1000000;
      break;
      case 'B' :
        $money_float = floatval($money_string) * 1000000000;
      break;
      case 'b' :
        $money_float = floatval($money_string) * 1000000000;
      break;
      default:
        $money_float = floatval($money_string);
      break;
    }
  }
  return $money_float;
}

/*
 * Convert scraped key into the db schema key.
 */
function _stockapi_process_key($key) {

  $key = trim($key);
  switch ($key) {
    case 'Range' :
      $key = stockapi_days_range;
    break;
    case '52 week' :
      $key = stockapi_52_week_range;
    break;
    case 'Open' :
      $key = stockapi_open;
    break;
    // needs post processing, comes in as Vol / Avg.
    case 'Vol / Avg.' :
      $key = stockapi_vol_avg_for_postprocessing;
      // Split this later into stockapi_volume and stockapi_avg_daily_volume.
    break;
    case 'Mkt cap' :
      $key = stockapi_market_cap;
    break;
    case 'P/E' :
      $key = stockapi_pe_ratio;
    break;
    case 'Shares' :
      $key = stockapi_float_shares;
    break;
  }
  return $key;
}

/*
 * Convert scraped val into format expected by schema.
 */
function _stockapi_process_val($val) {
  return $val;
}

/*
 * Alternative scrape approach, grab nodeValue lines from whole section.
 */
function _stockapi_parse_web($value, $corp) {
  $sans_vide = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $value);
  //watchdog('stockapi_sans_vide', '<pre>@print_r</pre>', array('@print_r' => print_r( $sans_vide, TRUE)));

  $array = explode("\n", $sans_vide);
  $stock = array();
  $stock[$corp][0]= $corp;//symbol
  foreach ($array as $key => $value) {
    $date_temp = '';
    if ($key == 1) {
      $stock[$corp][$key] = $value; // trade_price_only
    }
    if ($key == 2) {
      $stock[$corp][$key] = substr($value, (stripos($value, '+')+1)); //chg
    }
    if ($key == 3) {
      $stock[$corp][$key] = $value; //pct_chg
    }
    if ($key == 4) {
      $date_temp = substr($value, 0, stripos($value, ' -'));
      $stock[$corp][$key] = strtotime($date_temp . ' 2018'); // Updated
    }
    if (stripos($array[$key], 'Close') > 0) {
      $stock[$corp][5] = $array[1];// previous-close
    }
    if (stripos($array[$key], 'ange') == 1) {
      $stock[$corp][6] = $array[$key+1];// days_range
    }
    if (stripos($array[$key], '2 week') == 1) {
      $stock[$corp][7] = $array[$key+1];// 52_week_range
    }
    if (stripos($array[$key], 'pen') == 1) {
      $stock[$corp][8] = $array[$key+1];// open
    }
    if (stripos($array[$key], 'ol / Avg.') == 1) {
      $val = $array[$key+1];
      $stock[$corp][9] = substr($val, 0, (stripos($val, '/')));// volume
      $stock[$corp][10] = substr($val, (stripos($val, '/')+1));// avg_daily_volume
    }
    if (stripos($array[$key], 'kt cap') == 1) {
      $stock[$corp][11] = $array[$key+1];// market_cap
    }
    if (!empty($date_temp)) {
      $stock[$corp][12] = $date_temp; // trade_date string
      $stock[$corp][13] = $date_temp; // last_trade_date string
    }

  }
  return $stock;
}

function _file_get_contents($url) {

  // Load the JavaScript file

  // Trough drupal_http_request if remote
  $client = \Drupal::httpClient();
  $response = $client->request('GET', $url);
  if(preg_match('/^(http|https|proxy)\:\/\//i',$url)) {
    try {
      $page = $response->getBody();
      //$page = drupal_http_request($url)->data;
      if (!isset($page)) {
        throw new Exception('page not set');
      }
    } catch(Exception $e) {
      if (!isset($page)) {
        \Drupal::logger('stockapi')->notice('Sleep 11 seconds processing url:<pre>@print_url</pre>', array('@print_url' =>  $url));
        //watchdog('stockapi_info', 'Sleep 11 seconds processing url:<pre>@print_url</pre>',
          //array('@print_url' =>  $url),
          //WATCHDOG_NOTICE);
        sleep(11);
        $data = $response->getBody();
        //$page = drupal_http_request($url)->data;
      }
    }
    // Or through the usual way
  } else {
    $page = file_get_contents($url);
  }
  // Terms of service: https://www.alphavantage.co/terms_of_service
  sleep(4); // The api provider wishes no more than 1 request per second.
  return $page;
}
/**
 * Save a single stock quote to the database.
 *
 * @param $stock object
 *   The stock object keys' must match the field names of the database table.
 *   The stock object values' are the data to insert.
 *
 * @return
 *   TRUE on success, FALSE on failure
 */
function stockapi_save($stock) {
  // Fix potential text in float fields.
  $stock = _stockapi_fix_floats($stock);

  if (isset($stock->symbol) && db_query("SELECT symbol FROM {stockapi} WHERE symbol = :symbol", array(':symbol' => $stock->symbol))->fetchField()) {
    // Update
    $symbol = $stock->symbol;
    $stock = (array) $stock;
    unset($stock['symbol']); // Primary key

    $updated = db_update('stockapi')
        ->fields((array) $stock)
        ->condition('symbol', $symbol)
        ->execute();

    return $updated;
  }
  else {
    //Insert
    $inserted = db_insert('stockapi')
        ->fields((array) $stock)
        ->execute();

    return $inserted;
  }
}


/**
 * Return ticker information for a single symbol from our database, not Alpha Vantage.
 *
 * @param $symbol string
 *   The ticker symbol to retrieve information for.
 * @param $q string
 *   The type of information to retrieve. Can be either: basic, extended,
 *   or realtime. The default is the site-wide setting, which is usually the
 *   best bet here.
 * @return object
 */
function stockapi_load($symbol, $q = NULL) {
  static $stocks = array();

  if (!\Drupal\Component\Utility\Unicode::strlen($symbol)) {
    return array();
  }

  if (!isset($stocks[$symbol])) {
    $fields = _stockapi_get_fields($q);
    $results = db_query("SELECT " . implode(',', $fields) . " FROM {stockapi} WHERE symbol = :symbol", array(':symbol' => $symbol), array('fetch' => PDO::FETCH_ASSOC));
    if (count($results)) {
      foreach ($results as $result) {
        $stocks[$symbol] = $result;
      }
    }
  }
  else {
    $stocks[$symbol] = array();
  }

  return $stocks[$symbol];
}

/**
 * Return ticker information for a batch of symbols from our database, not Alpha Vantage.
 *
 * @param $symbol array
 *   An array of ticker symbols to retrieve information for.
 * @param $q string
 *   The type of information to retrieve. Can be either: basic, extended,
 *   or realtime. The default is the site-wide setting, which is usually the
 *   best bet here.
 * @return array
 *   An array of stocks, keyed by their ticker symbol. Each stock is an object within the array.
 */
function stockapi_multiload($symbol, $q = NULL) {
  $stocks = array();
  $fields = _stockapi_get_fields($q);
  $symbols = implode(', ', array_map('_stockapi_quote_it', $symbol));
  $results = db_query("SELECT " . implode(', ', $fields) . " FROM {stockapi} WHERE symbol IN ({$symbols})");
  foreach ($results as $stock) {
    $stocks[$stock->symbol] = $stock;
  }

  return $stocks;
}

/**
 * Wrap a string in quotes. Usually used with array_map().
 */
function _stockapi_quote_it($string, $style = 'single') {
  return ($style != 'single') ? '"' . $string . '"' : "'" . $string . "'";
}

/**
 * Return the field parameter string to pull the correct columns from Alpha Vantage.
 */
function stockapi_get_quotetype($q = NULL) {
  $quotetype = array();
  $quotetype['basic'] = 'snl1d1t1c1p2va2bapomwerr1dyj1x';
  $quotetype['extended'] = $quotetype['basic'] . 'cs7t8e7e8e9r6r7r5b4p6p5j4m3m4k3t7it6j5i5c8r2g6g5w4v7n4l2l3g3d2g4g1w1v1c3p1s1m6m5m8m7qk5k4j6';
  $quotetype['realtime'] = $quotetype['basic'] . 'b2b3k2k1c6m2j3';

  if ($quotetype[$q]) {
    return $quotetype[$q];
  }
  // Always return the basic set as a fall through.
  return $quotetype['basic'];
}

/**
 * Convert a stock from Alpha Vantage to its object oriented counterpart.
 *
 * @return object
 */
function _stockapi_to_object($stock, $q = NULL) {
  $fields = _stockapi_get_fields($q);
  $ns = count($fields);
  if ($ns) {
    $s = new stdClass();
    for ($i = 0; $i < $ns; $i++) {
      $s->{$fields[$i]} = $stock[$i];
      if (isset($stock[$i])) {
        $s->{$fields[$i]} = $stock[$i];
      }
      else {
        $s->{$fields[$i]} = NULL;
        \Drupal::logger('stockapi_info')->notice('<pre>@print_r</pre>', array('@print_r'
         => print_r( "Symbol " . $stock[0] . " Element $i NULL", TRUE)));
      }
    }
  }
  $s->updated = REQUEST_TIME;

  return $s;
}

/**
 * Given the current site-wide quotetype, generate the actual database fields
 * names currently active. These field names usually double as the keys for the
 * stock object.
 */
function _stockapi_get_fields($q = NULL) {
  if (!$q) {
    $q = \Drupal::config('stockapi.settings')->get('stockapi_quotetype');
  }
  preg_match_all('/[a-z][0-9]?/', stockapi_get_quotetype($q), $yfields);
  $yfields = $yfields[0];

  $fields = explode(', ', 'symbol, last_trade_price_only, chg, pct_chg, updated, previous_close, days_range, 52_week_range, open, volume, avg_daily_volume, market_cap, trade_date, last_trade_date, pe_ratio, float_shares, chg_and_pct_chg, last_trade_time, exchange, name');
  //watchdog('stockapi_fields_debug', '<pre>@print_r</pre>', array('@print_r' => print_r( $fields, TRUE)));
  return $fields;
}

/**
 * Internal helper function to deal cleanly with various HTTP response codes.
 */
function _stockapi_request_failure($results) {
  switch ($results->code) {
    case '200': // Success!
    case '304': // Not modified, nothing to do.
      return FALSE;
    default:
      \Drupal::logger('stockapi')->notice('Failed to retrieve stock quotes with error: %error', array('%error' => $results->error));
      return TRUE;
  }
  return FALSE;
}

/**
 * Internal helper function to change text values returned for float
 * fields to '0'.
 */
function _stockapi_fix_floats($stock) {
  $stock = (array) $stock;
  $schema = drupal_get_module_schema('stockapi');

  foreach ($schema['fields'] as $name => $field) {
    if ($field['type'] == 'float' && isset($stock[$name]) && !is_numeric($stock[$name])) {
      $stock[$name] = 0;
    }
  }

  return (object) $stock;
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc