[
  {
    "path": ".gitignore",
    "content": "gitpush.sh\r\n*.tar\r\n*.gz\r\ncli/config.sh\r\nsyncAll/config.sh\r\nuser.config.php\r\n"
  },
  {
    "path": "cli/config.sample.sh",
    "content": "#!/bin/bash\r\n\r\n# Gekko directory\r\n# Relative -or- absolute to where you have gekko tools installed\r\n\r\ngekkoDir='../../gekko'\r\n"
  },
  {
    "path": "cli/download.sh",
    "content": "#!/bin/bash\nclear\n\n# ------------------------------\n#\n# Download sync files @ lastupload/last.txt\n# ...and replace files in Gekko\n#\n# -------------------------------\n\n# imports\nsource imports/common.sh\nsource imports/colorizer.sh\n\n# check config.sh\nif [ ! -f config.sh ]; then\n    colorize \"<light-red>ERROR</light-red> config.sh could not be found, copy config.sample.sh and modify to suit your needs!\\n\"\n    return\nfi\n\n# check if last.txt file exist\nif [ ! -f lastupload/last.txt ]; then\n    colorize \"<light-red>ERROR</light-red> last.txt could not be found under lastupload so there's nothing to download.\\n\"\n    return\nfi\n\n# import user config\nsource config.sh\n\noutputDir=\"lastupload\"\nfile=$(cat $outputDir/last.txt)\nsaveFile=\"$outputDir/sync.tar.gz\";\n\n# get $filename function\nget() {\n\tif [ $# -eq 0 ];\n\t\tthen echo -e \"No arguments specified.\";\n\t\treturn 1;\n\tfi\n\techo\n\techo\n\tcurl \"$1\" >> $saveFile;\n}\n\n# get the file\ncolorize \"Downloading <light-yellow>$file</light-yellow>, please stand by....\"\nget $file\n\nprintf \"\\n\\n\"\n\n# untar\ncolorize \"Unpacking <light-yellow>$saveFile</light-yellow>...\\n\"\ntar -xvzf $saveFile\necho\n\n# sync new > old (and replace)\ncolorize \"Replacing old with new @ <light-yellow>$gekkoDir/history</light-yellow>\\n\"\nrsync -ah gekko/* $gekkoDir\necho\n\n# remove crap\nrm -rf gekko\nrm -rf \"$outputDir/sync.tar.gz\"\n\ncolorize \"<light-green>Completed.</light-green>\\n\"\n"
  },
  {
    "path": "cli/imports/Compatibility/bash/array.bash",
    "content": "####\n# Copyright (c) 2012, Jakob Westhoff <jakob@qafoo.com>\n# \n# All rights reserved.\n# \n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n# \n#  - Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#  - Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n# \n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n####\n#\n# This file does include a generic compatibility layer for accessing arrays in\n# bash.\n#\n####\n\n##\n# Define a variable as an empty array\n#\n# The variable should already exist in the scope you want to use it in\n#\n# @param name\n##\nARRAY_define() {\n    local name=\"${1}\"\n\n    eval \"${name}=()\"\n}\n\n##\n# Get the current amount of entries inside an existing array\n#\n# @param name\n##\nARRAY_count() {\n    local name=\"${1}\"\n\n    if [ -z \"$(set +u; eval \"echo \\\"\\${${name}}\\\"\")\" ]; then\n        echo \"0\"\n    else\n        eval \"echo \\\"\\${#${name}[@]}\\\"\"\n    fi\n}\n\n##\n# Get a certain index stored inside an array\n#\n# The index is zero based. The first entry entry is therefore supposed to be 0.\n# If the used shell does only provide 1-based arrays this needs to be mapped\n# inside of this function accordingly\n#\n# @param name\n# @param index\n##\nARRAY_get() {\n    local name=\"${1}\"\n    local index=\"${2}\"\n\n    eval \"echo \\\"\\${${name}[${index}]}\\\"\"\n}\n\n##\n# Set a certain value stored to a certain index inside an array\n#\n# The index is zero based. The first entry entry is therefore supposed to be 0.\n# If the used shell does only provide 1-based arrays this needs to be mapped\n# inside of this function accordingly\n#\n# @param name\n# @param index\n# @param value\n##\nARRAY_set() {\n    local name=\"${1}\"\n    local index=\"${2}\"\n    local value=\"${3}\"\n\n    eval \"${name}[${index}]=\\\"${value}\\\"\"\n}\n\n##\n# Push a given value to the end of the provided array name\n#\n# @param name\n# @param value\n##\nARRAY_push() {\n    local name=\"${1}\"\n    local value=\"${2}\"\n\n    eval \"${name}[\\${#${name}[@]}]=\\\"${value}\\\"\"\n}\n\n##\n# Peek at a value from the end of the provided array name without removing it\n#\n# @param name\n##\nARRAY_peek() {\n    local name=\"${1}\"\n\n    eval \"echo \\\"\\${${name}[\\${#${name}[@]}-1]}\\\"\"\n}\n\n##\n# Pop a value from the end of the provided array name\n#\n# @param name\n##\nARRAY_pop() {\n    local name=\"${1}\"\n\n    echo \"$(ARRAY_peek \"${name}\")\"\n    eval \"unset \\\"${name}[\\${#${name}[@]}-1]\\\"\"\n}\n\n##\n# Unset a certain index from the given array\n#\n# @param name\n##\nARRAY_unset() {\n    local name=\"${1}\"\n    local index=\"${2}\"\n\n    eval \"unset ${name}[${index}]\"\n}\n"
  },
  {
    "path": "cli/imports/Compatibility/compatibility.sh",
    "content": "####\n# Copyright (c) 2012, Jakob Westhoff <jakob@qafoo.com>\n# \n# All rights reserved.\n# \n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n# \n#  - Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#  - Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n# \n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n####\n#\n# This file does include generic entrypoint to the compatibility library, which\n# loads the compatibility layer for your currently used shell\n#\n####\n\nif [ -n \"${BASH_VERSION}\" ]; then\n    source \"$( cd \"$( dirname \"${BASH_SOURCE}\" )\" && pwd )/bash/\"*.bash\nelif [ -n \"${ZSH_VERSION}\" ]; then\n    source \"$( cd \"$( dirname \"${0}\" )\" && pwd )/zsh/\"*.zsh\nelse\n    source \"${COLORIZE_SH_SOURCE_DIR}/Compatibility/default/\"*.sh\nfi\n"
  },
  {
    "path": "cli/imports/Compatibility/default/array.sh",
    "content": "####\n# Copyright (c) 2013, Florian Sowade <f.sowade@r9e.de>\n#\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n#\n#  - Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#  - Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n####\n#\n# This file does include a generic compatibility layer for accessing arrays in\n# any POSIX shell.\n#\n####\n\n##\n# Define a variable as an empty array\n#\n# The variable should already exist in the scope you want to use it in\n#\n# @param name\n##\nARRAY_define() {\n    local name=\"${1}\"\n\n    eval \"${name}_COUNT=0\"\n}\n\n##\n# Get the current amount of entries inside an existing array\n#\n# @param name\n##\nARRAY_count() {\n    local name=\"${1}\"\n\n    eval \"echo \\\"\\${${name}_COUNT}\\\"\"\n}\n\n##\n# Push a given value to the end of the provided array name\n#\n# @param name\n# @param value\n##\nARRAY_push() {\n    local name=\"${1}\"\n    local value=\"${2}\"\n\n    local index=\"$(ARRAY_count \"${name}\")\"\n\n    eval \"${name}_${index}='${value}'\"\n    eval \"${name}_COUNT=$(( ${index} + 1 ))\"\n}\n\n##\n# Peek at a value from the end of the provided array name without removing it\n#\n# @param name\n##\nARRAY_peek() {\n    local name=\"${1}\"\n\n    local index=\"$(( $(ARRAY_count \"${name}\") - 1 ))\"\n    eval \"echo \\\"\\${${name}_${index}}\\\"\"\n}\n\n##\n# Pop a value from the end of the provided array name\n#\n# @param name\n##\nARRAY_pop() {\n    local name=\"${1}\"\n\n    local index=\"$(( $(ARRAY_count \"${name}\") - 1 ))\"\n    eval \"echo \\\"\\${${name}_${index}}\\\"\"\n    eval \"${name}_COUNT=${index}\"\n    eval \"unset ${name}_${index}\"\n}\n"
  },
  {
    "path": "cli/imports/Compatibility/zsh/array.zsh",
    "content": "####\n# Copyright (c) 2012, Jakob Westhoff <jakob@qafoo.com>\n# \n# All rights reserved.\n# \n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n# \n#  - Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#  - Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n# \n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n####\n#\n# This file does include a generic compatibility layer for accessing arrays in\n# zsh.\n#\n####\n\n##\n# Define a variable as an empty array\n#\n# The variable should already exist in the scope you want to use it in\n#\n# @param name\n##\nARRAY_define() {\n    local name=\"${1}\"\n\n    eval \"${name}=()\"\n}\n\n##\n# Get the current amount of entries inside an existing array\n#\n# @param name\n##\nARRAY_count() {\n    local name=\"${1}\"\n\n    if [ -z \"$(eval \"echo \\\"\\${${name}}\\\"\")\" ]; then\n        echo \"0\"\n    else\n        eval \"echo \\\"\\${#${name}[@]}\\\"\"\n    fi\n}\n\n##\n# Get a certain index stored inside an array\n#\n# The index is zero based. The first entry entry is therefore supposed to be 0.\n# If the used shell does only provide 1-based arrays this needs to be mapped\n# inside of this function accordingly\n#\n# @param name\n# @param index\n##\nARRAY_get() {\n    local name=\"${1}\"\n    local index=\"$((${2}+1))\"\n\n    eval \"echo \\\"\\${${name}[${index}]}\\\"\"\n}\n\n##\n# Set a certain value stored to a certain index inside an array\n#\n# The index is zero based. The first entry entry is therefore supposed to be 0.\n# If the used shell does only provide 1-based arrays this needs to be mapped\n# inside of this function accordingly\n#\n# @param name\n# @param index\n# @param value\n##\nARRAY_set() {\n    local name=\"${1}\"\n    local index=\"$((${2}+1))\"\n    local value=\"${3}\"\n\n    eval \"${name}[${index}]=\\\"${value}\\\"\"\n}\n\n##\n# Push a given value to the end of the provided array name\n#\n# @param name\n# @param value\n##\nARRAY_push() {\n    local name=\"${1}\"\n    local value=\"${2}\"\n    \n    local index=\"$(($(eval \"echo \\${#${name}[@]}\")+1))\"\n\n    eval \"${name}[${index}]=\\\"${value}\\\"\"\n}\n\n##\n# Peek at a value from the end of the provided array name without removing it\n#\n# @param name\n##\nARRAY_peek() {\n    local name=\"${1}\"\n\n    eval \"echo \\\"\\${${name}[\\${#${name}[@]}]}\\\"\"\n}\n\n##\n# Pop a value from the end of the provided array name\n#\n# @param name\n##\nARRAY_pop() {\n    local name=\"${1}\"\n\n    local index=\"$(eval \"echo \\${#${name}[@]}\")\"\n\n    echo \"$(ARRAY_peek \"${name}\")\"\n    ARRAY_unset \"${name}\" \"$((${index}-1))\"\n}\n\n##\n# Unset a certain index from the given array\n#\n# @param name\n##\nARRAY_unset() {\n    local name=\"${1}\"\n    local index=\"$((${2}+1))\"\n\n    eval \"${name}[${index}]=()\"\n}\n"
  },
  {
    "path": "cli/imports/colorizer.sh",
    "content": "####\n# Copyright (c) 2012, Jakob Westhoff <jakob@qafoo.com>\n# \n# All rights reserved.\n# \n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n# \n#  - Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#  - Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n# \n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n####\n\nsource \"${COLORIZE_SH_SOURCE_DIR:-$( cd \"$( dirname \"${BASH_SOURCE:-${0}}\" )\" && pwd )}/Compatibility/compatibility.sh\"\n\n# Escape codes\nCOLORIZER_START=${COLORIZER_START:=\"\\033[\"}\nCOLORIZER_END=${COLORIZER_END:=\"m\"}\n\n# Default colors\nCOLORIZER_blue=${COLORIZER_blue:=\"0;34\"}\nCOLORIZER_green=${COLORIZER_green:=\"0;32\"}\nCOLORIZER_cyan=${COLORIZER_cyan:=\"0;36\"}\nCOLORIZER_red=${COLORIZER_red:=\"0;31\"}\nCOLORIZER_purple=${COLORIZER_purple:=\"0;35\"}\nCOLORIZER_yellow=${COLORIZER_yellow:=\"0;33\"}\nCOLORIZER_gray=${COLORIZER_gray:=\"1;30\"}\nCOLORIZER_light_blue=${COLORIZER_light_blue:=\"1;34\"}\nCOLORIZER_light_green=${COLORIZER_light_green:=\"1;32\"}\nCOLORIZER_light_cyan=${COLORIZER_light_cyan:=\"1;36\"}\nCOLORIZER_light_red=${COLORIZER_light_red:=\"1;31\"}\nCOLORIZER_light_purple=${COLORIZER_light_purple:=\"1;35\"}\nCOLORIZER_light_yellow=${COLORIZER_light_yellow:=\"1;33\"}\nCOLORIZER_light_gray=${COLORIZER_light_gray:=\"0;37\"}\n\n# Somewhat special colors\nCOLORIZER_black=${COLORIZER_black:=\"0;30\"}\nCOLORIZER_white=${COLORIZER_white:=\"1;37\"}\nCOLORIZER_none=${COLORIZER_none:=\"0\"}\n\n##\n# Parse the input and return the ansi code output processed output\n##\nCOLORIZER_process_input() {\n    local prompt_option=\"${1}\"\n    local strip_option=\"${2}\"\n    shift 2\n    local processed=\"${*}\"\n    local pseudoTag=\"\"\n\n    local stack\n    ARRAY_define \"stack\"\n\n    local result=\"\"\n    local ansiToken=\"\"\n\n    result=\"${processed%%<*}\"\n    if [ \"${result}\" != \"\" ] && [ \"${result}\" != \"${processed}\" ]; then\n        # Cut outer content, which has been processed already\n        processed=\"<${processed#*<}\"\n    fi\n    while [ \"${processed#*<}\" != \"${processed}\" ]; do\n        # Isolate first tag in stream\n        pseudoTag=\"${processed#*<}\"\n        pseudoTag=\"${pseudoTag%%>*}\"\n\n        # Push/Pop tag to/from stack\n        if [ \"${pseudoTag:0:1}\" != \"/\" ]; then\n            ARRAY_push \"stack\" \"${pseudoTag}\"\n        else\n            if [ \"${pseudoTag:1}\" != \"$(ARRAY_peek \"stack\")\" ]; then\n                echo \"Mismatching colorize tag nesting at <$(ARRAY_peek \"stack\")>...<${pseudoTag}>\"\n                exit 42\n            fi\n            ARRAY_pop \"stack\" >/dev/null\n        fi\n\n        # Apply ansi formatting\n        if [ -z \"${strip_option}\" ]; then\n            pseudoTag=\"${pseudoTag/-/_}\"\n            if [ \"${pseudoTag:0:1}\" != \"/\" ]; then\n                # Opening Tag\n                eval \"ansiToken=\\\"\\${COLORIZER_${pseudoTag}}\\\"\"\n            else\n                # Closing Tag\n                if [ \"$(ARRAY_count \"stack\")\" -eq 0 ]; then\n                    ansiToken=\"${COLORIZER_none}\"\n                else\n                    eval \"ansiToken=\\\"\\${COLORIZER_$(ARRAY_peek \"stack\")}\\\"\"\n                fi\n            fi\n\n            # Add escape codes\n            ansiToken=\"${COLORIZER_START}${ansiToken}${COLORIZER_END}\"\n            if [ \"${prompt_option}\" = \"SET\" ]; then\n                ansiToken=\"\\[${ansiToken}\\]\"\n            fi\n\n            result=\"${result}${ansiToken}\"\n        fi\n\n        # Cut processed portion from stream\n        processed=\"${processed#*>}\"\n\n        # Update result with next content part\n        result=\"${result}${processed%%<*}\"\n    done\n\n    if [ \"$(ARRAY_count \"stack\")\" -ne 0 ]; then\n        echo \"Could not find closing tag for <$(ARRAY_peek \"stack\")>\"\n        exit 42\n    fi\n\n    result=\"${result//&lt;/<}\"\n    result=\"${result//&gt;/>}\"\n    echo \"${result}\"\n}\n\n##\n# Parse a given colorize string and output the correctly escaped ansi-code\n# formatted string for it.\n#\n# This function is the only public API method to this utillity\n#\n# echo -e is used for output.\n#\n# The -n option may be specified, which will behave exactly like echo -n, aka\n# omitting the newline.\n#\n# To use ansi in a prompt without behaving badly, using the -p option.\n#\n# @option -n omit the newline\n# @option -p escape ansi for prompt usage\n# @option -s instead of replacing with ansi, just strip the tags\n# @param [string,...]\n##\ncolorize() {\n    local OPTIND=1\n    local newline_option=\"\"\n    local prompt_option=\"\"\n    local strip_option=\"\"\n    local option=\"\"\n    while getopts \":nps\" option; do\n        case \"${option}\" in\n            n) newline_option=\"SET\";;\n            p) prompt_option=\"SET\";;\n            s) strip_option=\"SET\";;\n            \\?) echo \"Invalid option (-${OPTARG}) given to colorize\"; exit 42;;\n        esac\n    done\n    shift $((OPTIND-1))\n\n    local processed_message=\"$(COLORIZER_process_input \"${prompt_option}\" \"${strip_option}\" \"${@}\")\"\n\n    if [ \"${newline_option}\" = \"SET\" ]; then\n        echo -en \"${processed_message}\"\n    else\n        echo -e \"${processed_message}\"\n    fi\n}\n\n# Allow alternate spelling\nalias colourise=colorize\n"
  },
  {
    "path": "cli/imports/common.sh",
    "content": "#!/bin/bash\n\n# colors\nred=$'\\e[1;31m'\ngrn=$'\\e[1;32m'\nyel=$'\\e[1;33m'\nblu=$'\\e[1;34m'\nmag=$'\\e[1;35m'\ncyn=$'\\e[1;36m'\nend=$'\\e[0m'\n\n# upload to transer.sh\n# use: transer \"$file\"\ntransfer() {\n\tif [ $# -eq 0 ];\n\t\tthen echo -e \"No arguments specified. Usage:\\necho transfer /tmp/test.md\\ncat /tmp/test.md | transfer test.md\";\n\t\treturn 1;\n\tfi\n\n\ttmpfile=$( mktemp -t transferXXX );\n\tif tty -s; then basefile=$(basename \"$1\" | sed -e 's/[^a-zA-Z0-9._-]/-/g');\n\t\tcurl --progress-bar --upload-file \"$1\" \"https://transfer.sh/$basefile\" >> $tmpfile;\n\telse curl --progress-bar --upload-file \"-\" \"https://transfer.sh/$1\" >> $tmpfile ;\n\tfi;\n\techo \"Your file: \";\n\tcat $tmpfile;\n\tfile=$(cat $tmpfile);\n\tcat > $outputDir/last.txt <<< $file\n\trm -f $tmpfile;\n}\n"
  },
  {
    "path": "cli/lastupload/last.txt",
    "content": "https://transfer.sh/FrW09/sync.tar.gz\n"
  },
  {
    "path": "cli/upload.sh",
    "content": "#!/bin/bash\nclear\n\n# ------------------------------\n#\n# UPLOAD GEKKO HISTORY DATA\n# To transfer.sh + get link\n#\n# -------------------------------\n\n# imports\nsource imports/common.sh\nsource imports/colorizer.sh\n\n# check config.sh\nif [ ! -f config.sh ]; then\n    colorize \"<light-red>ERROR</light-red> config.sh could not be found, copy config.sample.sh and modify to suit your needs!\\n\"\n    return\nfi\n\n# import user config\nsource config.sh\n\n# create output dir\noutputDir=\"lastupload\"\nmkdir -p $outputDir\n\n# dir\nhistoryDir=\"$gekkoDir/history\"\n\necho\ncolorize \"COMPRESSING HISTORY DIRECTORY \\n---\\nDestination folder: <light-yellow>$outputDir</light-yellow>\\n\"\ncolorize \"<light-green>Please wait, it might take a while...</light-green>\\n\"\n\n# compress\ntar -czf \"$outputDir/sync.tar.gz\" $historyDir\n\n# msg\ncolorize \"<light-yellow>Compression completed.</light-yellow>\\n\"\n\ncolorize \"UPLOADING TO TRANSFER.SH \\n---\\n\"\n\n# upload\ntransfer \"$outputDir/sync.tar.gz\"\n\ncolorize \"\\n\\n<light-yellow>Completed.</light-yellow>\\n\"\n\ncolorize \"Saved lastfile src to: <light-yellow>$outputDir/last.txt</light-yellow>\\n\"\n\n# remove old compressed file\nrm -rf \"$outputDir/sync.tar.gz\"\n"
  },
  {
    "path": "strategies/RSI_BULL_BEAR.js",
    "content": "/*\r\n\tRSI Bull and Bear\r\n\tUse different RSI-strategies depending on a longer trend\r\n\t3 feb 2017\r\n\t\r\n\t(CC-BY-SA 4.0) Tommie Hansen\r\n\thttps://creativecommons.org/licenses/by-sa/4.0/\r\n\t\r\n*/\r\n\r\n// req's\r\nvar log = require ('../core/log.js');\r\nvar config = require ('../core/util.js').getConfig();\r\n\r\n// strategy\r\nvar strat = {\r\n\t\r\n\t/* INIT */\r\n\tinit: function()\r\n\t{\r\n\t\tthis.name = 'RSI Bull and Bear';\r\n\t\tthis.requiredHistory = config.tradingAdvisor.historySize;\r\n\t\tthis.resetTrend();\t\t\r\n\t\t\r\n\t\t// debug? set to flase to disable all logging/messages (improves performance)\r\n\t\tthis.debug = false;\r\n\t\t\r\n\t\t// performance\r\n\t\tconfig.backtest.batchSize = 1000; // increase performance\r\n\t\tconfig.silent = true;\r\n\t\tconfig.debug = false;\r\n\t\t\r\n\t\t// add indicators\r\n\t\tthis.addIndicator('maSlow', 'SMA', this.settings.SMA_long );\r\n\t\tthis.addIndicator('maFast', 'SMA', this.settings.SMA_short );\r\n\t\tthis.addIndicator('BULL_RSI', 'RSI', { interval: this.settings.BULL_RSI });\r\n\t\tthis.addIndicator('BEAR_RSI', 'RSI', { interval: this.settings.BEAR_RSI });\r\n\t\t\r\n\t\t// debug stuff\r\n\t\tthis.startTime = new Date();\r\n\t\tthis.stat = {\r\n\t\t\tbear: { min: 100, max: 0 },\r\n\t\t\tbull: { min: 100, max: 0 }\r\n\t\t};\r\n\t\t\r\n\t}, // init()\r\n\t\r\n\t\r\n\t/* RESET TREND */\r\n\tresetTrend: function()\r\n\t{\r\n\t\tvar trend = {\r\n\t\t\tduration: 0,\r\n\t\t\tdirection: 'none',\r\n\t\t\tlongPos: false,\r\n\t\t};\r\n\t\r\n\t\tthis.trend = trend;\r\n\t},\r\n\t\r\n\t/* get lowest/highest for backtest-period */\r\n\tlowHigh: function( rsi, type )\r\n\t{\r\n\t\tlet cur;\r\n\t\tif( type == 'bear' ) {\r\n\t\t\tcur = this.stat.bear;\r\n\t\t\tif( rsi < cur.min ) this.stat.bear.min = rsi; // set new\r\n\t\t\tif( rsi > cur.max ) this.stat.bear.max = rsi;\r\n\t\t}\r\n\t\telse {\r\n\t\t\tcur = this.stat.bull;\r\n\t\t\tif( rsi < cur.min ) this.stat.bull.min = rsi; // set new\r\n\t\t\tif( rsi > cur.max ) this.stat.bull.max = rsi;\r\n\t\t}\r\n\t},\r\n\t\r\n\t\r\n\t/* CHECK */\r\n\tcheck: function()\r\n\t{\r\n\t\t\r\n\t\t// get all indicators\r\n\t\tlet ind = this.indicators,\r\n\t\t\tmaSlow = ind.maSlow.result,\r\n\t\t\tmaFast = ind.maFast.result,\r\n\t\t\trsi;\r\n\t\t\t\r\n\t\t// BEAR TREND\r\n\t\tif( maFast < maSlow )\r\n\t\t{\r\n\t\t\trsi = ind.BEAR_RSI.result;\r\n\t\t\tif( rsi > this.settings.BEAR_RSI_high ) this.short();\r\n\t\t\telse if( rsi < this.settings.BEAR_RSI_low ) this.long();\r\n\t\t\t\r\n\t\t\tif(this.debug) this.lowHigh( rsi, 'bear' );\r\n\t\t\t//log.debug('BEAR-trend');\r\n\t\t}\r\n\r\n\t\t// BULL TREND\r\n\t\telse\r\n\t\t{\r\n\t\t\trsi = ind.BULL_RSI.result;\r\n\t\t\tif( rsi > this.settings.BULL_RSI_high ) this.short();\r\n\t\t\telse if( rsi < this.settings.BULL_RSI_low )  this.long();\r\n\t\t\tif(this.debug) this.lowHigh( rsi, 'bull' );\r\n\t\t\t//log.debug('BULL-trend');\r\n\t\t}\r\n\t\r\n\t}, // check()\r\n\t\r\n\t\r\n\t/* LONG */\r\n\tlong: function()\r\n\t{\r\n\t\tif( this.trend.direction !== 'up' ) // new trend? (only act on new trends)\r\n\t\t{\r\n\t\t\tthis.resetTrend();\r\n\t\t\tthis.trend.direction = 'up';\r\n\t\t\tthis.advice('long');\r\n\t\t\t//log.debug('go long');\r\n\t\t}\r\n\t\t\r\n\t\tif(this.debug)\r\n\t\t{\r\n\t\t\tthis.trend.duration++;\r\n\t\t\tlog.debug ('Long since', this.trend.duration, 'candle(s)');\r\n\t\t}\r\n\t},\r\n\t\r\n\t\r\n\t/* SHORT */\r\n\tshort: function()\r\n\t{\r\n\t\t// new trend? (else do things)\r\n\t\tif( this.trend.direction !== 'down' )\r\n\t\t{\r\n\t\t\tthis.resetTrend();\r\n\t\t\tthis.trend.direction = 'down';\r\n\t\t\tthis.advice('short');\r\n\t\t}\r\n\t\t\r\n\t\tif(this.debug)\r\n\t\t{\r\n\t\t\tthis.trend.duration++;\r\n\t\t\tlog.debug ('Short since', this.trend.duration, 'candle(s)');\r\n\t\t}\r\n\t},\r\n\t\r\n\t\r\n\t/* END backtest */\r\n\tend: function(){\r\n\t\t\r\n\t\tlet seconds = ((new Date()- this.startTime)/1000),\r\n\t\t\tminutes = seconds/60,\r\n\t\t\tstr;\r\n\t\t\t\r\n\t\tminutes < 1 ? str = seconds + ' seconds' : str = minutes + ' minutes';\r\n\t\t\r\n\t\tlog.debug('====================================');\r\n\t\tlog.debug('Finished in ' + str);\r\n\t\tlog.debug('====================================');\r\n\t\t\r\n\t\tif(this.debug)\r\n\t\t{\r\n\t\t\tlet stat = this.stat;\r\n\t\t\tlog.debug('RSI low/high for period');\r\n\t\t\tlog.debug('BEAR low/high: ' + stat.bear.min + ' / ' + stat.bear.max);\r\n\t\t\tlog.debug('BULL low/high: ' + stat.bull.min + ' / ' + stat.bull.max);\r\n\t\t}\r\n\r\n\t}\r\n\t\r\n};\r\n\r\nmodule.exports = strat;"
  },
  {
    "path": "strategies/RSI_BULL_BEAR.toml",
    "content": "# SMA Trends\r\nSMA_long = 1000\r\nSMA_short = 50\r\n\r\n# BULL\r\nBULL_RSI = 10\r\nBULL_RSI_high = 80\r\nBULL_RSI_low = 60\r\n\r\n# BEAR\r\nBEAR_RSI = 15\r\nBEAR_RSI_high = 50\r\nBEAR_RSI_low = 20\r\n\r\n# BULL/BEAR is defined by the longer SMA trends\r\n# if SHORT over LONG = BULL\r\n# if SHORT under LONG = BEAR"
  },
  {
    "path": "strategies/RSI_BULL_BEAR_ADX.js",
    "content": "/*\r\n\tRSI Bull and Bear + ADX modifier\r\n\t1. Use different RSI-strategies depending on a longer trend\r\n\t2. But modify this slighly if shorter BULL/BEAR is detected\r\n\t-\r\n\t(CC-BY-SA 4.0) Tommie Hansen\r\n\thttps://creativecommons.org/licenses/by-sa/4.0/\r\n\t-\r\n\tNOTE: Requires custom indicators found here:\r\n\thttps://github.com/Gab0/Gekko-extra-indicators\r\n\t(c) Gabriel Araujo\r\n\tHowto: Download + add to gekko/strategies/indicators\r\n*/\r\n\r\n// req's\r\nvar log = require('../core/log.js');\r\nvar config = require('../core/util.js').getConfig();\r\n\r\n// strategy\r\nvar strat = {\r\n\t\r\n\t/* INIT */\r\n\tinit: function()\r\n\t{\r\n\t\t// core\r\n\t\tthis.name = 'RSI Bull and Bear + ADX';\r\n\t\tthis.requiredHistory = config.tradingAdvisor.historySize;\r\n\t\tthis.resetTrend();\r\n\t\t\r\n\t\t// debug? set to false to disable all logging/messages/stats (improves performance in backtests)\r\n\t\tthis.debug = false;\r\n\t\t\r\n\t\t// performance\r\n\t\tconfig.backtest.batchSize = 1000; // increase performance\r\n\t\tconfig.silent = true; // NOTE: You may want to set this to 'false' @ live\r\n\t\tconfig.debug = false;\r\n\t\t\r\n\t\t// SMA\r\n\t\tthis.addIndicator('maSlow', 'SMA', this.settings.SMA.long );\r\n\t\tthis.addIndicator('maFast', 'SMA', this.settings.SMA.short );\r\n\t\t\r\n\t\t// RSI\r\n\t\tthis.addIndicator('BULL_RSI', 'RSI', { interval: this.settings.BULL.rsi });\r\n\t\tthis.addIndicator('BEAR_RSI', 'RSI', { interval: this.settings.BEAR.rsi });\r\n\t\t\r\n\t\t// ADX\r\n\t\tthis.addIndicator('ADX', 'ADX', this.settings.ADX.adx );\r\n\t\t\r\n\t\t// MOD (RSI modifiers)\r\n\t\tthis.BULL_MOD_high = this.settings.BULL.mod_high;\r\n\t\tthis.BULL_MOD_low = this.settings.BULL.mod_low;\r\n\t\tthis.BEAR_MOD_high = this.settings.BEAR.mod_high;\r\n\t\tthis.BEAR_MOD_low = this.settings.BEAR.mod_low;\r\n\t\t\r\n\t\t\r\n\t\t// debug stuff\r\n\t\tthis.startTime = new Date();\r\n\t\t\r\n\t\t// add min/max if debug\r\n\t\tif( this.debug ){\r\n\t\t\tthis.stat = {\r\n\t\t\t\tadx: { min: 1000, max: 0 },\r\n\t\t\t\tbear: { min: 1000, max: 0 },\r\n\t\t\t\tbull: { min: 1000, max: 0 }\r\n\t\t\t};\r\n\t\t}\r\n\t\t\r\n\t\t/* MESSAGES */\r\n\t\t\r\n\t\t// message the user about required history\r\n\t\tlog.info(\"====================================\");\r\n\t\tlog.info('Running', this.name);\r\n\t\tlog.info('====================================');\r\n\t\tlog.info(\"Make sure your warmup period matches SMA_long and that Gekko downloads data if needed\");\r\n\t\t\r\n\t\t// warn users\r\n\t\tif( this.requiredHistory < this.settings.SMA.long )\r\n\t\t{\r\n\t\t\tlog.warn(\"*** WARNING *** Your Warmup period is lower then SMA_long. If Gekko does not download data automatically when running LIVE the strategy will default to BEAR-mode until it has enough data.\");\r\n\t\t}\r\n\t\t\r\n\t}, // init()\r\n\t\r\n\t\r\n\t/* RESET TREND */\r\n\tresetTrend: function()\r\n\t{\r\n\t\tvar trend = {\r\n\t\t\tduration: 0,\r\n\t\t\tdirection: 'none',\r\n\t\t\tlongPos: false,\r\n\t\t};\r\n\t\r\n\t\tthis.trend = trend;\r\n\t},\r\n\t\r\n\t\r\n\t/* get low/high for backtest-period */\r\n\tlowHigh: function( val, type )\r\n\t{\r\n\t\tlet cur;\r\n\t\tif( type == 'bear' ) {\r\n\t\t\tcur = this.stat.bear;\r\n\t\t\tif( val < cur.min ) this.stat.bear.min = val; // set new\r\n\t\t\telse if( val > cur.max ) this.stat.bear.max = val;\r\n\t\t}\r\n\t\telse if( type == 'bull' ) {\r\n\t\t\tcur = this.stat.bull;\r\n\t\t\tif( val < cur.min ) this.stat.bull.min = val; // set new\r\n\t\t\telse if( val > cur.max ) this.stat.bull.max = val;\r\n\t\t}\r\n\t\telse {\r\n\t\t\tcur = this.stat.adx;\r\n\t\t\tif( val < cur.min ) this.stat.adx.min = val; // set new\r\n\t\t\telse if( val > cur.max ) this.stat.adx.max = val;\r\n\t\t}\r\n\t},\r\n\t\r\n\t\r\n\t/* CHECK */\r\n\tcheck: function()\r\n\t{\r\n\t\t// get all indicators\r\n\t\tlet ind = this.indicators,\r\n\t\t\tmaSlow = ind.maSlow.result,\r\n\t\t\tmaFast = ind.maFast.result,\r\n\t\t\trsi,\r\n\t\t\tadx = ind.ADX.result;\r\n\t\t\t\r\n\t\t// BEAR TREND\r\n\t\t// NOTE: maFast will always be under maSlow if maSlow can't be calculated\r\n\t\tif( maFast < maSlow )\r\n\t\t{\r\n\t\t\trsi = ind.BEAR_RSI.result;\r\n\t\t\tlet rsi_hi = this.settings.BEAR.high,\r\n\t\t\t\trsi_low = this.settings.BEAR.low;\r\n\t\t\t\r\n\t\t\t// ADX trend strength?\r\n\t\t\tif( adx > this.settings.ADX.high ) rsi_hi = rsi_hi + this.BEAR_MOD_high;\r\n\t\t\telse if( adx < this.settings.ADX.low ) rsi_low = rsi_low + this.BEAR_MOD_low;\r\n\t\t\t\t\r\n\t\t\tif( rsi > rsi_hi ) this.short();\r\n\t\t\telse if( rsi < rsi_low ) this.long();\r\n\t\t\t\r\n\t\t\tif(this.debug) this.lowHigh( rsi, 'bear' );\r\n\t\t}\r\n\r\n\t\t// BULL TREND\r\n\t\telse\r\n\t\t{\r\n\t\t\trsi = ind.BULL_RSI.result;\r\n\t\t\tlet rsi_hi = this.settings.BULL.high,\r\n\t\t\t\trsi_low = this.settings.BULL.low;\r\n\t\t\t\r\n\t\t\t// ADX trend strength?\r\n\t\t\tif( adx > this.settings.ADX.high ) rsi_hi = rsi_hi + this.BULL_MOD_high;\t\t\r\n\t\t\telse if( adx < this.settings.ADX.low ) rsi_low = rsi_low + this.BULL_MOD_low;\r\n\t\t\t\t\r\n\t\t\tif( rsi > rsi_hi ) this.short();\r\n\t\t\telse if( rsi < rsi_low )  this.long();\r\n\t\t\tif(this.debug) this.lowHigh( rsi, 'bull' );\r\n\t\t}\r\n\t\t\r\n\t\t// add adx low/high if debug\r\n\t\tif( this.debug ) this.lowHigh( adx, 'adx');\r\n\t\r\n\t}, // check()\r\n\t\r\n\t\r\n\t/* LONG */\r\n\tlong: function()\r\n\t{\r\n\t\tif( this.trend.direction !== 'up' ) // new trend? (only act on new trends)\r\n\t\t{\r\n\t\t\tthis.resetTrend();\r\n\t\t\tthis.trend.direction = 'up';\r\n\t\t\tthis.advice('long');\r\n\t\t\tif( this.debug ) log.info('Going long');\r\n\t\t}\r\n\t\t\r\n\t\tif( this.debug )\r\n\t\t{\r\n\t\t\tthis.trend.duration++;\r\n\t\t\tlog.info('Long since', this.trend.duration, 'candle(s)');\r\n\t\t}\r\n\t},\r\n\t\r\n\t\r\n\t/* SHORT */\r\n\tshort: function()\r\n\t{\r\n\t\t// new trend? (else do things)\r\n\t\tif( this.trend.direction !== 'down' )\r\n\t\t{\r\n\t\t\tthis.resetTrend();\r\n\t\t\tthis.trend.direction = 'down';\r\n\t\t\tthis.advice('short');\r\n\t\t\tif( this.debug ) log.info('Going short');\r\n\t\t}\r\n\t\t\r\n\t\tif( this.debug )\r\n\t\t{\r\n\t\t\tthis.trend.duration++;\r\n\t\t\tlog.info('Short since', this.trend.duration, 'candle(s)');\r\n\t\t}\r\n\t},\r\n\t\r\n\t\r\n\t/* END backtest */\r\n\tend: function()\r\n\t{\r\n\t\tlet seconds = ((new Date()- this.startTime)/1000),\r\n\t\t\tminutes = seconds/60,\r\n\t\t\tstr;\r\n\t\t\t\r\n\t\tminutes < 1 ? str = seconds.toFixed(2) + ' seconds' : str = minutes.toFixed(2) + ' minutes';\r\n\t\t\r\n\t\tlog.info('====================================');\r\n\t\tlog.info('Finished in ' + str);\r\n\t\tlog.info('====================================');\r\n\t\r\n\t\t// print stats and messages if debug\r\n\t\tif(this.debug)\r\n\t\t{\r\n\t\t\tlet stat = this.stat;\r\n\t\t\tlog.info('BEAR RSI low/high: ' + stat.bear.min + ' / ' + stat.bear.max);\r\n\t\t\tlog.info('BULL RSI low/high: ' + stat.bull.min + ' / ' + stat.bull.max);\r\n\t\t\tlog.info('ADX min/max: ' + stat.adx.min + ' / ' + stat.adx.max);\r\n\t\t}\r\n\t\t\r\n\t}\r\n\t\r\n};\r\n\r\nmodule.exports = strat;"
  },
  {
    "path": "strategies/RSI_BULL_BEAR_ADX.toml",
    "content": "[SMA]\nlong = 1000\nshort = 50\n\n[BULL]\nrsi = 10\nhigh = 80\nlow = 60\nmod_high = 5\nmod_low = -5\n\n[BEAR]\nrsi = 15\nhigh = 50\nlow = 20\nmod_high = 15\nmod_low  = -5\n\n[ADX]\nadx = 3\nhigh = 70\nlow = 50"
  },
  {
    "path": "strategies/TEMA.js",
    "content": "/*\r\n\tTEMA\r\n\tTriple EMA strategy with 'safety net'\r\n\t---\r\n\tUses 1x TEMA to go long/short if short trend is over TEMA.\r\n\tOn top of this it uses a longer SMA as a safety net and simple\r\n\tstays out of the market if short SMA is under that SMA.\r\n\t---\r\n\tThe general idea is to buy in good market conditions and simply not\r\n\tbe a part of longer downwards trends.\r\n*/\r\n\r\n// req's\r\nvar log = require ('../core/log.js');\r\nvar config = require ('../core/util.js').getConfig();\r\n\r\n// strategy\r\nvar strat = {\r\n\t\r\n\t\r\n\t/* INIT */\r\n\tinit: function()\r\n\t{\r\n\t\t// base\r\n\t\tthis.name = 'TEMA';\r\n\t\tthis.requiredHistory = config.tradingAdvisor.historySize;\r\n\t\tthis.debug = false; // outputs messages, set to false to increase performance\r\n\t\t\r\n\t\t// add indicators and reset trend\r\n\t\tthis.resetTrend();\r\n\t\tthis.addTulipIndicator('maSlow', 'tema', { optInTimePeriod: this.settings.long });\r\n\t\tthis.addTulipIndicator('maFast', 'sma', { optInTimePeriod: this.settings.short });\r\n\t\t\r\n\t\t// check if long SMA is to be used\r\n\t\tif( this.settings.SMA_long > 0 )\r\n\t\t{\r\n\t\t\tthis.useSafety = true;\r\n\t\t\tthis.addTulipIndicator('maSlowest', 'sma', { optInTimePeriod: this.settings.SMA_long });\r\n\t\t}\r\n\t\t\r\n\t\t// set startTime to measure execution time @ end()\r\n\t\tthis.startTime = new Date();\r\n\t\t\r\n\t}, // init()\r\n\t\r\n\t\r\n\t\r\n\t/* RESET TREND */\r\n\tresetTrend: function()\r\n\t{\r\n\t\tvar trend = {\r\n\t\t\tduration: 0,\r\n\t\t\tdirection: 'none',\r\n\t\t\tlongPos: false,\r\n\t\t};\r\n\t\r\n\t\tthis.trend = trend;\r\n\t\t\r\n\t}, // resetTrend()\r\n\t\r\n\t\r\n\t\r\n\t/* CHECK */\r\n\tcheck: function()\r\n\t{\r\n\t\t// do nothing if we don't got enough history\r\n\t\tif( this.candle.close.length < this.requiredHistory ) return;\r\n\t\t\r\n\t\t// fetch indicators\r\n\t\tlet ti = this.tulipIndicators;\r\n\t\tlet maFast = ti.maFast.result.result,\r\n\t\t\tmaSlow = ti.maSlow.result.result;\r\n\t\t\r\n\t\t\r\n\t\t// check if safety option > 0\r\n\t\tif( this.useSafety )\r\n\t\t{\r\n\t\t\tlet maSlowest = ti.maSlowest.result.result;\r\n\t\t\tif( maSlow < maSlowest )\r\n\t\t\t{\r\n\t\t\t\tthis.short();\r\n\t\t\t\treturn; // quit\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\t// other rules\r\n\t\tif( maFast > maSlow ) { this.long(); }\r\n\t\telse if( maFast < maSlow ) { this.short(); }\r\n\t\t\r\n\t}, // check()\r\n\t\r\n\t\r\n\t\r\n\t/* LONG */\r\n\tlong: function()\r\n\t{\r\n\t\tif( this.trend.direction !== 'up' )\r\n\t\t{\r\n\t\t\tthis.resetTrend();\r\n\t\t\tthis.trend.direction = 'up';\r\n\t\t\tthis.advice('long');\r\n\t\t}\r\n\t\t\r\n\t\tif( this.debug )\r\n\t\t{\r\n\t\t\tthis.trend.duration++;\r\n\t\t\tlog.debug ('Positive since', this.trend.duration, 'candle(s)');\r\n\t\t}\r\n\t},\r\n\t\r\n\t\r\n\t\r\n\t/* SHORT */\r\n\tshort: function()\r\n\t{\r\n\t\tif( this.trend.direction !== 'down' )\r\n\t\t{\r\n\t\t\tthis.resetTrend();\r\n\t\t\tthis.trend.direction = 'down';\r\n\t\t\tthis.advice('short');\r\n\t\t}\r\n\t\t\r\n\t\tif( this.debug )\r\n\t\t{\r\n\t\t\tthis.trend.duration++;\r\n\t\t\tlog.debug ('Negative since', this.trend.duration, 'candle(s)');\r\n\t\t}\r\n\t},\r\n\t\r\n\t\r\n\t/* END */\r\n\tend: function()\r\n\t{\r\n\t\tlet seconds = ((new Date()- this.startTime)/1000),\r\n\t\t\tminutes = seconds/60,\r\n\t\t\tstr;\r\n\t\t\t\r\n\t\tminutes < 1 ? str = seconds + ' seconds' : str = minutes + ' minutes';\r\n\t\t\r\n\t\tlog.debug('Finished in ' + str);\r\n\t}\r\n\t\r\n}; // strat{}\r\n\r\n\r\n\r\n/* EXPORT */\r\nmodule.exports = strat;"
  },
  {
    "path": "strategies/TEMA.toml",
    "content": "# Short / Long (EMA/TEMA)\r\nshort = 10\r\nlong = 80\r\n\r\n# Safety (SMA, 0 = disable)\r\nSMA_long = 200"
  },
  {
    "path": "syncAll/config.sample.sh",
    "content": "#!/bin/bash\n\n# Gekko directory\n# Relative -or- absolute to where you have gekko tools installed\n\ngDir='../../gekko'\n"
  },
  {
    "path": "syncAll/download.sh",
    "content": "#!/bin/bash\nclear\n\n### Download sync files @ ./sync/last.txt and replace files in Gekko with these new files ###\n\n# colors\ngrn=$'\\e[1;32m'\nyel=$'\\e[1;33m'\nmag=$'\\e[1;35m'\ncyn=$'\\e[1;36m'\nend=$'\\e[0m'\n\n# check config.sh\nif [ ! -f config.sh ]; then\n    printf \"${red}ERROR${end} config.sh could not be found, copy config.sample.sh and modify to suit your needs!\\n\"\n    return\nfi\n\n# import user config ($gDir)\nsource config.sh\n\noutputDir=\"sync\"\n\n# get latest file from github\ngit pull\n\n\n\nfile=$(cat $outputDir/last.txt)\nsaveFile='sync.tar.gz';\n\n# get $filename function\nget() {\n\tif [ $# -eq 0 ];\n\t\tthen echo -e \"No arguments specified.\";\n\t\treturn 1;\n\tfi\n\techo\n\techo\n\tcurl \"$1\" >> $saveFile;\n}\n\n# get the file\nprintf \"Downloading file ${yel}$file${end}, please stand by...\";\nget $file\n\nprintf \"\\n\\n\"\n\n# untar\nprintf \"Unpacking ${yel}$saveFile${end}...\\n\\n\"\ntar -xvzf $saveFile\necho\n\n# sync new > old (and replace)\nprintf \"Replacing old with new @ ${yel}gekko/ > $gDir ${end}\\n\\n\"\nrsync -ah gekko/* $gDir\necho\n\n# remove crap\nrm -rf gekko\nrm -rf sync.tar.gz\n\n\nprintf \"${grn}Completed.${end}\\n\\n\"\n"
  },
  {
    "path": "syncAll/sync/last.txt",
    "content": "https://transfer.sh/AKPh1/sync.tar.gz\n"
  },
  {
    "path": "syncAll/upload.sh",
    "content": "#!/bin/bash\nclear\n\n# colors\nred=$'\\e[1;31m'\ngrn=$'\\e[1;32m'\nyel=$'\\e[1;33m'\nblu=$'\\e[1;34m'\nmag=$'\\e[1;35m'\ncyn=$'\\e[1;36m'\nend=$'\\e[0m'\n\n# check config.sh\nif [ ! -f config.sh ]; then\n    printf \"${red}ERROR${end} config.sh could not be found, copy config.sample.sh and modify to suit your needs!\\n\"\n    return\nfi\n\n# import user config ($gDir)\nsource config.sh\n\noutputDir=\"sync\"\nmkdir -p $outputDir\n\n# dir string\nDIRS=\"$gDir/strategies $gDir/config\"\n\n\n\n# ask user if history should be included or not\nread -p \"${yel}Include history?${end} [y/n]: \" -n 1 -r\necho\n\nif [[ $REPLY =~ ^[Yy]$ ]]\n    then\n        DIRS+=\" $gDir/history\"\n        echo \"${cyn}OK -- including history${end}\"\nfi\n\necho\nprintf \"Compressing the files/folders ${yel}$DIRS${end} \\nDestination folder: ${yel}$outputDir${end}\\n\"\nprintf \"${grn}Please wait...${end}\\n\\n\"\n\n# compress\ntar -czf \"$outputDir/sync.tar.gz\" $DIRS\necho\nprintf \"${yel}Compression completed.${end}\\n\\n\"\n\n\n# upload to transer.sh\ntransfer() {\n\tif [ $# -eq 0 ];\n\t\tthen echo -e \"No arguments specified. Usage:\\necho transfer /tmp/test.md\\ncat /tmp/test.md | transfer test.md\";\n\t\treturn 1;\n\tfi\n\n\ttmpfile=$( mktemp -t transferXXX );\n\tif tty -s; then basefile=$(basename \"$1\" | sed -e 's/[^a-zA-Z0-9._-]/-/g');\n\t\tcurl --progress-bar --upload-file \"$1\" \"https://transfer.sh/$basefile\" >> $tmpfile;\n\telse curl --progress-bar --upload-file \"-\" \"https://transfer.sh/$1\" >> $tmpfile ;\n\tfi;\n\techo \"Your file: \";\n\tcat $tmpfile;\n\tfile=$(cat $tmpfile);\n\tcat > $outputDir/last.txt <<< $file\n\trm -f $tmpfile;\n}\n\nprintf \"${grn}Uploading to transfer.sh, please wait...${end}\\n\\n\"\n\ntransfer \"$outputDir/sync.tar.gz\"\nprintf \"\\n\"\nprintf \"${yel}Done.${end}\\n\\n\"\n\nprintf \"Saved lastfile src to: ${yel}$outputDir/last.txt${end}\\n\\n\"\n\n# remove old compressed file\nrm -rf \"$outputDir/sync.tar.gz\"\n"
  },
  {
    "path": "temp/system/functions.php",
    "content": "<?php\r\n\r\n\t/*\r\n\t\tthis file needs cleaning and classes\r\n\t*/\r\n\r\n\t# GET FILTER\r\n\tfunction _G($var){\r\n\t\t$val = false;\r\n\t\tif( isset($_GET[$var]) ) { $val = $_GET[$var]; }\r\n\t\t$val = htmlspecialchars($val);\r\n\t\t$val = strip_tags($val); // remove tags\r\n\r\n\t\treturn $val;\r\n\t}\r\n\r\n\t# POST filter\r\n\tfunction _P($var){\r\n\t\t$val = false;\r\n\t\tif( isset($_POST[$var]) ) { $val = $_POST[$var]; }\r\n\t\t#$val = htmlspecialchars($val);\r\n\t\t$val = strip_tags($val); // remove tags\r\n\r\n\t\treturn $val;\r\n\t}\r\n\r\n\t# FILTER DATA, remove all tags\r\n\tfunction filterData($str){\r\n\t\t$str = preg_replace(\"/<([a-z][a-z0-9]*)[^>]*?(\\/?)>/i\",'<$1$2>', $str);\r\n\t\treturn $str;\r\n\t}\r\n\r\n\t# prp: <pre> + print_r\r\n\tfunction prp($str, $color = ''){ echo '<pre style=\"color: '. $color .'\">'; print_r($str); echo '</pre>'; }\r\n\tfunction prph($str){\r\n\t\techo '<pre>=======================================</pre>';\r\n\t\techo '<pre>'; print_r($str); echo '</pre>';\r\n\t\techo '<pre>=======================================</pre>';\r\n\t}\r\n\r\n\r\n\t# get all files in a dir\r\n\tfunction listFiles( $dir ){\r\n        $files = @array_diff(scandir($dir), array('.', '..', '.gitignore'));\r\n\t    if( !$files ) $files = 'Error: No files reside within the directory ' . $dir;\r\n\t\treturn $files;\r\n\t}\r\n\r\n\r\n\t# contains\r\n\t# sample: if( contains('something', $source) ){ /* string 'something' existed in $source */ }\r\n\tfunction contains($needle, $haystack) {\r\n\t\treturn strpos($haystack, $needle) !== false;\r\n\t}\r\n\r\n\r\n\t# rtrim + ltrim\r\n\tfunction rmspace($str){\r\n\t\t$data = rtrim(ltrim($str, ' '), ' ');\r\n\t\t$data = str_replace(\"&nbsp;\", '', $data);\r\n\t\treturn $data;\r\n\t}\r\n\r\n\tfunction numfix($str){\r\n\t\t$data = str_replace(',','.',$str);\r\n\t\t$data = htmlentities($data);\r\n\t\t$data = rmspace($data);\r\n\t\treturn $data;\r\n\t}\r\n\r\n\r\n\t# date diff\r\n\tfunction date_between($a, $b){\r\n\t\t$a = new DateTime($a);\r\n\t\t$b = new DateTime($b);\r\n\t\treturn $b->diff($a);\r\n\t}\r\n\r\n\t# T-date split e.g. 2017-24-12T16:00:00.000Z > 2017-24-12 16:00:00\r\n\tfunction tdate($str){\r\n\t\t$arr = explode('T', $str);\r\n\t\t$date = $arr[0];\r\n\t\t$time = explode('.', $arr[1])[0];\r\n\r\n\t\treturn $date .' ' .$time;\r\n\t}\r\n\r\n\t# convert ms to days, hours etc\r\n\tfunction secondsToHuman($str){\r\n\t    $a = new \\DateTime('@0');\r\n\t    $b = new \\DateTime(\"@$str\");\r\n\r\n\t\t$diff = $a->diff($b);\r\n\t\tif( $diff->days > 0 ){\r\n\t\t\treturn $diff->format('%ad, %hh');\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn $diff->format('%hh');\r\n\t\t}\r\n\t}\r\n\r\n\r\n\t# curl get\r\n\tfunction curl_get($url)\r\n\t{\r\n\r\n\t\tdefined(SERVER_TIMEOUT) ? $timeout = SERVER_TIMEOUT : $timeout = 600;\r\n\r\n\t\t$curl = curl_init($url);\r\n\t\tcurl_setopt($curl, CURLOPT_ENCODING, 0);\r\n\t\tcurl_setopt($curl, CURLOPT_RETURNTRANSFER, true);\r\n\t\tcurl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );\r\n\t\tcurl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r\n\t\tcurl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 120);\r\n\t\tcurl_setopt($curl, CURLOPT_TIMEOUT, $timeout);\r\n\r\n\t\t$result = curl_exec($curl);\r\n\t\t$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r\n\r\n\t\tif ( $status != 200 ) {\r\n\t\t\tdie(\"Error: call to URL $url failed with status $status, response $result, curl_error \" . curl_error($curl) . \", curl_errno \" . curl_errno($curl));\r\n\t\t}\r\n\r\n\t\tcurl_close($curl);\r\n\r\n\t\treturn $result;\r\n\r\n\t}\r\n\r\n\r\n\t# measure time (simple)\r\n\tfunction timer_start(){ $a =  microtime(true); return $a; }\r\n\tfunction timer_end($start){\r\n\t\t$end = microtime(true);\r\n\t\t$diff = ($end-$start); // secondsls\r\n\t\t$diff = round($diff*100)/100;\r\n\r\n\t\t// conver to minutes\r\n\t\tif( $diff > 65 ) { $diff = round(($diff/60)*10)/10 . 'min'; }\r\n\t\telse { $diff = $diff . 's'; } // or just use seconds\r\n\t\treturn $diff;\r\n\t}\r\n\r\n\r\n\r\n\t# output table from query; requires PDO::FETCHASSOC from query result\r\n\tfunction sqlTable($res, $class = 'tbl', $echo = true, $id = false ){\r\n\t\t$html = '';\r\n\t\t$head = $res[0];\r\n\t\tif($id) $id = \" id='$id'\";\r\n\t\t$html .= \"<table class='$class'$id><thead><tr>\";\r\n\t\tforeach($head as $k=>$v){\r\n\t\t    $html .= \"<th>$k</th>\";\r\n\t\t}\r\n\t\t$html .= \"</tr></thead><tbody>\";\r\n\r\n\t\tforeach( $res as $key => $val ){\r\n\t\t    $html .= \"<tr>\";\r\n\t\t    foreach($head as $i=>$h){\r\n\t\t        $html .= \"<td>\" . $val[$i] . \"</td>\";\r\n\t\t    }\r\n\t\t    $html .= \"</tr>\";\r\n\t\t}\r\n\r\n\t\t$html .= \"</tbody></table>\";\r\n\r\n\t\tif($echo) { echo $html; }\r\n\t\telse { return $html; }\r\n\r\n\t}\r\n\r\n\r\n\t# flatten arrays\r\n\tfunction array_flatten( $a, $key=NULL)\r\n\t{\r\n\t\t$r = array();\r\n\t\tif(is_array($a))foreach($a as $k=>$v)$r=array_merge($r,array_flatten($v,$k));\r\n\t\telse $r[$key]=$a;\r\n\t\treturn $r;\r\n\t}\r\n\r\n\r\n\t/*-----------------------------------------------------\r\n\r\n\t\tSIMPLIFIED CURL GET CACHE FUNC\r\n\r\n\t    example\r\n\t    curl_cache('http://google.com', 'cache/mycachefile.php', '1 hour');\r\n\r\n\t-----------------------------------------------------*/\r\n\r\n\tfunction curl_cache($src, $file, $time){\r\n\r\n\t    $exists = file_exists($file);\r\n\t    $time = \"+\" . $time;\r\n\r\n\t\t$isExternal = false;\r\n\t\tif (strpos($src, 'http') !== false) { $isExternal = true; }\r\n\r\n\t    // file does not exist or is over x time\r\n\t    if( !$exists || ( $exists && time() > strtotime(\"$time\", filemtime($file))) ) {\r\n\r\n\t\t\tif( $isExternal ){\r\n\r\n\t\t\t\tdefined(SERVER_TIMEOUT) ? $timeout = SERVER_TIMEOUT : $timeout = 600;\r\n\r\n\t\t        $curl = curl_init($src);\r\n\t\t\t\tcurl_setopt($curl, CURLOPT_ENCODING, 1);\r\n\t\t\t\tcurl_setopt($curl, CURLOPT_RETURNTRANSFER, true);\r\n\t\t\t\tcurl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );\r\n\t\t\t\tcurl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r\n\t\t\t\tcurl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 120);\r\n\t\t\t\tcurl_setopt($curl, CURLOPT_TIMEOUT, $timeout);\r\n\t\t\t\tcurl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept-Encoding: gzip,deflate')); // important -- reduce doc by 90%\r\n\r\n\t\t\t\t$data = curl_exec($curl);\r\n\t\t\t\t$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r\n\r\n\t\t\t\tif( $status === 200 ){\r\n\t\t\t\t\tfile_put_contents( $file, gzencode($data) );\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcurl_close($curl);\r\n\r\n\t\t\t} // $isExternal\r\n\r\n\t\t\t// not external, is a string of some sort -- so just put it in a file\r\n\t\t\telse {\r\n\t\t\t\tfile_put_contents( $file, gzencode($src) );\r\n\t\t\t}\r\n\r\n\t    }\r\n\r\n\t    // file exists, just get it\r\n\t    else {\r\n\t        $data = gzdecode( file_get_contents($file) );\r\n\t    }\r\n\r\n\t\t// return\r\n\t\treturn $data;\r\n\r\n\t} // curl_cache()\r\n\r\n\r\n\r\n\r\n\t/*-----------------------------------------------------\r\n\r\n\t\tCURL POST\r\n\r\n\t\turi\r\n\t\tobject with vars {}\r\n\r\n\t-----------------------------------------------------*/\r\n\r\n\tfunction curl_post($url, $vars)\r\n\t{\r\n\t\tdefined(SERVER_TIMEOUT) ? $timeout = SERVER_TIMEOUT : $timeout = 600;\r\n\r\n\t\t$curl = curl_init($url);\r\n\t\tcurl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);\r\n\t\tcurl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);\r\n\t\tcurl_setopt($curl, CURLOPT_HTTPHEADER, array(\"Content-type: application/json\",'Accept-Encoding: gzip,deflate'));\r\n\t\tcurl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r\n\t\tcurl_setopt($curl, CURLOPT_POSTFIELDS, $vars);\r\n\t\tcurl_setopt($curl, CURLOPT_POST, true);\r\n\t\tcurl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 120);\r\n\t\tcurl_setopt($curl, CURLOPT_TIMEOUT, $timeout);\r\n\r\n\t\t$data = curl_exec($curl);\r\n\t\t$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);\r\n\r\n\t\t$arr['data'] = $data;\r\n\t\t$arr['status'] = $status;\r\n\t\t$arr = (object) $arr;\r\n\r\n\t\tcurl_close($curl);\r\n\r\n\t\treturn $arr;\r\n\r\n\t}\r\n\r\n\t// used to posting to 'self'\r\n\tfunction curl_post2($url, $vars)\r\n\t{\r\n\t\tdefined(SERVER_TIMEOUT) ? $timeout = SERVER_TIMEOUT : $timeout = 600;\r\n\r\n\t\t$curl = curl_init($url);\r\n\t\tcurl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);\r\n\t\tcurl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);\r\n\t\tcurl_setopt($curl, CURLOPT_POST, true);\r\n\t\tcurl_setopt($curl, CURLOPT_POSTFIELDS, $vars);\r\n\t\tcurl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 120);\r\n\t\tcurl_setopt($curl, CURLOPT_TIMEOUT, $timeout);\r\n\t\t$data = curl_exec($curl);\r\n\r\n\t\tcurl_close($curl);\r\n\r\n\t\treturn $data;\r\n\r\n\t}\r\n\r\n\tfunction curl_post_cache( $url, $vars, $file, $cacheTime )\r\n\t{\r\n\t\t$exists = file_exists($file);\r\n\t    $time = \"+\" . $cacheTime;\r\n\r\n\t\t// file does not exist or is over x time\r\n\t    if( !$exists || ( $exists && time() > strtotime(\"$time\", filemtime($file))) )\r\n\t\t{\r\n\t\t\t$data = curl_post($url, $vars);\r\n\r\n\t\t\t# only cache file if everything was ok\r\n\t\t\tif( $data->status === 200 ){\r\n\t\t\t\tfile_put_contents( $file, gzencode(json_encode($data)) );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// file exists, just get it\r\n\t    else {\r\n\t        $data = json_decode(gzdecode(file_get_contents($file)));\r\n\t    }\r\n\r\n\t\t$data->data = json_decode($data->data);\r\n\r\n\t\treturn $data;\r\n\r\n\t}\r\n\r\n\r\n?>\r\n"
  },
  {
    "path": "temp/system/user.config.sample.php",
    "content": "<?php\n\n\t/*\n\t\t*** GEKKO LOCAL PATH ***\n\t\tMust point to your install\n\t\t-\n\t\tIf you get errors, just try until it works or really\n\t\tcheck that the path is correct.\n\t*/\n\t\n\t$gekko_path = '../../../www/gekko/';"
  },
  {
    "path": "temp/test.php",
    "content": "<!doctype html>\n<html>\n<head>\n\t<title># TEST</title>\n\t<style>\n\thtml {font-size: 62.5%; }\n\tbody{padding:2% 0;font-family:monospace,mono-space;font-size:1.5rem;color:deeppink;text-align:center;overlow-x:hidden}*,:after,:before{box-sizing:border-box}body,html{height:100%}\n\tbody { background-image: linear-gradient(to top,#060311,#140b3c); background-attachment: fixed; }\n\th1{color:#ff1493;font-weight:400;font-size:1.2rem;margin:20px 0 30px}\n\tpre,textarea{display:block;text-align:left;border:1px dashed #ff0;color:#ff0;padding:20px;vertical-align:top;transition:all .5s ease;cursor:default;min-width:30%;max-width:100%;position:relative;border-radius:2px;line-height:1.4}\n\tpre { margin-bottom: 20px; }\n\tpre:after,textarea:after{content:'$';line-height:1;position:absolute;left:20px;top:-7px;background:#140b3c;padding:0 5px}\n\tpre b,textarea b{font-size:40px;font-weight:400}\n\t\n\tbody,html { height: 100%; margin: 0; }\n\tbody { margin-bottom: 200px; }\n\th1,h2 { color: deeppink; font-weight: normal; }\n\th1 { font-size: 1.8rem; }\n\tsection { max-width: 600px; position: relative; margin:0 auto; }\n\t\n\t/* pre stuff */\n\tpre { margin-bottom: 30px; }\n\tpre:nth-child(2n+1){ border-color: deeppink; color: deeppink; }\n\tpre:nth-child(3n+1){ border-color: cyan; color: cyan; }\n\tpre:focus,pre:hover{color:#fff;border-color:lime; }\n\t</style>\n</head>\n<body>\n<section>\n<h1>some test</h1>\n\n<?php\n\n\t// JS-date > readable date\n\tfunction timeToDate( $str, $format = 'Y-m-d' ) {\n\t\treturn date($format, $str);\n\t}\n\t\n\t/*\n\t\tmin/max date range for a pair\n\t\t-\n\t\tget min/max date range and convert to readable date\n\t\trequires db-handle and table name\n\t\t-\n\t\treturn array with date_min/date_max\n\t*/\n\tfunction minMax( $db, $table )\n\t{\n\t\t$sql = \"\n\t\t\tSELECT\n\t\t\t\tstrftime('%Y-%m-%d %H:%M', datetime(MIN(start), 'unixepoch')) as date_min,\n\t\t\t\tstrftime('%Y-%m-%d %H:%M', datetime(MAX(start), 'unixepoch')) as date_max\n\t\t\tFROM `$table`\n\t\t\";\n\t\t$q = $db->query($sql);\n\t\t$q = $q->fetchAll()[0]; // always just one\n\t\treturn $q;\n\t}\n\t\n\n\t/*\n\t\tCRITICAL CHECKS\n\t*/\n\n\t// check if user config exists\n\tif( !file_exists('system/user.config.php') ) die('<b>ERROR</b> Could not find user.config.php, make sure you have createad it.');\n\t\n\trequire_once 'system/functions.php';\n\trequire_once 'system/user.config.php';\n\t\n\t// check if the path is correct\n\tif( !file_exists($gekko_path . 'README.md') ) die('<b>ERROR</b> The path to your Gekko install is wrong or not working.');\n\t\n\t\n\t\n\t\n\t/*\n\t\tSETUP PATHS\n\t*/\n\t\n\t$gp = $gekko_path;\n\t$paths = [\n\t\t'gekko' => $gp,\n\t\t'history' => $gp . 'history/',\n\t];\n\t\n\t$paths = json_decode(json_encode($paths));\n\t#prp($paths);\n\t\n\t\n\t\n\t\n\t/*\n\t\tGET ALL HISTORY FILES\n\t*/\n\t\n\t$files = listFiles( $paths->history );\n\t\n\t// ..then remove any not ending with '.db'\n\tforeach($files as $key => $value)\n\t{\n\t\t$cur = end(explode('.', $files[$key]));\t// explode + get last\t\n\t\tif( $cur !== 'db' ) unset($files[$key]);\n\t}\n\t\n\t$files = array_values($files); // re-index\n\techo 'history files';\n\tprp($files);\n\t\n\t\n\t\n\t\n\t\n\t/*\n\t\tTRY SQLite STUFF\n\t*/\n\t\n\t// init $db, TEMP: use first db\n\t$first = $paths->history . $files[0];\n\t$db = new PDO('sqlite:' . $first) or die('<b>ERROR</b> Could not connect to database.');\n\t$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);\n\t\n\t$sql = \"\n\t\tSELECT name\n\t\tFROM main.sqlite_master\n\t\tWHERE type = 'table'\n\t\";\n\t\n\t$tables = $db->query($sql);\n\t$tables = $tables->fetchAll();\n\t\n\t\n\t// filter out tables not containg candles_\n\tforeach( $tables as $key => $val )\n\t{\n\t\t$cur = $tables[$key];\n\t\t$name = $val['name'];\t\t\n\t\tif( !contains('candles_', $name) || contains('sqlite', $name) ) unset($tables[$key]);\n\t}\n\t\n\techo 'tables';\n\tprp($tables);\n\t\n\t\n\t// get min/max\n\techo 'min/max';\n\t$stuff = minMax($db, $tables[0]['name']);\n\tprp($stuff);\n\t\n\t$db = null;\n?>\n\n</section>\n</body>\n</html>"
  },
  {
    "path": "wsl-install/gekko_install.sh",
    "content": "#!/bin/bash\nclear\necho \"-------------------------------------------\"\necho 'INSTALLING GEKKO + DEPENDECIES'\necho '-------------------------------------------'\necho 'This will take a while, go get some coffe'\nsleep 3\nsudo su\napt-get update -y && apt-upgrade -y && apt-get update -y\ncurl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -\napt-get install nodejs -y\napt-get install build-essential -y\napt-get install git -y\ncd /mnt/c/\nmkdir www\ncd www\ngit clone git://github.com/askmike/gekko.git\ncd gekko\nnpm install --only=production\ncd exchange\nnpm install --only=production\ncd ..\nnpm install tulind\napt-get install nano -y\napt-get autoremove -y\nnode gekko --ui\n"
  },
  {
    "path": "wsl-install/readme.md",
    "content": "# Install Gekko in Windows bash\n\n##### Prerequisites (required)\n\n* Install Bash on Windows 10:\nhttps://docs.microsoft.com/en-us/windows/wsl/install-win10\n* C-drive in Windows (or change all references to c to any other drive you might have)\n\n#### How-to\n##### Simple way\n\n1. Open bash\n2. curl -sL https://raw.githubusercontent.com/tommiehansen/gekko_tools/master/wsl-install/gekko_install.sh | sudo bash -\n\n##### Manual way\n\n1. open bash (win-key + type 'bash' + ENTER)\n2. sudo su\n3. apt-get update -y && apt-upgrade -y && apt-get update -y\nAbove will take some time, expect it to take 10-20 minutes.\n4. curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -\n5. apt-get install nodejs -y\n6. apt-get install build-essential -y\n7. apt-get install git -y\n8. apt-get install jed && apt-get install nano\n9. cd /mnt/c/\n10. mkdir www\n11. cd www\n12. git clone git://github.com/askmike/gekko.git\n13. cd gekko\n14. npm install --only=production\ncd exchange\nnpm install --only=production\ncd ..\n15. npm install tulind\n16. apt-get autoremove -y\n17. node gekko --ui\n"
  }
]