-1
\$\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\$
  • 2
    \$\begingroup\$ "I have simplified and added some dummy numbers" Please, don't simplify your code before putting it up for review. Take a look at the help center and our guide on asking questions if anything is unclear, but we need to see your real code in it's real habitat to be able to write a quality, useful review. \$\endgroup\$ – Mast Mar 16 at 15:15
  • \$\begingroup\$ Scripts being too large doesn't change our rules, really. We have a character limit for questions of 65536. That's more than double of what fits on most Stack Exchange sites. Are you sure it's too big to fit? \$\endgroup\$ – Mast Mar 16 at 15:20
1
\$\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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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