public function get_ohlc($pair, $start, $end, $bid_offer, $timeslice, $lag = 0) { //to seconds factor lookup $this->to_seconds_factor['s'] = 1; $this->to_seconds_factor['m'] = 60; $this->to_seconds_factor['h'] = 60 * 60; $this->to_seconds_factor['d'] = 60 * 60 * 24; //predis class require 'Predis/lib/Predis/Autoloader.php'; Predis\Autoloader::register(); // set some utility variables $range = $end - $start; $startTime = gmstrftime('%Y-%m-%d %H:%M:%S', $start / 1000); $endTime = gmstrftime('%Y-%m-%d %H:%M:%S', $end / 1000); //set suggested timeslice depending on range //[1s,5s,10s,20s,30s,1m,5m,10m,20m,30m,1h,2h,3h,6h,1d,1w,1M] if (!isset($timeslice)) { $stick_threshold = 300; //on screen count threshold //seconds if ($range < $stick_threshold * 1000) { $timeslice = '1s'; } elseif ($range < $stick_threshold * 5 * 1000) { $timeslice = '5s'; } elseif ($range < $stick_threshold * 10 * 1000) { $timeslice = '10s'; } elseif ($range < $stick_threshold * 20 * 1000) { $timeslice = '20s'; } elseif ($range < $stick_threshold * 30 * 1000) { $timeslice = '30s'; //minutes } elseif ($range < $stick_threshold * 60 * 1000) { $timeslice = '1m'; } elseif ($range < $stick_threshold * 60 * 5 * 1000) { $timeslice = '5m'; } elseif ($range < $stick_threshold * 60 * 10 * 1000) { $timeslice = '10m'; } elseif ($range < $stick_threshold * 60 * 20 * 1000) { $timeslice = '20m'; } elseif ($range < $stick_threshold * 60 * 30 * 1000) { $timeslice = '30m'; //hour } elseif ($range < $stick_threshold * 60 * 60 * 1000) { $timeslice = '1h'; } elseif ($range < $stick_threshold * 60 * 60 * 2 * 1000) { $timeslice = '2h'; } elseif ($range < $stick_threshold * 60 * 60 * 3 * 1000) { $timeslice = '3h'; } elseif ($range < $stick_threshold * 60 * 60 * 6 * 1000) { $timeslice = '6h'; //day } elseif ($range < $stick_threshold * 60 * 60 * 24 * 1000) { $timeslice = '1d'; //week } elseif ($range < $stick_threshold * 60 * 60 * 7 * 1000) { $timeslice = '1w'; //month } else { $timeslice = '1M'; } } //validate and parse timeslice if (!preg_match('/^(?P<dur>\\d+)(?P<len>s|m|h|d|w|M)$/', $timeslice, $matches)) { die("Invalid timeslice parameter: {$timeslice}"); } //timeslice duration (1,2,3,4,..) $ts_duration = $matches['dur']; //timeslice length (s,m,h,d,w,M) $ts_len = $matches['len']; //check durations modulus of parent //60 seconds in a minute and 60 minute in an hour if (in_array($ts_len, array('s', 'm'))) { if (60 % $ts_duration != 0) { //not evenly divisable by 60 die("Invalid 1timeslice parameter: {$ts_duration}{$ts_len}"); } } //24 hours in day if (in_array($ts_len, array('h'))) { if (24 % $ts_duration != 0) { //not evenly divisable by 24 die("Invalid timeslice parameter: {$ts_duration}{$ts_len}"); } } //max 2 day|week|month if (in_array($ts_len, array('d', 'w', 'M'))) { if ($ts_duration > 2) { //not valid duration die("Invalid timeslice parameter: {$ts_duration}{$ts_len}"); } } //check are not cacheing week and month $is_caching = in_array($ts_len, array('w', 'M')) ? false : true; //add lag if ($lag > 0) { if (!in_array($ts_len, array('w', 'M'))) { //to sec $sec = $this->to_seconds_factor[$ts_len] * $ts_duration; //lag start $start = (ceil($start / 1000 / $sec) * $sec - $sec * $lag) * 1000; } else { if ($ts_len == 'w') { $date = date_create_from_format('U', $start); date_sub($date, date_interval_create_from_date_string("{$lag} weeks")); } if ($ts_len == 'M') { $date = date_create_from_format('U', $start); date_sub($date, date_interval_create_from_date_string("{$lag} months")); } //save new start $start = date_format($date, 'U') * 1000; } } //if no week or Month, round start/end time to match candle length if (!in_array($ts_len, array('w', 'M'))) { //to sec $sec = $this->to_seconds_factor[$ts_len] * $ts_duration; //round up start time, round down end time $startTime = date('Y-m-d H:i:s', ceil($start / 1000 / $sec) * $sec); //round down end time $endTime = date('Y-m-d H:i:s', floor($end / 1000 / $sec) * $sec + $sec - 1); } //init vars $time_slices = array(); $result = array(); $ranges = array(); $cache = array(); $time_slices = array(); $empty_time_slices = array(); $is_cache_miss = false; $redis = new Predis\Client(); $redis_zset = "{$pair}:{$bid_offer}:{$timeslice}"; //query redis cache if ($is_caching) { //get list of expected time slices $time_slices = $this->buildTimeSlices($ts_len, $ts_duration, $start / 1000, $end / 1000); //check redis cache $return = $redis->zrangebyscore($redis_zset, $start / 1000, $end / 1000, array('withscores' => true)); //build cache datastructure foreach ($return as $row) { $cache[$row[1]] = $row[0]; } //get empty timeslices $return = $redis->zrangebyscore('empty:' . $redis_zset, $start / 1000, $end / 1000, array('withscores' => true)); //remove empty timeslices from time_slices foreach ($return as $row) { unset($time_slices[$row[1]]); } //find missing slices $cache_miss_slices = array_diff_key($time_slices, $cache); //check for cache misses if (count($cache_miss_slices) > 0) { $is_cache_miss = true; $redis->incr('cache_misses'); //get ranges for missing slices $ranges = $this->buildRangeFromMissingTimeSlices($ts_len, $ts_duration, $cache_miss_slices); } else { $redis->incr('cache_hits'); $results = $cache; } } else { //build full range $ranges[] = array('startTime' => $startTime, 'endTime' => $endTime); } //hit database if we are not caching or if we have a cache miss if (!$is_caching || $is_cache_miss) { //db $return = $this->fetchMySQLAndCache($ts_len, $ts_duration, $is_cache_miss ? $cache_miss_slices : $time_slices, $ranges, $is_caching, $redis, $redis_zset, $pair, $bid_offer); //add missing slices to cache obj if ($is_cache_miss) { foreach ($return as $row) { extract($row); $cache[$datetime] = "[{$datetime},{$open},{$high},{$low},{$close},{$vol}]"; } //sort by key (timestamp) ksort($cache); $results = $cache; } else { //build non cached results results foreach ($return as $row) { extract($row); $results[] = "[{$datetime},{$open},{$high},{$low},{$close},{$vol}]"; } } } return $results; }