Equity data processing: Fast and/or efficient file writing using PHP The Next CEO of Stack OverflowIs there more efficient way of processing my XML file in PHP?Formatting and writing dataPHP class for file handling and creationREST API using PHPReading and writing to fileUsing PHP to create a file structurePHP script to download json file and display dataEfficient ways of looking for start-key and processing dataProcessing XYZ data from a large fileWriting a ~100Kb HTML string over an MD file (number of iterations ~10K)
Why do airplanes bank sharply to the right after air-to-air refueling?
Why does standard notation not preserve intervals (visually)
Is micro rebar a better way to reinforce concrete than rebar?
Non-deterministic sum of floats
Are there any unintended negative consequences to allowing PCs to gain multiple levels at once in a short milestone-XP game?
Why do professional authors make "consistency" mistakes? And how to avoid them?
What was the first Unix version to run on a microcomputer?
To not tell, not take, and not want
What connection does MS Office have to Netscape Navigator?
Indicator light circuit
Is there a difference between "Fahrstuhl" and "Aufzug"
Has this building technique been used in an official set?
If/When UK leaves the EU, can a future goverment conduct a referendum to join the EU?
What can we do to stop prior company from asking us questions?
Example of a Mathematician/Physicist whose Other Publications during their PhD eclipsed their PhD Thesis
Is it possible to search for a directory/file combination?
What benefits would be gained by using human laborers instead of drones in deep sea mining?
Can we say or write : "No, it'sn't"?
How powerful is the invisibility granted by the Gloom Stalker ranger's Umbral Sight feature?
Would a galaxy be visible from outside, but nearby?
Won the lottery - how do I keep the money?
Why am I allowed to create multiple unique pointers from a single object?
WOW air has ceased operation, can I get my tickets refunded?
Several mode to write the symbol of a vector
Equity data processing: Fast and/or efficient file writing using PHP
The Next CEO of Stack OverflowIs there more efficient way of processing my XML file in PHP?Formatting and writing dataPHP class for file handling and creationREST API using PHPReading and writing to fileUsing PHP to create a file structurePHP script to download json file and display dataEfficient ways of looking for start-key and processing dataProcessing XYZ data from a large fileWriting a ~100Kb HTML string over an MD file (number of iterations ~10K)
$begingroup$
This is my first scripting project and I'm sure it has so many issues.
The main class, EQ
, scrapes equities data using EquityRecords
, calculates sector coefficients using
SectorMovers
, estimates equity prices, and finally writes an HTML string in the view files. I couldn't add EQ
class in the post due to character limitation of the posts. The entire code is on this GitHub link.
The only goal is making EQ
as fast/efficient as possible for a single server. Would you be so kind and review it and kindly help me to reach this goal?
EquityRecords
date_default_timezone_set("UTC");
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
set_time_limit(0);
// EquityRecords::allEquitiesSignleJSON(new EquityRecords());
class EquityRecords
const NUMBER_OF_STOCKS_PER_REQUEST = 100;
const NEW_LINE = "n";
/**
*
* @var a string of iextrading symbols
*/
const SYMBOLS_PATH = '/../../config/z-iextrading-symbs.md';
/**
*
* @var a string of our symbols json directory
*/
const SYMBOLS_DIR = "/../../blog-back/equities/real-time-60sec/z-raw-equilibrium-estimation";
/**
*
* @var a string of target path and query
*/
const TARGET_QUERY = "stock/market/batch?symbols=";
/**
*
* @var a string of iextrading base URL
*/
const BASE_URL = "https://api.iextrading.com/1.0/";
/**
*
* @var a string of iextrading end point
*/
const END_POINT = "&types=quote,chart&range=1m&last=10";
/**
*
* @var an integer for maximum number of stocks per URL on each call
*/
//***************** A ********************** //
// public static function getSymbols()
// return array_map(function($line) return str_getcsv($line, "t"); , file(__DIR__ . self::SYMBOLS_PATH));
//
public static function getSymbols()
//***************** START: ALL SYMBOLS ARRAY ********************** //
// var: is a filename path directory, where there is an md file with list of equities
$list_of_equities_file = __DIR__ . self::SYMBOLS_PATH;
// var: is content of md file with list of equities
$content_of_equities = file_get_contents($list_of_equities_file);
// var is an array(3) of equities such as: string(4) "ZYNE", string(10) "2019-01-04", string(27) "ZYNERBA PHARMACEUTICALS INC"
// $symbols_array=preg_split('/rn
public static function allEquitiesSignleJSON()
$equity_arrays = EquityRecords::getSymbols();
$base_url = self::BASE_URL . self::TARGET_QUERY;
$current_time = date("Y-m-d-H-i-s");
$all_equities = array();
// ticker: AAPL, GE, AMD
foreach ($equity_arrays as $ticker_arr)
$ticker = array_column($ticker_arr, 0);
$equity_url = $base_url . implode("%2C", $ticker) . self::END_POINT;
$raw_eauity_json = file_get_contents($equity_url);
$raw_equity_array = json_decode($raw_eauity_json, true);
$all_equities = array_merge($all_equities, $raw_equity_array);
$all_equities_json = json_encode($all_equities);
$symbols_dir = __DIR__ . self::SYMBOLS_DIR;
if (!is_dir($symbols_dir)) mkdir($symbols_dir, 0755, true);
$raw_equity_file = $symbols_dir . "/" . $current_time . ".json";
$fp = fopen($raw_equity_file, "x+");
fwrite($fp, $all_equities_json);
fclose($fp);
echo "YAAAY! Equity JSON file success at " . __METHOD__ . " ! 💚 " . self::NEW_LINE;
/**
* @return a string for var_dump
*/
public static function p()
$args = func_get_args();
$die = (end($args) === 1) && array_pop($args);
echo self::NEW_LINE;
foreach ($args as $v)
$output = print_r($v, true);
var_dump($output) . self::NEW_LINE;
echo self::NEW_LINE;
if ($die)
die();
SectorMovers
date_default_timezone_set("UTC");
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
set_time_limit(0);
require_once __DIR__ . "/EquityRecords.php";
SectorMovers::getSectors();
class SectorMovers
/**
*
* @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 = "/../../blog-back/sectors/real-time-60sec/z-raw-sector-";
/**
*
* @var a string for backend path for index sector
*/
const INDEX_SECTOR_DIR_PREFIX = "/../../blog-back/sectors/real-time-60sec/y-index/";
/**
*
* @var a string for live data path
*/
const LIVE_DATA_DIR = "/../../../public_html/blog/files/";
const DIR_FRONT_SECTOR_COEF_FILENAME = "s-1.txt"; // Filename that records sector coefficient JSON
public static function getSectors()
$base_url = self::BASE_URL . self::TARGET_QUERY;
$current_time = date("Y-m-d-H-i-s");
$permission = 0755;
$index_data = array("Overall" => array("sector_weight" => 1, "sector_coefficient" => 1, "sector_value" => 0));
$sector_movers = SectorMovers::iexSectorParams();
foreach ($sector_movers as $sector_mover)
// $sector_url = $base_url . implode(",", array_keys($sector_mover["selected_tickers"])) . "&types=quote&range=1m";
$sector_url = $base_url . implode("%2C", array_keys($sector_mover["selected_tickers"])) . "&types=quote&range=1m";
$rawSectorJson = file_get_contents($sector_url);
$raw_sector_array = json_decode($rawSectorJson, true);
// ******************* Back Data ***************** //
// Write the raw file in the back directories
// $rawSectorDir = __DIR__ . self::EACH_SECTOR_DIR_PREFIX . $sector_mover["directory"];
// // if back directory not exist
// if (!is_dir($rawSectorDir)) mkdir($rawSectorDir, $permission, true);
// // create and open/write/close sector data to back directories
// $rawSectorFile = $rawSectorDir . "/" . $current_time . ".json";
// $fp = fopen($rawSectorFile, "a+");
// fwrite($fp, $rawSectorJson);
// fclose($fp);
// ******************* End Back Data ***************** //
// Calculate the real-time index
$index_value = 0;
foreach ($raw_sector_array as $ticker => $ticker_stats)
if (isset($sector_mover["selected_tickers"][$ticker], $ticker_stats["quote"], $ticker_stats["quote"]["extendedChangePercent"], $ticker_stats["quote"]["changePercent"], $ticker_stats["quote"]["ytdChange"]))
$change_amount = ($ticker_stats["quote"]["extendedChangePercent"] + $ticker_stats["quote"]["changePercent"] + $ticker_stats["quote"]["ytdChange"]) / 200;
$index_value += $sector_mover["sector_weight"] * $sector_mover["selected_tickers"][$ticker] * $change_amount;
$index_data[$sector_mover["sector"]] = array("sector_weight" => $sector_mover["sector_weight"], "sector_coefficient" => $sector_mover["sector_coefficient"], "sector_value" => $index_value);
$index_data["Overall"]["sector_value"] += $index_data[$sector_mover["sector"]]["sector_value"];
// Calculate the index factor for better visibility between -1 and +1
$front_index_data = array();
foreach ($index_data as $sector_name => $sector_index_data)
// $index_sign = $sector_index_data["sector_value"];
// if ($index_sign < 0)
// $index_sign = - $index_sign;
//
$index_sign = abs($sector_index_data["sector_value"]);
$index_factor = 1;
for ($i = 0; $i <= 10; $i++)
$index_factor = pow(10, $i);
if (($index_factor * $index_sign) > 1)
$index_factor = pow(10, $i - 1);
break;
// $index_factor = 10 ** strlen(preg_match('~.K0+~', $float, $zeros) ? $zeros[0] : 0);
$front_index_data[$sector_name] = $sector_index_data["sector_weight"] * $sector_index_data["sector_coefficient"] * $sector_index_data["sector_value"] * $index_factor;
// Write the index file
$index_sector_dir = __DIR__ . self::INDEX_SECTOR_DIR_PREFIX;
if (!is_dir($index_sector_dir)) mkdir($index_sector_dir, $permission, true);
$index_sector_file = $index_sector_dir . $current_time . ".json";
$index_sector_json = json_encode($front_index_data, JSON_FORCE_OBJECT);
$fp = fopen($index_sector_file, "a+");
fwrite($fp, $index_sector_json);
fclose($fp);
$sector_dir = __DIR__ . self::LIVE_DATA_DIR;
if (!is_dir($sector_dir)) mkdir($sector_dir, $permission, true); // if data directory did not exist
// if s-1 file did not exist
if (!file_exists($sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME))
$handle = fopen($sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME, "wb");
fwrite($handle, "d");
fclose($handle);
$sector_coef_file = $sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME;
copy($index_sector_file, $sector_coef_file);
echo "YAAAY! " . __METHOD__ . " updated sector coefficients successfully 💚!n";
return $front_index_data;
public static function iexSectorParams()
$sector_movers = array(
array(
"sector" => "IT",
"directory" => "information-technology",
"sector_weight" => 0.18,
"sector_coefficient" => 4,
"selected_tickers" => array(
"AAPL" => 0.18,
"AMZN" => 0.16,
"GOOGL" => 0.14,
"IBM" => 0.2,
"MSFT" => 0.1,
"FB" => 0.1,
"NFLX" => 0.08,
"ADBE" => 0.06,
"CRM" => 0.04,
"NVDA" => 0.02,
),
),
array(
"sector" => "Telecommunication",
"directory" => "telecommunication-services",
"sector_weight" => 0.12,
"sector_coefficient" => 4,
"selected_tickers" => array(
"VZ" => 0.18,
"CSCO" => 0.16,
"CMCSA" => 0.14,
"T" => 0.12,
"CTL" => 0.1,
"CHTR" => 0.1,
"S" => 0.08,
"DISH" => 0.06,
"USM" => 0.04,
"VOD" => 0.02,
),
),
array(
"sector" => "Finance",
"directory" => "financial-services",
"sector_weight" => 0.1,
"sector_coefficient" => 6,
"selected_tickers" => array(
"JPM" => 0.18,
"GS" => 0.16,
"V" => 0.14,
"BAC" => 0.12,
"AXP" => 0.1,
"WFC" => 0.1,
"USB" => 0.08,
"PNC" => 0.06,
"AMG" => 0.04,
"AIG" => 0.02,
),
),
array(
"sector" => "Energy",
"directory" => "energy",
"sector_weight" => 0.1,
"sector_coefficient" => 6,
"selected_tickers" => array(
"CVX" => 0.18,
"XOM" => 0.16,
"APA" => 0.14,
"COP" => 0.12,
"BHGE" => 0.1,
"VLO" => 0.1,
"APC" => 0.08,
"ANDV" => 0.06,
"OXY" => 0.04,
"HAL" => 0.02,
),
),
array(
"sector" => "Industrials",
"directory" => "industrials",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"CAT" => 0.18,
"FLR" => 0.16,
"GE" => 0.14,
"JEC" => 0.12,
"JCI" => 0.1,
"MAS" => 0.1,
"FLS" => 0.08,
"AAL" => 0.06,
"AME" => 0.04,
"CHRW" => 0.02,
),
),
array(
"sector" => "Materials and Chemicals",
"directory" => "materials-and-chemicals",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"DWDP" => 0.18,
"APD" => 0.16,
"EMN" => 0.14,
"ECL" => 0.12,
"FMC" => 0.1,
"LYB" => 0.1,
"MOS" => 0.08,
"NEM" => 0.06,
"PPG" => 0.04,
"MLM" => 0.02,
),
),
array(
"sector" => "Utilities",
"directory" => "utilities",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"PPL" => 0.18,
"PCG" => 0.16,
"SO" => 0.14,
"WEC" => 0.12,
"PEG" => 0.1,
"XEL" => 0.1,
"D" => 0.08,
"NGG" => 0.06,
"NEE" => 0.04,
"PNW" => 0.02,
),
),
array(
"sector" => "Consumer Discretionary",
"directory" => "consumer-discretionary",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"DIS" => 0.18,
"HD" => 0.16,
"BBY" => 0.14,
"CBS" => 0.12,
"CMG" => 0.1,
"MCD" => 0.1,
"GPS" => 0.08,
"HOG" => 0.06,
"AZO" => 0.04,
"EXPE" => 0.02,
),
),
array(
"sector" => "Consumer Staples",
"directory" => "consumer-staples",
"sector_weight" => 0.06,
"sector_coefficient" => 8,
"selected_tickers" => array(
"PEP" => 0.18,
"PM" => 0.16,
"PG" => 0.14,
"MNST" => 0.12,
"TSN" => 0.1,
"CPB" => 0.1,
"HRL" => 0.08,
"SJM" => 0.06,
"CAG" => 0.04,
"KHC" => 0.02,
),
),
array(
"sector" => "Defense",
"directory" => "defense-and-aerospace",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"BA" => 0.18,
"LMT" => 0.16,
"UTX" => 0.14,
"NOC" => 0.12,
"HON" => 0.1,
"RTN" => 0.1,
"TXT" => 0.08,
"LLL" => 0.06,
"COL" => 0.04,
"GD" => 0.02,
),
),
array(
"sector" => "Health",
"directory" => "health-care-and-pharmaceuticals",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"UNH" => 0.18,
"JNJ" => 0.16,
"PFE" => 0.14,
"UHS" => 0.12,
"AET" => 0.1,
"RMD" => 0.1,
"TMO" => 0.08,
"MRK" => 0.06,
"ABT" => 0.04,
"LLY" => 0.02,
),
),
array(
"sector" => "Real Estate",
"directory" => "real-estate",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"CCI" => 0.18,
"AMT" => 0.16,
"AVB" => 0.14,
"HCP" => 0.12,
"RCL" => 0.1,
"HST" => 0.1,
"NCLH" => 0.08,
"HLT" => 0.06,
"ARE" => 0.04,
"AIV" => 0.02,
),
),
);
return $sector_movers;
I'd like to thank these users for being so helpful, which I could implement some of their advices in the code.
performance beginner php file-system api
$endgroup$
This question has an open bounty worth +50
reputation from Emma ending ending at 2019-04-05 23:34:46Z">in 7 days.
Looking for an answer drawing from credible and/or official sources.
|
show 1 more comment
$begingroup$
This is my first scripting project and I'm sure it has so many issues.
The main class, EQ
, scrapes equities data using EquityRecords
, calculates sector coefficients using
SectorMovers
, estimates equity prices, and finally writes an HTML string in the view files. I couldn't add EQ
class in the post due to character limitation of the posts. The entire code is on this GitHub link.
The only goal is making EQ
as fast/efficient as possible for a single server. Would you be so kind and review it and kindly help me to reach this goal?
EquityRecords
date_default_timezone_set("UTC");
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
set_time_limit(0);
// EquityRecords::allEquitiesSignleJSON(new EquityRecords());
class EquityRecords
const NUMBER_OF_STOCKS_PER_REQUEST = 100;
const NEW_LINE = "n";
/**
*
* @var a string of iextrading symbols
*/
const SYMBOLS_PATH = '/../../config/z-iextrading-symbs.md';
/**
*
* @var a string of our symbols json directory
*/
const SYMBOLS_DIR = "/../../blog-back/equities/real-time-60sec/z-raw-equilibrium-estimation";
/**
*
* @var a string of target path and query
*/
const TARGET_QUERY = "stock/market/batch?symbols=";
/**
*
* @var a string of iextrading base URL
*/
const BASE_URL = "https://api.iextrading.com/1.0/";
/**
*
* @var a string of iextrading end point
*/
const END_POINT = "&types=quote,chart&range=1m&last=10";
/**
*
* @var an integer for maximum number of stocks per URL on each call
*/
//***************** A ********************** //
// public static function getSymbols()
// return array_map(function($line) return str_getcsv($line, "t"); , file(__DIR__ . self::SYMBOLS_PATH));
//
public static function getSymbols()
//***************** START: ALL SYMBOLS ARRAY ********************** //
// var: is a filename path directory, where there is an md file with list of equities
$list_of_equities_file = __DIR__ . self::SYMBOLS_PATH;
// var: is content of md file with list of equities
$content_of_equities = file_get_contents($list_of_equities_file);
// var is an array(3) of equities such as: string(4) "ZYNE", string(10) "2019-01-04", string(27) "ZYNERBA PHARMACEUTICALS INC"
// $symbols_array=preg_split('/rn
public static function allEquitiesSignleJSON()
$equity_arrays = EquityRecords::getSymbols();
$base_url = self::BASE_URL . self::TARGET_QUERY;
$current_time = date("Y-m-d-H-i-s");
$all_equities = array();
// ticker: AAPL, GE, AMD
foreach ($equity_arrays as $ticker_arr)
$ticker = array_column($ticker_arr, 0);
$equity_url = $base_url . implode("%2C", $ticker) . self::END_POINT;
$raw_eauity_json = file_get_contents($equity_url);
$raw_equity_array = json_decode($raw_eauity_json, true);
$all_equities = array_merge($all_equities, $raw_equity_array);
$all_equities_json = json_encode($all_equities);
$symbols_dir = __DIR__ . self::SYMBOLS_DIR;
if (!is_dir($symbols_dir)) mkdir($symbols_dir, 0755, true);
$raw_equity_file = $symbols_dir . "/" . $current_time . ".json";
$fp = fopen($raw_equity_file, "x+");
fwrite($fp, $all_equities_json);
fclose($fp);
echo "YAAAY! Equity JSON file success at " . __METHOD__ . " ! 💚 " . self::NEW_LINE;
/**
* @return a string for var_dump
*/
public static function p()
$args = func_get_args();
$die = (end($args) === 1) && array_pop($args);
echo self::NEW_LINE;
foreach ($args as $v)
$output = print_r($v, true);
var_dump($output) . self::NEW_LINE;
echo self::NEW_LINE;
if ($die)
die();
SectorMovers
date_default_timezone_set("UTC");
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
set_time_limit(0);
require_once __DIR__ . "/EquityRecords.php";
SectorMovers::getSectors();
class SectorMovers
/**
*
* @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 = "/../../blog-back/sectors/real-time-60sec/z-raw-sector-";
/**
*
* @var a string for backend path for index sector
*/
const INDEX_SECTOR_DIR_PREFIX = "/../../blog-back/sectors/real-time-60sec/y-index/";
/**
*
* @var a string for live data path
*/
const LIVE_DATA_DIR = "/../../../public_html/blog/files/";
const DIR_FRONT_SECTOR_COEF_FILENAME = "s-1.txt"; // Filename that records sector coefficient JSON
public static function getSectors()
$base_url = self::BASE_URL . self::TARGET_QUERY;
$current_time = date("Y-m-d-H-i-s");
$permission = 0755;
$index_data = array("Overall" => array("sector_weight" => 1, "sector_coefficient" => 1, "sector_value" => 0));
$sector_movers = SectorMovers::iexSectorParams();
foreach ($sector_movers as $sector_mover)
// $sector_url = $base_url . implode(",", array_keys($sector_mover["selected_tickers"])) . "&types=quote&range=1m";
$sector_url = $base_url . implode("%2C", array_keys($sector_mover["selected_tickers"])) . "&types=quote&range=1m";
$rawSectorJson = file_get_contents($sector_url);
$raw_sector_array = json_decode($rawSectorJson, true);
// ******************* Back Data ***************** //
// Write the raw file in the back directories
// $rawSectorDir = __DIR__ . self::EACH_SECTOR_DIR_PREFIX . $sector_mover["directory"];
// // if back directory not exist
// if (!is_dir($rawSectorDir)) mkdir($rawSectorDir, $permission, true);
// // create and open/write/close sector data to back directories
// $rawSectorFile = $rawSectorDir . "/" . $current_time . ".json";
// $fp = fopen($rawSectorFile, "a+");
// fwrite($fp, $rawSectorJson);
// fclose($fp);
// ******************* End Back Data ***************** //
// Calculate the real-time index
$index_value = 0;
foreach ($raw_sector_array as $ticker => $ticker_stats)
if (isset($sector_mover["selected_tickers"][$ticker], $ticker_stats["quote"], $ticker_stats["quote"]["extendedChangePercent"], $ticker_stats["quote"]["changePercent"], $ticker_stats["quote"]["ytdChange"]))
$change_amount = ($ticker_stats["quote"]["extendedChangePercent"] + $ticker_stats["quote"]["changePercent"] + $ticker_stats["quote"]["ytdChange"]) / 200;
$index_value += $sector_mover["sector_weight"] * $sector_mover["selected_tickers"][$ticker] * $change_amount;
$index_data[$sector_mover["sector"]] = array("sector_weight" => $sector_mover["sector_weight"], "sector_coefficient" => $sector_mover["sector_coefficient"], "sector_value" => $index_value);
$index_data["Overall"]["sector_value"] += $index_data[$sector_mover["sector"]]["sector_value"];
// Calculate the index factor for better visibility between -1 and +1
$front_index_data = array();
foreach ($index_data as $sector_name => $sector_index_data)
// $index_sign = $sector_index_data["sector_value"];
// if ($index_sign < 0)
// $index_sign = - $index_sign;
//
$index_sign = abs($sector_index_data["sector_value"]);
$index_factor = 1;
for ($i = 0; $i <= 10; $i++)
$index_factor = pow(10, $i);
if (($index_factor * $index_sign) > 1)
$index_factor = pow(10, $i - 1);
break;
// $index_factor = 10 ** strlen(preg_match('~.K0+~', $float, $zeros) ? $zeros[0] : 0);
$front_index_data[$sector_name] = $sector_index_data["sector_weight"] * $sector_index_data["sector_coefficient"] * $sector_index_data["sector_value"] * $index_factor;
// Write the index file
$index_sector_dir = __DIR__ . self::INDEX_SECTOR_DIR_PREFIX;
if (!is_dir($index_sector_dir)) mkdir($index_sector_dir, $permission, true);
$index_sector_file = $index_sector_dir . $current_time . ".json";
$index_sector_json = json_encode($front_index_data, JSON_FORCE_OBJECT);
$fp = fopen($index_sector_file, "a+");
fwrite($fp, $index_sector_json);
fclose($fp);
$sector_dir = __DIR__ . self::LIVE_DATA_DIR;
if (!is_dir($sector_dir)) mkdir($sector_dir, $permission, true); // if data directory did not exist
// if s-1 file did not exist
if (!file_exists($sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME))
$handle = fopen($sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME, "wb");
fwrite($handle, "d");
fclose($handle);
$sector_coef_file = $sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME;
copy($index_sector_file, $sector_coef_file);
echo "YAAAY! " . __METHOD__ . " updated sector coefficients successfully 💚!n";
return $front_index_data;
public static function iexSectorParams()
$sector_movers = array(
array(
"sector" => "IT",
"directory" => "information-technology",
"sector_weight" => 0.18,
"sector_coefficient" => 4,
"selected_tickers" => array(
"AAPL" => 0.18,
"AMZN" => 0.16,
"GOOGL" => 0.14,
"IBM" => 0.2,
"MSFT" => 0.1,
"FB" => 0.1,
"NFLX" => 0.08,
"ADBE" => 0.06,
"CRM" => 0.04,
"NVDA" => 0.02,
),
),
array(
"sector" => "Telecommunication",
"directory" => "telecommunication-services",
"sector_weight" => 0.12,
"sector_coefficient" => 4,
"selected_tickers" => array(
"VZ" => 0.18,
"CSCO" => 0.16,
"CMCSA" => 0.14,
"T" => 0.12,
"CTL" => 0.1,
"CHTR" => 0.1,
"S" => 0.08,
"DISH" => 0.06,
"USM" => 0.04,
"VOD" => 0.02,
),
),
array(
"sector" => "Finance",
"directory" => "financial-services",
"sector_weight" => 0.1,
"sector_coefficient" => 6,
"selected_tickers" => array(
"JPM" => 0.18,
"GS" => 0.16,
"V" => 0.14,
"BAC" => 0.12,
"AXP" => 0.1,
"WFC" => 0.1,
"USB" => 0.08,
"PNC" => 0.06,
"AMG" => 0.04,
"AIG" => 0.02,
),
),
array(
"sector" => "Energy",
"directory" => "energy",
"sector_weight" => 0.1,
"sector_coefficient" => 6,
"selected_tickers" => array(
"CVX" => 0.18,
"XOM" => 0.16,
"APA" => 0.14,
"COP" => 0.12,
"BHGE" => 0.1,
"VLO" => 0.1,
"APC" => 0.08,
"ANDV" => 0.06,
"OXY" => 0.04,
"HAL" => 0.02,
),
),
array(
"sector" => "Industrials",
"directory" => "industrials",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"CAT" => 0.18,
"FLR" => 0.16,
"GE" => 0.14,
"JEC" => 0.12,
"JCI" => 0.1,
"MAS" => 0.1,
"FLS" => 0.08,
"AAL" => 0.06,
"AME" => 0.04,
"CHRW" => 0.02,
),
),
array(
"sector" => "Materials and Chemicals",
"directory" => "materials-and-chemicals",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"DWDP" => 0.18,
"APD" => 0.16,
"EMN" => 0.14,
"ECL" => 0.12,
"FMC" => 0.1,
"LYB" => 0.1,
"MOS" => 0.08,
"NEM" => 0.06,
"PPG" => 0.04,
"MLM" => 0.02,
),
),
array(
"sector" => "Utilities",
"directory" => "utilities",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"PPL" => 0.18,
"PCG" => 0.16,
"SO" => 0.14,
"WEC" => 0.12,
"PEG" => 0.1,
"XEL" => 0.1,
"D" => 0.08,
"NGG" => 0.06,
"NEE" => 0.04,
"PNW" => 0.02,
),
),
array(
"sector" => "Consumer Discretionary",
"directory" => "consumer-discretionary",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"DIS" => 0.18,
"HD" => 0.16,
"BBY" => 0.14,
"CBS" => 0.12,
"CMG" => 0.1,
"MCD" => 0.1,
"GPS" => 0.08,
"HOG" => 0.06,
"AZO" => 0.04,
"EXPE" => 0.02,
),
),
array(
"sector" => "Consumer Staples",
"directory" => "consumer-staples",
"sector_weight" => 0.06,
"sector_coefficient" => 8,
"selected_tickers" => array(
"PEP" => 0.18,
"PM" => 0.16,
"PG" => 0.14,
"MNST" => 0.12,
"TSN" => 0.1,
"CPB" => 0.1,
"HRL" => 0.08,
"SJM" => 0.06,
"CAG" => 0.04,
"KHC" => 0.02,
),
),
array(
"sector" => "Defense",
"directory" => "defense-and-aerospace",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"BA" => 0.18,
"LMT" => 0.16,
"UTX" => 0.14,
"NOC" => 0.12,
"HON" => 0.1,
"RTN" => 0.1,
"TXT" => 0.08,
"LLL" => 0.06,
"COL" => 0.04,
"GD" => 0.02,
),
),
array(
"sector" => "Health",
"directory" => "health-care-and-pharmaceuticals",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"UNH" => 0.18,
"JNJ" => 0.16,
"PFE" => 0.14,
"UHS" => 0.12,
"AET" => 0.1,
"RMD" => 0.1,
"TMO" => 0.08,
"MRK" => 0.06,
"ABT" => 0.04,
"LLY" => 0.02,
),
),
array(
"sector" => "Real Estate",
"directory" => "real-estate",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"CCI" => 0.18,
"AMT" => 0.16,
"AVB" => 0.14,
"HCP" => 0.12,
"RCL" => 0.1,
"HST" => 0.1,
"NCLH" => 0.08,
"HLT" => 0.06,
"ARE" => 0.04,
"AIV" => 0.02,
),
),
);
return $sector_movers;
I'd like to thank these users for being so helpful, which I could implement some of their advices in the code.
performance beginner php file-system api
$endgroup$
This question has an open bounty worth +50
reputation from Emma ending ending at 2019-04-05 23:34:46Z">in 7 days.
Looking for an answer drawing from credible and/or official sources.
1
$begingroup$
Could you let your IDE format the code consistently? That would free us from mentioning this boring topic in the code review.
$endgroup$
– Roland Illig
Mar 20 at 22:32
1
$begingroup$
I googled for "sublime format php" and found phpfmt, which looks promising. On that web page you just need to read the "Installation" and the "What does it do" sections. You can skip the long list of features if that looks frightening to you.
$endgroup$
– Roland Illig
Mar 21 at 6:06
1
$begingroup$
@Emma, Can you change the title to represent business requirement and not what you are doing. Ex: Creating Equity Dump?
$endgroup$
– 422_unprocessable_entity
Mar 21 at 16:10
1
$begingroup$
Please embed code directly in your question
$endgroup$
– Mike Brant
19 mins ago
$begingroup$
@MikeBrant Hi Mike, Thanks for your comment, I did try that though, couldn't, because of character limitation.
$endgroup$
– Emma
15 mins ago
|
show 1 more comment
$begingroup$
This is my first scripting project and I'm sure it has so many issues.
The main class, EQ
, scrapes equities data using EquityRecords
, calculates sector coefficients using
SectorMovers
, estimates equity prices, and finally writes an HTML string in the view files. I couldn't add EQ
class in the post due to character limitation of the posts. The entire code is on this GitHub link.
The only goal is making EQ
as fast/efficient as possible for a single server. Would you be so kind and review it and kindly help me to reach this goal?
EquityRecords
date_default_timezone_set("UTC");
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
set_time_limit(0);
// EquityRecords::allEquitiesSignleJSON(new EquityRecords());
class EquityRecords
const NUMBER_OF_STOCKS_PER_REQUEST = 100;
const NEW_LINE = "n";
/**
*
* @var a string of iextrading symbols
*/
const SYMBOLS_PATH = '/../../config/z-iextrading-symbs.md';
/**
*
* @var a string of our symbols json directory
*/
const SYMBOLS_DIR = "/../../blog-back/equities/real-time-60sec/z-raw-equilibrium-estimation";
/**
*
* @var a string of target path and query
*/
const TARGET_QUERY = "stock/market/batch?symbols=";
/**
*
* @var a string of iextrading base URL
*/
const BASE_URL = "https://api.iextrading.com/1.0/";
/**
*
* @var a string of iextrading end point
*/
const END_POINT = "&types=quote,chart&range=1m&last=10";
/**
*
* @var an integer for maximum number of stocks per URL on each call
*/
//***************** A ********************** //
// public static function getSymbols()
// return array_map(function($line) return str_getcsv($line, "t"); , file(__DIR__ . self::SYMBOLS_PATH));
//
public static function getSymbols()
//***************** START: ALL SYMBOLS ARRAY ********************** //
// var: is a filename path directory, where there is an md file with list of equities
$list_of_equities_file = __DIR__ . self::SYMBOLS_PATH;
// var: is content of md file with list of equities
$content_of_equities = file_get_contents($list_of_equities_file);
// var is an array(3) of equities such as: string(4) "ZYNE", string(10) "2019-01-04", string(27) "ZYNERBA PHARMACEUTICALS INC"
// $symbols_array=preg_split('/rn
public static function allEquitiesSignleJSON()
$equity_arrays = EquityRecords::getSymbols();
$base_url = self::BASE_URL . self::TARGET_QUERY;
$current_time = date("Y-m-d-H-i-s");
$all_equities = array();
// ticker: AAPL, GE, AMD
foreach ($equity_arrays as $ticker_arr)
$ticker = array_column($ticker_arr, 0);
$equity_url = $base_url . implode("%2C", $ticker) . self::END_POINT;
$raw_eauity_json = file_get_contents($equity_url);
$raw_equity_array = json_decode($raw_eauity_json, true);
$all_equities = array_merge($all_equities, $raw_equity_array);
$all_equities_json = json_encode($all_equities);
$symbols_dir = __DIR__ . self::SYMBOLS_DIR;
if (!is_dir($symbols_dir)) mkdir($symbols_dir, 0755, true);
$raw_equity_file = $symbols_dir . "/" . $current_time . ".json";
$fp = fopen($raw_equity_file, "x+");
fwrite($fp, $all_equities_json);
fclose($fp);
echo "YAAAY! Equity JSON file success at " . __METHOD__ . " ! 💚 " . self::NEW_LINE;
/**
* @return a string for var_dump
*/
public static function p()
$args = func_get_args();
$die = (end($args) === 1) && array_pop($args);
echo self::NEW_LINE;
foreach ($args as $v)
$output = print_r($v, true);
var_dump($output) . self::NEW_LINE;
echo self::NEW_LINE;
if ($die)
die();
SectorMovers
date_default_timezone_set("UTC");
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
set_time_limit(0);
require_once __DIR__ . "/EquityRecords.php";
SectorMovers::getSectors();
class SectorMovers
/**
*
* @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 = "/../../blog-back/sectors/real-time-60sec/z-raw-sector-";
/**
*
* @var a string for backend path for index sector
*/
const INDEX_SECTOR_DIR_PREFIX = "/../../blog-back/sectors/real-time-60sec/y-index/";
/**
*
* @var a string for live data path
*/
const LIVE_DATA_DIR = "/../../../public_html/blog/files/";
const DIR_FRONT_SECTOR_COEF_FILENAME = "s-1.txt"; // Filename that records sector coefficient JSON
public static function getSectors()
$base_url = self::BASE_URL . self::TARGET_QUERY;
$current_time = date("Y-m-d-H-i-s");
$permission = 0755;
$index_data = array("Overall" => array("sector_weight" => 1, "sector_coefficient" => 1, "sector_value" => 0));
$sector_movers = SectorMovers::iexSectorParams();
foreach ($sector_movers as $sector_mover)
// $sector_url = $base_url . implode(",", array_keys($sector_mover["selected_tickers"])) . "&types=quote&range=1m";
$sector_url = $base_url . implode("%2C", array_keys($sector_mover["selected_tickers"])) . "&types=quote&range=1m";
$rawSectorJson = file_get_contents($sector_url);
$raw_sector_array = json_decode($rawSectorJson, true);
// ******************* Back Data ***************** //
// Write the raw file in the back directories
// $rawSectorDir = __DIR__ . self::EACH_SECTOR_DIR_PREFIX . $sector_mover["directory"];
// // if back directory not exist
// if (!is_dir($rawSectorDir)) mkdir($rawSectorDir, $permission, true);
// // create and open/write/close sector data to back directories
// $rawSectorFile = $rawSectorDir . "/" . $current_time . ".json";
// $fp = fopen($rawSectorFile, "a+");
// fwrite($fp, $rawSectorJson);
// fclose($fp);
// ******************* End Back Data ***************** //
// Calculate the real-time index
$index_value = 0;
foreach ($raw_sector_array as $ticker => $ticker_stats)
if (isset($sector_mover["selected_tickers"][$ticker], $ticker_stats["quote"], $ticker_stats["quote"]["extendedChangePercent"], $ticker_stats["quote"]["changePercent"], $ticker_stats["quote"]["ytdChange"]))
$change_amount = ($ticker_stats["quote"]["extendedChangePercent"] + $ticker_stats["quote"]["changePercent"] + $ticker_stats["quote"]["ytdChange"]) / 200;
$index_value += $sector_mover["sector_weight"] * $sector_mover["selected_tickers"][$ticker] * $change_amount;
$index_data[$sector_mover["sector"]] = array("sector_weight" => $sector_mover["sector_weight"], "sector_coefficient" => $sector_mover["sector_coefficient"], "sector_value" => $index_value);
$index_data["Overall"]["sector_value"] += $index_data[$sector_mover["sector"]]["sector_value"];
// Calculate the index factor for better visibility between -1 and +1
$front_index_data = array();
foreach ($index_data as $sector_name => $sector_index_data)
// $index_sign = $sector_index_data["sector_value"];
// if ($index_sign < 0)
// $index_sign = - $index_sign;
//
$index_sign = abs($sector_index_data["sector_value"]);
$index_factor = 1;
for ($i = 0; $i <= 10; $i++)
$index_factor = pow(10, $i);
if (($index_factor * $index_sign) > 1)
$index_factor = pow(10, $i - 1);
break;
// $index_factor = 10 ** strlen(preg_match('~.K0+~', $float, $zeros) ? $zeros[0] : 0);
$front_index_data[$sector_name] = $sector_index_data["sector_weight"] * $sector_index_data["sector_coefficient"] * $sector_index_data["sector_value"] * $index_factor;
// Write the index file
$index_sector_dir = __DIR__ . self::INDEX_SECTOR_DIR_PREFIX;
if (!is_dir($index_sector_dir)) mkdir($index_sector_dir, $permission, true);
$index_sector_file = $index_sector_dir . $current_time . ".json";
$index_sector_json = json_encode($front_index_data, JSON_FORCE_OBJECT);
$fp = fopen($index_sector_file, "a+");
fwrite($fp, $index_sector_json);
fclose($fp);
$sector_dir = __DIR__ . self::LIVE_DATA_DIR;
if (!is_dir($sector_dir)) mkdir($sector_dir, $permission, true); // if data directory did not exist
// if s-1 file did not exist
if (!file_exists($sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME))
$handle = fopen($sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME, "wb");
fwrite($handle, "d");
fclose($handle);
$sector_coef_file = $sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME;
copy($index_sector_file, $sector_coef_file);
echo "YAAAY! " . __METHOD__ . " updated sector coefficients successfully 💚!n";
return $front_index_data;
public static function iexSectorParams()
$sector_movers = array(
array(
"sector" => "IT",
"directory" => "information-technology",
"sector_weight" => 0.18,
"sector_coefficient" => 4,
"selected_tickers" => array(
"AAPL" => 0.18,
"AMZN" => 0.16,
"GOOGL" => 0.14,
"IBM" => 0.2,
"MSFT" => 0.1,
"FB" => 0.1,
"NFLX" => 0.08,
"ADBE" => 0.06,
"CRM" => 0.04,
"NVDA" => 0.02,
),
),
array(
"sector" => "Telecommunication",
"directory" => "telecommunication-services",
"sector_weight" => 0.12,
"sector_coefficient" => 4,
"selected_tickers" => array(
"VZ" => 0.18,
"CSCO" => 0.16,
"CMCSA" => 0.14,
"T" => 0.12,
"CTL" => 0.1,
"CHTR" => 0.1,
"S" => 0.08,
"DISH" => 0.06,
"USM" => 0.04,
"VOD" => 0.02,
),
),
array(
"sector" => "Finance",
"directory" => "financial-services",
"sector_weight" => 0.1,
"sector_coefficient" => 6,
"selected_tickers" => array(
"JPM" => 0.18,
"GS" => 0.16,
"V" => 0.14,
"BAC" => 0.12,
"AXP" => 0.1,
"WFC" => 0.1,
"USB" => 0.08,
"PNC" => 0.06,
"AMG" => 0.04,
"AIG" => 0.02,
),
),
array(
"sector" => "Energy",
"directory" => "energy",
"sector_weight" => 0.1,
"sector_coefficient" => 6,
"selected_tickers" => array(
"CVX" => 0.18,
"XOM" => 0.16,
"APA" => 0.14,
"COP" => 0.12,
"BHGE" => 0.1,
"VLO" => 0.1,
"APC" => 0.08,
"ANDV" => 0.06,
"OXY" => 0.04,
"HAL" => 0.02,
),
),
array(
"sector" => "Industrials",
"directory" => "industrials",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"CAT" => 0.18,
"FLR" => 0.16,
"GE" => 0.14,
"JEC" => 0.12,
"JCI" => 0.1,
"MAS" => 0.1,
"FLS" => 0.08,
"AAL" => 0.06,
"AME" => 0.04,
"CHRW" => 0.02,
),
),
array(
"sector" => "Materials and Chemicals",
"directory" => "materials-and-chemicals",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"DWDP" => 0.18,
"APD" => 0.16,
"EMN" => 0.14,
"ECL" => 0.12,
"FMC" => 0.1,
"LYB" => 0.1,
"MOS" => 0.08,
"NEM" => 0.06,
"PPG" => 0.04,
"MLM" => 0.02,
),
),
array(
"sector" => "Utilities",
"directory" => "utilities",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"PPL" => 0.18,
"PCG" => 0.16,
"SO" => 0.14,
"WEC" => 0.12,
"PEG" => 0.1,
"XEL" => 0.1,
"D" => 0.08,
"NGG" => 0.06,
"NEE" => 0.04,
"PNW" => 0.02,
),
),
array(
"sector" => "Consumer Discretionary",
"directory" => "consumer-discretionary",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"DIS" => 0.18,
"HD" => 0.16,
"BBY" => 0.14,
"CBS" => 0.12,
"CMG" => 0.1,
"MCD" => 0.1,
"GPS" => 0.08,
"HOG" => 0.06,
"AZO" => 0.04,
"EXPE" => 0.02,
),
),
array(
"sector" => "Consumer Staples",
"directory" => "consumer-staples",
"sector_weight" => 0.06,
"sector_coefficient" => 8,
"selected_tickers" => array(
"PEP" => 0.18,
"PM" => 0.16,
"PG" => 0.14,
"MNST" => 0.12,
"TSN" => 0.1,
"CPB" => 0.1,
"HRL" => 0.08,
"SJM" => 0.06,
"CAG" => 0.04,
"KHC" => 0.02,
),
),
array(
"sector" => "Defense",
"directory" => "defense-and-aerospace",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"BA" => 0.18,
"LMT" => 0.16,
"UTX" => 0.14,
"NOC" => 0.12,
"HON" => 0.1,
"RTN" => 0.1,
"TXT" => 0.08,
"LLL" => 0.06,
"COL" => 0.04,
"GD" => 0.02,
),
),
array(
"sector" => "Health",
"directory" => "health-care-and-pharmaceuticals",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"UNH" => 0.18,
"JNJ" => 0.16,
"PFE" => 0.14,
"UHS" => 0.12,
"AET" => 0.1,
"RMD" => 0.1,
"TMO" => 0.08,
"MRK" => 0.06,
"ABT" => 0.04,
"LLY" => 0.02,
),
),
array(
"sector" => "Real Estate",
"directory" => "real-estate",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"CCI" => 0.18,
"AMT" => 0.16,
"AVB" => 0.14,
"HCP" => 0.12,
"RCL" => 0.1,
"HST" => 0.1,
"NCLH" => 0.08,
"HLT" => 0.06,
"ARE" => 0.04,
"AIV" => 0.02,
),
),
);
return $sector_movers;
I'd like to thank these users for being so helpful, which I could implement some of their advices in the code.
performance beginner php file-system api
$endgroup$
This is my first scripting project and I'm sure it has so many issues.
The main class, EQ
, scrapes equities data using EquityRecords
, calculates sector coefficients using
SectorMovers
, estimates equity prices, and finally writes an HTML string in the view files. I couldn't add EQ
class in the post due to character limitation of the posts. The entire code is on this GitHub link.
The only goal is making EQ
as fast/efficient as possible for a single server. Would you be so kind and review it and kindly help me to reach this goal?
EquityRecords
date_default_timezone_set("UTC");
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
set_time_limit(0);
// EquityRecords::allEquitiesSignleJSON(new EquityRecords());
class EquityRecords
const NUMBER_OF_STOCKS_PER_REQUEST = 100;
const NEW_LINE = "n";
/**
*
* @var a string of iextrading symbols
*/
const SYMBOLS_PATH = '/../../config/z-iextrading-symbs.md';
/**
*
* @var a string of our symbols json directory
*/
const SYMBOLS_DIR = "/../../blog-back/equities/real-time-60sec/z-raw-equilibrium-estimation";
/**
*
* @var a string of target path and query
*/
const TARGET_QUERY = "stock/market/batch?symbols=";
/**
*
* @var a string of iextrading base URL
*/
const BASE_URL = "https://api.iextrading.com/1.0/";
/**
*
* @var a string of iextrading end point
*/
const END_POINT = "&types=quote,chart&range=1m&last=10";
/**
*
* @var an integer for maximum number of stocks per URL on each call
*/
//***************** A ********************** //
// public static function getSymbols()
// return array_map(function($line) return str_getcsv($line, "t"); , file(__DIR__ . self::SYMBOLS_PATH));
//
public static function getSymbols()
//***************** START: ALL SYMBOLS ARRAY ********************** //
// var: is a filename path directory, where there is an md file with list of equities
$list_of_equities_file = __DIR__ . self::SYMBOLS_PATH;
// var: is content of md file with list of equities
$content_of_equities = file_get_contents($list_of_equities_file);
// var is an array(3) of equities such as: string(4) "ZYNE", string(10) "2019-01-04", string(27) "ZYNERBA PHARMACEUTICALS INC"
// $symbols_array=preg_split('/rn
public static function allEquitiesSignleJSON()
$equity_arrays = EquityRecords::getSymbols();
$base_url = self::BASE_URL . self::TARGET_QUERY;
$current_time = date("Y-m-d-H-i-s");
$all_equities = array();
// ticker: AAPL, GE, AMD
foreach ($equity_arrays as $ticker_arr)
$ticker = array_column($ticker_arr, 0);
$equity_url = $base_url . implode("%2C", $ticker) . self::END_POINT;
$raw_eauity_json = file_get_contents($equity_url);
$raw_equity_array = json_decode($raw_eauity_json, true);
$all_equities = array_merge($all_equities, $raw_equity_array);
$all_equities_json = json_encode($all_equities);
$symbols_dir = __DIR__ . self::SYMBOLS_DIR;
if (!is_dir($symbols_dir)) mkdir($symbols_dir, 0755, true);
$raw_equity_file = $symbols_dir . "/" . $current_time . ".json";
$fp = fopen($raw_equity_file, "x+");
fwrite($fp, $all_equities_json);
fclose($fp);
echo "YAAAY! Equity JSON file success at " . __METHOD__ . " ! 💚 " . self::NEW_LINE;
/**
* @return a string for var_dump
*/
public static function p()
$args = func_get_args();
$die = (end($args) === 1) && array_pop($args);
echo self::NEW_LINE;
foreach ($args as $v)
$output = print_r($v, true);
var_dump($output) . self::NEW_LINE;
echo self::NEW_LINE;
if ($die)
die();
SectorMovers
date_default_timezone_set("UTC");
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
set_time_limit(0);
require_once __DIR__ . "/EquityRecords.php";
SectorMovers::getSectors();
class SectorMovers
/**
*
* @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 = "/../../blog-back/sectors/real-time-60sec/z-raw-sector-";
/**
*
* @var a string for backend path for index sector
*/
const INDEX_SECTOR_DIR_PREFIX = "/../../blog-back/sectors/real-time-60sec/y-index/";
/**
*
* @var a string for live data path
*/
const LIVE_DATA_DIR = "/../../../public_html/blog/files/";
const DIR_FRONT_SECTOR_COEF_FILENAME = "s-1.txt"; // Filename that records sector coefficient JSON
public static function getSectors()
$base_url = self::BASE_URL . self::TARGET_QUERY;
$current_time = date("Y-m-d-H-i-s");
$permission = 0755;
$index_data = array("Overall" => array("sector_weight" => 1, "sector_coefficient" => 1, "sector_value" => 0));
$sector_movers = SectorMovers::iexSectorParams();
foreach ($sector_movers as $sector_mover)
// $sector_url = $base_url . implode(",", array_keys($sector_mover["selected_tickers"])) . "&types=quote&range=1m";
$sector_url = $base_url . implode("%2C", array_keys($sector_mover["selected_tickers"])) . "&types=quote&range=1m";
$rawSectorJson = file_get_contents($sector_url);
$raw_sector_array = json_decode($rawSectorJson, true);
// ******************* Back Data ***************** //
// Write the raw file in the back directories
// $rawSectorDir = __DIR__ . self::EACH_SECTOR_DIR_PREFIX . $sector_mover["directory"];
// // if back directory not exist
// if (!is_dir($rawSectorDir)) mkdir($rawSectorDir, $permission, true);
// // create and open/write/close sector data to back directories
// $rawSectorFile = $rawSectorDir . "/" . $current_time . ".json";
// $fp = fopen($rawSectorFile, "a+");
// fwrite($fp, $rawSectorJson);
// fclose($fp);
// ******************* End Back Data ***************** //
// Calculate the real-time index
$index_value = 0;
foreach ($raw_sector_array as $ticker => $ticker_stats)
if (isset($sector_mover["selected_tickers"][$ticker], $ticker_stats["quote"], $ticker_stats["quote"]["extendedChangePercent"], $ticker_stats["quote"]["changePercent"], $ticker_stats["quote"]["ytdChange"]))
$change_amount = ($ticker_stats["quote"]["extendedChangePercent"] + $ticker_stats["quote"]["changePercent"] + $ticker_stats["quote"]["ytdChange"]) / 200;
$index_value += $sector_mover["sector_weight"] * $sector_mover["selected_tickers"][$ticker] * $change_amount;
$index_data[$sector_mover["sector"]] = array("sector_weight" => $sector_mover["sector_weight"], "sector_coefficient" => $sector_mover["sector_coefficient"], "sector_value" => $index_value);
$index_data["Overall"]["sector_value"] += $index_data[$sector_mover["sector"]]["sector_value"];
// Calculate the index factor for better visibility between -1 and +1
$front_index_data = array();
foreach ($index_data as $sector_name => $sector_index_data)
// $index_sign = $sector_index_data["sector_value"];
// if ($index_sign < 0)
// $index_sign = - $index_sign;
//
$index_sign = abs($sector_index_data["sector_value"]);
$index_factor = 1;
for ($i = 0; $i <= 10; $i++)
$index_factor = pow(10, $i);
if (($index_factor * $index_sign) > 1)
$index_factor = pow(10, $i - 1);
break;
// $index_factor = 10 ** strlen(preg_match('~.K0+~', $float, $zeros) ? $zeros[0] : 0);
$front_index_data[$sector_name] = $sector_index_data["sector_weight"] * $sector_index_data["sector_coefficient"] * $sector_index_data["sector_value"] * $index_factor;
// Write the index file
$index_sector_dir = __DIR__ . self::INDEX_SECTOR_DIR_PREFIX;
if (!is_dir($index_sector_dir)) mkdir($index_sector_dir, $permission, true);
$index_sector_file = $index_sector_dir . $current_time . ".json";
$index_sector_json = json_encode($front_index_data, JSON_FORCE_OBJECT);
$fp = fopen($index_sector_file, "a+");
fwrite($fp, $index_sector_json);
fclose($fp);
$sector_dir = __DIR__ . self::LIVE_DATA_DIR;
if (!is_dir($sector_dir)) mkdir($sector_dir, $permission, true); // if data directory did not exist
// if s-1 file did not exist
if (!file_exists($sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME))
$handle = fopen($sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME, "wb");
fwrite($handle, "d");
fclose($handle);
$sector_coef_file = $sector_dir . self::DIR_FRONT_SECTOR_COEF_FILENAME;
copy($index_sector_file, $sector_coef_file);
echo "YAAAY! " . __METHOD__ . " updated sector coefficients successfully 💚!n";
return $front_index_data;
public static function iexSectorParams()
$sector_movers = array(
array(
"sector" => "IT",
"directory" => "information-technology",
"sector_weight" => 0.18,
"sector_coefficient" => 4,
"selected_tickers" => array(
"AAPL" => 0.18,
"AMZN" => 0.16,
"GOOGL" => 0.14,
"IBM" => 0.2,
"MSFT" => 0.1,
"FB" => 0.1,
"NFLX" => 0.08,
"ADBE" => 0.06,
"CRM" => 0.04,
"NVDA" => 0.02,
),
),
array(
"sector" => "Telecommunication",
"directory" => "telecommunication-services",
"sector_weight" => 0.12,
"sector_coefficient" => 4,
"selected_tickers" => array(
"VZ" => 0.18,
"CSCO" => 0.16,
"CMCSA" => 0.14,
"T" => 0.12,
"CTL" => 0.1,
"CHTR" => 0.1,
"S" => 0.08,
"DISH" => 0.06,
"USM" => 0.04,
"VOD" => 0.02,
),
),
array(
"sector" => "Finance",
"directory" => "financial-services",
"sector_weight" => 0.1,
"sector_coefficient" => 6,
"selected_tickers" => array(
"JPM" => 0.18,
"GS" => 0.16,
"V" => 0.14,
"BAC" => 0.12,
"AXP" => 0.1,
"WFC" => 0.1,
"USB" => 0.08,
"PNC" => 0.06,
"AMG" => 0.04,
"AIG" => 0.02,
),
),
array(
"sector" => "Energy",
"directory" => "energy",
"sector_weight" => 0.1,
"sector_coefficient" => 6,
"selected_tickers" => array(
"CVX" => 0.18,
"XOM" => 0.16,
"APA" => 0.14,
"COP" => 0.12,
"BHGE" => 0.1,
"VLO" => 0.1,
"APC" => 0.08,
"ANDV" => 0.06,
"OXY" => 0.04,
"HAL" => 0.02,
),
),
array(
"sector" => "Industrials",
"directory" => "industrials",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"CAT" => 0.18,
"FLR" => 0.16,
"GE" => 0.14,
"JEC" => 0.12,
"JCI" => 0.1,
"MAS" => 0.1,
"FLS" => 0.08,
"AAL" => 0.06,
"AME" => 0.04,
"CHRW" => 0.02,
),
),
array(
"sector" => "Materials and Chemicals",
"directory" => "materials-and-chemicals",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"DWDP" => 0.18,
"APD" => 0.16,
"EMN" => 0.14,
"ECL" => 0.12,
"FMC" => 0.1,
"LYB" => 0.1,
"MOS" => 0.08,
"NEM" => 0.06,
"PPG" => 0.04,
"MLM" => 0.02,
),
),
array(
"sector" => "Utilities",
"directory" => "utilities",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"PPL" => 0.18,
"PCG" => 0.16,
"SO" => 0.14,
"WEC" => 0.12,
"PEG" => 0.1,
"XEL" => 0.1,
"D" => 0.08,
"NGG" => 0.06,
"NEE" => 0.04,
"PNW" => 0.02,
),
),
array(
"sector" => "Consumer Discretionary",
"directory" => "consumer-discretionary",
"sector_weight" => 0.08,
"sector_coefficient" => 8,
"selected_tickers" => array(
"DIS" => 0.18,
"HD" => 0.16,
"BBY" => 0.14,
"CBS" => 0.12,
"CMG" => 0.1,
"MCD" => 0.1,
"GPS" => 0.08,
"HOG" => 0.06,
"AZO" => 0.04,
"EXPE" => 0.02,
),
),
array(
"sector" => "Consumer Staples",
"directory" => "consumer-staples",
"sector_weight" => 0.06,
"sector_coefficient" => 8,
"selected_tickers" => array(
"PEP" => 0.18,
"PM" => 0.16,
"PG" => 0.14,
"MNST" => 0.12,
"TSN" => 0.1,
"CPB" => 0.1,
"HRL" => 0.08,
"SJM" => 0.06,
"CAG" => 0.04,
"KHC" => 0.02,
),
),
array(
"sector" => "Defense",
"directory" => "defense-and-aerospace",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"BA" => 0.18,
"LMT" => 0.16,
"UTX" => 0.14,
"NOC" => 0.12,
"HON" => 0.1,
"RTN" => 0.1,
"TXT" => 0.08,
"LLL" => 0.06,
"COL" => 0.04,
"GD" => 0.02,
),
),
array(
"sector" => "Health",
"directory" => "health-care-and-pharmaceuticals",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"UNH" => 0.18,
"JNJ" => 0.16,
"PFE" => 0.14,
"UHS" => 0.12,
"AET" => 0.1,
"RMD" => 0.1,
"TMO" => 0.08,
"MRK" => 0.06,
"ABT" => 0.04,
"LLY" => 0.02,
),
),
array(
"sector" => "Real Estate",
"directory" => "real-estate",
"sector_weight" => 0.04,
"sector_coefficient" => 10,
"selected_tickers" => array(
"CCI" => 0.18,
"AMT" => 0.16,
"AVB" => 0.14,
"HCP" => 0.12,
"RCL" => 0.1,
"HST" => 0.1,
"NCLH" => 0.08,
"HLT" => 0.06,
"ARE" => 0.04,
"AIV" => 0.02,
),
),
);
return $sector_movers;
I'd like to thank these users for being so helpful, which I could implement some of their advices in the code.
performance beginner php file-system api
performance beginner php file-system api
edited 12 mins ago
Emma
asked Mar 16 at 5:50
EmmaEmma
150213
150213
This question has an open bounty worth +50
reputation from Emma ending ending at 2019-04-05 23:34:46Z">in 7 days.
Looking for an answer drawing from credible and/or official sources.
This question has an open bounty worth +50
reputation from Emma ending ending at 2019-04-05 23:34:46Z">in 7 days.
Looking for an answer drawing from credible and/or official sources.
1
$begingroup$
Could you let your IDE format the code consistently? That would free us from mentioning this boring topic in the code review.
$endgroup$
– Roland Illig
Mar 20 at 22:32
1
$begingroup$
I googled for "sublime format php" and found phpfmt, which looks promising. On that web page you just need to read the "Installation" and the "What does it do" sections. You can skip the long list of features if that looks frightening to you.
$endgroup$
– Roland Illig
Mar 21 at 6:06
1
$begingroup$
@Emma, Can you change the title to represent business requirement and not what you are doing. Ex: Creating Equity Dump?
$endgroup$
– 422_unprocessable_entity
Mar 21 at 16:10
1
$begingroup$
Please embed code directly in your question
$endgroup$
– Mike Brant
19 mins ago
$begingroup$
@MikeBrant Hi Mike, Thanks for your comment, I did try that though, couldn't, because of character limitation.
$endgroup$
– Emma
15 mins ago
|
show 1 more comment
1
$begingroup$
Could you let your IDE format the code consistently? That would free us from mentioning this boring topic in the code review.
$endgroup$
– Roland Illig
Mar 20 at 22:32
1
$begingroup$
I googled for "sublime format php" and found phpfmt, which looks promising. On that web page you just need to read the "Installation" and the "What does it do" sections. You can skip the long list of features if that looks frightening to you.
$endgroup$
– Roland Illig
Mar 21 at 6:06
1
$begingroup$
@Emma, Can you change the title to represent business requirement and not what you are doing. Ex: Creating Equity Dump?
$endgroup$
– 422_unprocessable_entity
Mar 21 at 16:10
1
$begingroup$
Please embed code directly in your question
$endgroup$
– Mike Brant
19 mins ago
$begingroup$
@MikeBrant Hi Mike, Thanks for your comment, I did try that though, couldn't, because of character limitation.
$endgroup$
– Emma
15 mins ago
1
1
$begingroup$
Could you let your IDE format the code consistently? That would free us from mentioning this boring topic in the code review.
$endgroup$
– Roland Illig
Mar 20 at 22:32
$begingroup$
Could you let your IDE format the code consistently? That would free us from mentioning this boring topic in the code review.
$endgroup$
– Roland Illig
Mar 20 at 22:32
1
1
$begingroup$
I googled for "sublime format php" and found phpfmt, which looks promising. On that web page you just need to read the "Installation" and the "What does it do" sections. You can skip the long list of features if that looks frightening to you.
$endgroup$
– Roland Illig
Mar 21 at 6:06
$begingroup$
I googled for "sublime format php" and found phpfmt, which looks promising. On that web page you just need to read the "Installation" and the "What does it do" sections. You can skip the long list of features if that looks frightening to you.
$endgroup$
– Roland Illig
Mar 21 at 6:06
1
1
$begingroup$
@Emma, Can you change the title to represent business requirement and not what you are doing. Ex: Creating Equity Dump?
$endgroup$
– 422_unprocessable_entity
Mar 21 at 16:10
$begingroup$
@Emma, Can you change the title to represent business requirement and not what you are doing. Ex: Creating Equity Dump?
$endgroup$
– 422_unprocessable_entity
Mar 21 at 16:10
1
1
$begingroup$
Please embed code directly in your question
$endgroup$
– Mike Brant
19 mins ago
$begingroup$
Please embed code directly in your question
$endgroup$
– Mike Brant
19 mins ago
$begingroup$
@MikeBrant Hi Mike, Thanks for your comment, I did try that though, couldn't, because of character limitation.
$endgroup$
– Emma
15 mins ago
$begingroup$
@MikeBrant Hi Mike, Thanks for your comment, I did try that though, couldn't, because of character limitation.
$endgroup$
– Emma
15 mins ago
|
show 1 more comment
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215553%2fequity-data-processing-fast-and-or-efficient-file-writing-using-php%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215553%2fequity-data-processing-fast-and-or-efficient-file-writing-using-php%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
$begingroup$
Could you let your IDE format the code consistently? That would free us from mentioning this boring topic in the code review.
$endgroup$
– Roland Illig
Mar 20 at 22:32
1
$begingroup$
I googled for "sublime format php" and found phpfmt, which looks promising. On that web page you just need to read the "Installation" and the "What does it do" sections. You can skip the long list of features if that looks frightening to you.
$endgroup$
– Roland Illig
Mar 21 at 6:06
1
$begingroup$
@Emma, Can you change the title to represent business requirement and not what you are doing. Ex: Creating Equity Dump?
$endgroup$
– 422_unprocessable_entity
Mar 21 at 16:10
1
$begingroup$
Please embed code directly in your question
$endgroup$
– Mike Brant
19 mins ago
$begingroup$
@MikeBrant Hi Mike, Thanks for your comment, I did try that though, couldn't, because of character limitation.
$endgroup$
– Emma
15 mins ago