0
\$\begingroup\$

Class SectorController calculates weight coefficients for sector performances in equity exchange markets using minute data from an API (for instance, if a group of equities are up in the past 5 minutes, then coefficient is positive, if not, is negative, ranging from -1 to +1). Most of calculations are based on other scripts, which is not necessary for this review.

Would you be so kind and review this class and help me to make it faster, if possible?

Script

class SectorController
{

  /**
  *
  * @var a string of iextrading base URL
  */

  const BASE_URL = "https://api.iextrading.com/1.0/"; 

  /**
  *
  * @var a string of target path and query
  */

  const TARGET_QUERY = "stock/market/batch?symbols=";


  /**
  *
  * @var a string for backend path for every sector
  */

  const EACH_SECTOR_DIR_PREFIX = "/../../dir/dir/dir/dir-";


  /**
  *
  * @var a string for backend path for index sector
  */

  const INDEX_SECTOR_DIR_PREFIX = "/../../dir/dir/dir/dir/";


  /**
  *
  * @var a string for live data path
  */

  const LIVE_DATA_DIR = "/../../../public_html/dir/dir/";

  function __construct()
  {
    echo "YAAAY! " . __METHOD__ . " success 💚\n"; 
    return true;
  }

  public static function getSectors(){
    $baseUrl=self::BASE_URL.self::TARGET_QUERY;
    $currentTime = date("Y-m-d-H-i-s");

    $permission = 0755;

    $indexData = array( "Overall" => array("sector_weight" => 1, "sector_coefficient" => 5,
    $sectorInfos=SectorController::iexSectorParams();
    foreach ($sectorInfos as $a => $sectorInfo) {
      $sectorUrl = $baseUrl . implode(",", array_keys($sectorInfo["selected_tickers"])) . "&types=quote&range=1m";
      $rawSectorJson = file_get_contents($sectorUrl);
      $rawSectorArray = json_decode($rawSectorJson, true);

      // Write the raw file
      $rawSectorDir =  __DIR__ . self::EACH_SECTOR_DIR_PREFIX . $sectorInfo["directory"];

      if (!is_dir($rawSectorDir)) {
        mkdir($rawSectorDir, $permission, true);
      }

      $rawSectorFile = $rawSectorDir . "/" . $currentTime . ".json";
      $fp = fopen($rawSectorFile, "a+");
      fwrite($fp, $rawSectorJson);
      fclose($fp);

      // Calculate the real-time index
      $indexValue = 0;
      foreach ($rawSectorArray as $ticker => $tickerStats) {
        if (isset($sectorInfo["selected_tickers"][$ticker], $tickerStats["quote"], $tickerStats["quote"]["extendedChangePercent"], $tickerStats["quote"]["changePercent"], $tickerStats["quote"]["ytdChange"])) {

          $changeAmount = ($tickerStats["quote"]["extendedChangePercent"] + $tickerStats["quote"]["changePercent"] + $tickerStats["quote"]["ytdChange"])/200;
          $indexValue += $sectorInfo["sector_weight"] * $sectorInfo["selected_tickers"][$ticker] * $changeAmount;
        }
      }

      $indexData[$sectorInfo["sector"]] = array("sector_weight" => $sectorInfo["sector_weight"], "sector_coefficient" => 5,
      $indexData["Overall"]["sector_value"] += $indexData[$sectorInfo["sector"]]["sector_value"];
    }

    // Calculate the index factor for better visibility between -1 and +1
    $frontIndexData = array();
    foreach ($indexData as $sectorName => $sectorIndexData) {
      $indexSign = $sectorIndexData["sector_value"];
      if ($indexSign < 0) {
        $indexSign = - $indexSign;
      }

      $indexFactor = 1;
      for ($i=0; $i <= 10; $i++) { 
        $indexFactor = pow(10, $i);
        if (($indexFactor * $indexSign) > 1) {
          $indexFactor = pow(10, $i - 1);
          break;
        }
      }

      $frontIndexData[$sectorName] = $sectorIndexData["sector_weight"] * $sectorIndexData["sector_coefficient"] * $sectorIndexData["sector_value"] * $indexFactor;
    }

    // Write the index file
    $indexSectorDir =  __DIR__ . self::INDEX_SECTOR_DIR_PREFIX;

    if (!is_dir($indexSectorDir)) {mkdir($indexSectorDir, $permission, true);}

    $indexSectorFile = $indexSectorDir . $currentTime . ".json";

    $indexSectorJson = json_encode($frontIndexData, JSON_FORCE_OBJECT);
    $fp = fopen($indexSectorFile, "a+");
    fwrite($fp, $indexSectorJson);
    fclose($fp);

    $sectorDir =  __DIR__ . self::LIVE_DATA_DIR;


    if (!is_dir($sectorDir)) {mkdir($sectorDir, $permission, true);} // if data directory did not exist

    // if text file did not exist
    if (!file_exists($sectorDir . "text.txt")){
      $handle=fopen($sectorDir . "text.txt", "wb");
      fwrite($handle, "d");
      fclose($handle);
    }

    $sectorCoefFile = $sectorDir . "text.txt";
    copy($indexSectorFile, $sectorCoefFile);
    echo "YAAAY! " . __METHOD__ . " updated sector coefficients successfully 💚!\n";

    return $frontIndexData;
  }


  public static function iexSectorParams(){
    $sectorInfos = array(
      array(
        "sector" => "IT",
        "directory" => "information-technology",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array( 
          "AAPL" => 0.05,
          "AMZN" => 0.05,
          "GOOGL" => 0.05,
          "IBM" => 0.05,
          "MSFT" => 0.05,
        )
      ),
      array( 
        "sector" => "Telecommunication",
        "directory" => "telecommunication-services",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array(
          "VZ" => 0.05,
          "CSCO" => 0.05,
          "CMCSA" => 0.05,
          "T" => 0.05,
          "CTL" => 0.05,
        )
      ),
      array( 
        "sector" => "Finance",
        "directory" => "financial-services",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array( 
          "JPM" => 0.05,
          "GS" => 0.05,
          "V" => 0.05,
          "BAC" => 0.05,
          "AXP" => 0.05,
        )
      ),
      array(
        "sector" => "Energy",
        "directory" => "energy",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array(
          "CVX" => 0.05,
          "XOM" => 0.05,
          "APA" => 0.05,
          "COP" => 0.05,
        )
      ),
      array(
        "sector" => "Industrials",
        "directory" => "industrials",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array(
          "CAT" => 0.05,
          "FLR" => 0.05,
          "GE" => 0.05,
          "JEC" => 0.05,
        )
      ),
      array(
        "sector" => "Materials and Chemicals",
        "directory" => "materials-and-chemicals",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array(
          "DWDP" => 0.05,
          "APD" => 0.05,
          "EMN" => 0.05,
          "ECL" => 0.05,
          "FMC" => 0.05,
        )
      ),
      array(
        "sector" => "Utilities",
        "directory" => "utilities",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array(
          "PPL" => 0.05,
          "PCG" => 0.05,
          "SO" => 0.05,
          "WEC" => 0.05,
        )
      ),
      array( 
        "sector" => "Consumer Discretionary",
        "directory" => "consumer-discretionary",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array(
          "DIS" => 0.05,
          "HD" => 0.05,
          "BBY" => 0.05,
          "CBS" => 0.05,
          "CMG" => 0.05,
        )
      ),
      array(
        "sector" => "Consumer Staples",
        "directory" => "consumer-staples",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array( 
          "PEP" => 0.05,
          "PM" => 0.05,
          "PG" => 0.05,
          "MNST" => 0.05,
          "TSN" => 0.05,
        )
      ),
      array( 
        "sector" => "Defense",
        "directory" => "defense-and-aerospace",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array( 
          "BA" => 0.05,
          "LMT" => 0.05,
          "UTX" => 0.05,
          "NOC" => 0.05,
          "HON" => 0.05,
        )
      ),
      array( 
        "sector" => "Health",
        "directory" => "health-care-and-pharmaceuticals",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array( 
          "UNH" => 0.05,
          "JNJ" => 0.05,
          "PFE" => 0.05,
          "UHS" => 0.05,
          "AET" => 0.05,
          "RMD" => 0.05,
        )
      ),


      array(
        "sector" => "Real Estate",
        "directory" => "real-estate",
        "sector_weight" => 0.05,
        "sector_coefficient" => 5,
        "selected_tickers" => array(
          "CCI" => 0.05,
          "AMT" => 0.05,
          "AVB" => 0.05,
          "HCP" => 0.05,
          "RCL" => 0.05,
          "HST" => 0.05,
        )
      )
    );
    return $sectorInfos;
  }


  function __destruct()
  {
    echo "YAAAY! " . __METHOD__ . " success! 💚 \n";
    return true;
  }

}

Output (text.txt)

{"Overall":0.05,"IT":0.05,"Telecommunication":0.05,"Finance":0.05,"Energy":0.05,"Industrials":0.05,"Materials and Chemicals":0.05,"Utilities":0.05,"Consumer Discretionary":0.05,"Consumer Staples":0.05,"Defense":0.05,"Health":0.05,"Real Estate":0.05}

\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

I don't fully understand what your script is doing, but I can offer a few refinements.

  1. Regarding $sectorUrl = $baseUrl . implode(",", array_keys($sectorInfo["selected_tickers"])) . "&types=quote&range=1m";, because you are building a url, I think it would be better practices to implode with %2C to make the RFC folks happy.

  2. It doesn't look like a good idea to append json strings after json strings. For this reason, you should not be fwriting with a+. If you mean to consolidate json data on a single json file, then the pre-written data needs to be extracted, decoded, merged with the next data, then re-encoded before updating the file. Otherwise, you will generate invalid json in your .json file.

  3. Rather than manually converting negative values to positive with if ($indexSign < 0) {$indexSign = - $indexSign;}, you should be using abs() to force values to be positive.

    $indexSign = abs($sectorIndexData["sector_value"]);
    
  4. The $indexFactor can be determined without iterated mathematics, you can treat it as a string and just count the zeros immediately to the right of the decimal place.

    $indexFactor = 10 ** strlen(preg_match('~\.\K0+~', $float, $zeros) ? $zeros[0] : 0)
    

    The \K in the pattern means "restart the fullstring match" on perhaps it would be clearer for this situation to say "forget the previously matched characters (the dot)". pow() can be written as ** from php5.6+

Beyond those few pieces, I don't see much to comment on. As I have stated in recent posts on your questions, always endeavor to minimize total fwrite() calls as much as possible.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.