Mike Fishy and Automation System Programming (Bots)


#364

Hmmm…are we going to start programming with C++? I personally don’t see the need to go to a compilable language. I’ve built all my bots to-date with just Ruby and Javascript without too many headaches, but @Mike_Fishy should elaborate here since he knows where this thing can ultimately go.

The one area I can see a compiled program benefiting is computing RSI and other indicators if we start adding those to the logic where iterating large price datasets quickly is a must, but I don’t really see it here with the direction we’re heading in so far. In earlier threads, Mike talks about how he’s watching prices in millisecond increments to get best price after a buy/sell signal is issued, and presumably with Weibull and other predictive formulas rapidly computing during such.


#365

No I don’t think the main bot will be in C++. It’s just some folk are fine tuning their coding skills as part of the bot learning process.


#366

Actually I was going to bring up an issue relating to timing.

My tests suggest the php sleep(5) might be a total cycle time of 5.6 seconds compared with running the code in C++. That said I think the MCU core devs have recently fine tuned their timer so it could have been a drift specific to the MCU.

I like to run php and C++ versions concurrently to check the data tallies so timing is an issue for me.

Mike has previously said that 4 PowerShell sessions can be run at the same time and I didn’t know if that related to the Binance API of 100,000 hits per day or if it was a PowerShell constraint.

Previous to working with this bot I was refreshing the API every 1233 ms to keep within the 100,000 hits / 86400 seconds limit.


#367

Guys lets just focus on PHP in this topic please. @Mike_Fishy gave us some homework. Anybody already making some progress? We need data gathered and ST MT and LT should be pair specific man! :slight_smile: :slight_smile:


#368

Here’s 3 RSI’s. No other changes to the code. LT RSI is still used and will yield same result as Fishy’s last update.

// REMOVED -- see post below for correct script.

#369

If we as a group know what data we need centrally, we could cloud it? Is this an option then we have a number of bots pulling data off the same source. How is this for an idea.

Thoughts? we can then do some of the more simple calculations in the cloud that are generic.

Crash


#370

I don’t see 3 RSI’s, are you sure?


#371

maybe I posted the wrong one…here’s 3 RSI’s plus each CDA has it’s own trend. I renamed a few variables along the way and added directional constants to make the code a bit more readable.

<?php
require 'vendor/autoload.php';

// This bot will trade USDT to the other pairs and back to make more USDT
// It shall use all the trading pairs to make more UDST except the ones we tell it not to use

$init_stperiod = 50;             // Short Term Trend - must be less than $mtperiod
$init_mtperiod = 90;             // Medium Term Trend - must be less than $ltperiod
$init_ltperiod = 120;            // Long Term Trend
$tradefile = "BTC.txt";          // The Trade Logging file name
$minspread = 1.1;                // The minimum spread percentage needed for a trade
$minrsi = 45;                    // Relative Strength must be below this number to buy
$sellbuffer = 1.003;             // Create a buffer to hold CDA if sell is not profitable
$maxorders = 10;                 // Maximum number of concurrent orders

// Do not change any of the flags, we use this to signal the bot what to do and when
$buyready = 0;            // This flag signals the bot that the pair meets rules to buy
$buyprep = 1;             // This flag signals the bot to prepare to buy
$buyord = 2;              // This flag signals the bot to place an order
$sellok = 3;              // This flag signals the bot that the order was completed
$sellready = 4;           // This flag signals the bot to sell
$selldone = 5;            // This flag signals the bot the trade completed
$dontbuy = 6;             // This flag signals the bot we dont want to trade BCASH :P

// Trend Directions
$unset = 0;
$down = 1;
$flat = 2;
$up = 3;

// Standard variables and arrays we use
$replay = 1;
$i = 0;
$binance_prices = array();
$time_start = time();
$time_end = 0;
$run_time = 0;
$rpc = 0;
$tpc = 0;
$q = 0;
$cdaorders = 0;
$btcprice = 0;
$btctrend = array();
$bttrend = "WAIT";

// API call to fetch pricing data from Binance
function getprices()
{
  global $replay;
  global $pricefh;
  
  if($replay == 0)
  {
    $api = new Binance\API("<api key>","<secret>");
    $mp = $api->prices();
    $pricefh = fopen("BTC-prices.txt","a") or die("Cannot open file");
    fwrite($pricefh, serialize($mp) . "\n");
    fclose($pricefh);
  }
  else
  {
    if (!$pricefh)
    {
      $pricefh = fopen("BTC-prices.txt","r") or die("Cannot open replay file");
    }
    $mp = unserialize(fgets($pricefh));
    if ($mp == NULL) die("end of price data reached.\n");
  }
  return $mp;
}

if ($replay == 1) 
{
  $simulation_mode = "replaying"; 
}
else 
{
  $simulation_mode = "real-time";
  if(file_exists("BTC-prices.txt"))
  {
    unlink("BTC-prices.txt");
  }
}

// Start of the Loop - can run for months - press CTRL-C to stop
for($i = 0; $i <= 2000000; $i++)
{
  $time_end = time();
  $run_time = round((($time_end - $time_start)/60),2);
  print "====================================\n";
  print "Iteration = $i ($simulation_mode) \n";
  print "Running Time: $run_time mins \n";
  print "Current BTC Price is: $btcprice T:$bttrend \n";
  print "Current Orders in progress = $cdaorders / $maxorders\n";
  print "Current running percentage = $rpc \n";
  print "====================================\n";

  // Fetch current prices from Binance
  $binance_prices = getprices();

  // Loop through the price data as key and value pairs
  foreach($binance_prices as $key => $value)
  {
    // Track BTC price for display
    if($key == "BTCUSDT")
    {
      $btcprice = $value;
      array_push($btctrend, $value);
      if($i >= ${$tick . "mtperiod"})
      {
        array_shift($btctrend);
        $btcavg = round((array_sum($btctrend)/${$tick . "ltperiod"}),8);
        if($value >= $btcavg)
        {
          $bttrend = "UP";
        }
        else
        {
          $bttrend = "DOWN";
        }
      }
    }
    // Only process pairs with BTC
    if(strpos($key, "BTC"))
    {

      // Convert the pair name to lower case in varibale $tick
      // for example the name "BTCUSDT" will become "btcusdt"

      $tick = strtolower($key);

      // For the first iteration, create arrays and varibales for the bot
      if($i == 0)
      {

        // Use the lower case name to form the leading part of varibales and arrays
        // for exmaple, using "btcusdt" and adding "st" for the short term array
        // will initialise an array called "btcusdtst"
        // as we loop thorugh the pairs, each one gets created for each pair
        // for exmaple "NEOUSDT" will become "neousdtst"

        ${$tick . "st"} = array();           // Short Term Array
        ${$tick . "mt"} = array();           // Medium Term Array
        ${$tick . "lt"} = array();           // Long Term Array
        
        ${$tick . "stperiod"} = $init_stperiod; // Short Term Duration
        ${$tick . "mtperiod"} = $init_mtperiod; // Medium Term Duration
        ${$tick . "ltperiod"} = $init_ltperiod; // Long Term Duration

        ${$tick . "stavg"} = 0;              // Short Term Moving Average
        ${$tick . "mtavg"} = 0;              // Medium Term Moving Average
        ${$tick . "ltavg"} = 0;              // Long Term Moving Average
        ${$tick . "stdir"} = $unset;         // Short Term Moving Trend
        ${$tick . "mtdir"} = $unset;         // Medium Term Moving Trend
        ${$tick . "ltdir"} = $unset;         // Long Term Moving Trend
        ${$tick . "lspread"} = 0;            // Long Term Spread
        ${$tick . "strsi"} = 0;              // Short Term Relative Strength Indicator for this pair
        ${$tick . "mtrsi"} = 0;              // Medium Term Relative Strength Indicator for this pair
        ${$tick . "ltrsi"} = 0;              // Long Term Relative Strength Indicator for this pair
        ${$tick . "tradeflag"} = $buyready;  // Set this pair to buyready
        ${$tick . "buyvalue"} = 0;           // record what we buy for on this pair
        ${$tick . "sellvalue"} = 0;          // record what we sell for on this pair
        ${$tick . "lasttrade"} = 0;          // record we have had one trade done
        ${$tick . "lasttpc"} = 0;            // record what percentage last the trade was
        ${$tick . "isset"} = 1;              // used to signal we are initialised for this pair
      }

      // We are not on the first loop anymore, we proceed with processing the data
      else
      {

        // Exclude List - these ones we do not trade

        if($key == "BCHABCBTC") ${$tick . "tradeflag"} = $dontbuy;
        if($key == "BCHSVBTC") ${$tick . "tradeflag"} = $dontbuy;
        if($key == "BCCBTC") ${$tick . "tradeflag"} = $dontbuy;

        // Check if the trading pair has been initialised
        // this covers if Binance add a new trading pair on USDT while we are running
        // if Binance adds new trading pairs while bot is running, we shall
        // ignore them and only use the ones since the bot was started and initialised

        if(isset(${$tick . "isset"}))
        {

          // Push data into arrays and shift arrays once we have enough data
          array_push(${$tick . "st"}, $value);
          array_push(${$tick . "mt"}, $value);
          array_push(${$tick . "lt"}, $value);
          if($i > ${$tick . "stperiod"}) array_shift(${$tick . "st"});
          if($i > ${$tick . "mtperiod"}) array_shift(${$tick . "mt"});
          if($i > ${$tick . "ltperiod"}) array_shift(${$tick . "lt"});

          // Wait until we have all the arrays populated with data
          if($i <= ${$tick . "ltperiod"})
          {
            if($key == "BCHSVBTC") print "Loading Arrays with data until Iteration ${$tick . "ltperiod"} - patience, Grasshopper...\n";
          }

          // Arrays are populated, so on with the processing
          else
          {

            // Calculate the Moving Average for the 3 arrays
            ${$tick . "stavg"} = round((array_sum(${$tick . "st"})/${$tick . "stperiod"}),8);
            ${$tick . "mtavg"} = round((array_sum(${$tick . "mt"})/${$tick . "mtperiod"}),8);
            ${$tick . "ltavg"} = round((array_sum(${$tick . "lt"})/${$tick . "ltperiod"}),8);

            // Check if the Short Term Trend is trending down, flat or up
            // We use the current price to see if it is above or below the short term moving average
            // We use "1" to signal it is trending down, "2" for flat, "3" for trending up
            if($value < ${$tick . "stavg"}) ${$tick . "stdir"} = $down;
            if($value == ${$tick . "stavg"}) ${$tick . "stdir"} = $flat;
            if($value > ${$tick . "stavg"}) ${$tick . "stdir"} = $up;

            // Check if the Medium Term Trend is trending down, flat or up
            // We use the short term moving average to see if it is above or below the medium term moving average
            // We use "1" to signal it is trending down, "2" for flat, "3" for trending up
            if(${$tick . "stavg"} < ${$tick . "mtavg"}) ${$tick . "mtdir"} = $down;
            if(${$tick . "stavg"} == ${$tick . "mtavg"}) ${$tick . "mtdir"} = $flat;
            if(${$tick . "stavg"} > ${$tick . "mtavg"}) ${$tick . "mtdir"} = $up;

            // Check if the Long Term Trend is trending down, flat or up
            // We use the medium term moving average to see if it is above or below the long term moving average
            // We use "1" to signal it is trending down, "2" for flat, "3" for trending up
            if(${$tick . "mtavg"} < ${$tick . "ltavg"}) ${$tick . "ltdir"} = $down;
            if(${$tick . "mtavg"} == ${$tick . "ltavg"}) ${$tick . "ltdir"} = $flat;
            if(${$tick . "mtavg"} > ${$tick . "ltavg"}) ${$tick . "ltdir"} = $up;

            // Calculate the Medium Term spread, which is the percentage difference between
            // the highest recorded price and the lowest recorded price in the Medium Term Array
            $mlow = min(${$tick . "lt"});
            $mhigh = max(${$tick . "lt"});
            ${$tick . "lspread"} = round(((1-($mlow/$mhigh))*100),3);
            
            // Calculate the Relative Strength Indicator on the Long Term Array
            // A Low RSI indicates a buy opportunity
            $rsitck = 0;
            ${$tick . "gain"} = array();
            ${$tick . "loss"} = array();
            foreach(${$tick . "lt"} as $cdaval)
            {
              if($rsitck == 0)
              {
                $cdagain = 0;
                $cdaloss = 0;
              }
              else
              {
                if($cdaval == $cdaprev)
                {
                  $cdagain = 0;
                  $cdaloss = 0;
                }
                elseif($cdaval > $cdaprev)
                {
                  $cdacalc = $cdaval - $cdaprev;
                  $cdagain = number_format($cdacalc,8);
                  $cdaloss = 0;
                }
                else
                {
                  $cdacalc = $cdaprev - $cdaval;
                  $cdaloss = number_format($cdacalc,8);
                  $cdagain = 0;
                }
              }
              array_push(${$tick . "gain"}, $cdagain);
              array_push(${$tick . "loss"}, $cdaloss);
              $cdaprev = $cdaval;
              $rsitck++;
            }

            // Long Term RSI
            ${$tick . "ltrsi"} = 100;
            $cdarsgain = (array_sum(${$tick . "gain"})) / ${$tick . "ltperiod"};
            $cdarsloss = (array_sum(${$tick . "loss"})) / ${$tick . "ltperiod"};
            if($cdarsloss > 0) ${$tick . "ltrsi"} = round(100-(100/(1+($cdarsgain/$cdarsloss))),3);

            // Medium Term RSI
            ${$tick . "mtrsi"} = 100;
            $gains = array_slice(${$tick . "gain"}, ${$tick . "ltperiod"} - ${$tick . "mtperiod"}, ${$tick . "mtperiod"});
            $losses = array_slice(${$tick . "loss"}, ${$tick . "ltperiod"} - ${$tick . "mtperiod"}, ${$tick . "mtperiod"});
            $cdarsgain = (array_sum($gains) / ${$tick . "mtperiod"});
            $cdarsloss = (array_sum($losses) / ${$tick . "mtperiod"});
            if($cdarsloss > 0) ${$tick . "mtrsi"} = round(100-(100/(1+($cdarsgain/$cdarsloss))),3);

            // Short Term RSI
            ${$tick . "strsi"} = 100;
            $gains = array_slice(${$tick . "gain"}, ${$tick . "ltperiod"} - ${$tick . "stperiod"}, ${$tick . "stperiod"});
            $losses = array_slice(${$tick . "loss"}, ${$tick . "ltperiod"} - ${$tick . "stperiod"}, ${$tick . "stperiod"});
            $cdarsgain = (array_sum($gains) / ${$tick . "stperiod"});
            $cdarsloss = (array_sum($losses) / ${$tick . "stperiod"});
            if($cdarsloss > 0) ${$tick . "strsi"} = round(100-(100/(1+($cdarsgain/$cdarsloss))),3);

            // Print out only ones in Buy Order and Sell Ready
            if(${$tick . "tradeflag"} == $buyord OR ${$tick . "tradeflag"} == $sellready)
            {
              printf("%-9s",$key);
              print "\tV:";
              printf("%-14.8F",$value);
              print "\t  ST:";
              printf("%-14.8F",${$tick . "stavg"});
              if(${$tick . "stdir"} == $down) printf("%-5s",":DOWN");
              if(${$tick . "stdir"} == $flat) printf("%-5s",":FLAT");
              if(${$tick . "stdir"} == $up) printf("%-5s",":UP");
              print "\t  MT:";
              printf("%-14.8F",${$tick . "mtavg"});
              if(${$tick . "mtdir"} == $down) printf("%-5s",":DOWN");
              if(${$tick . "mtdir"} == $flat) printf("%-5s",":FLAT");
              if(${$tick . "mtdir"} == $up) printf("%-5s",":UP");
              print "\t  LT:";
              printf("%-14.8F",${$tick . "ltavg"});
              if(${$tick . "ltdir"} == $down) printf("%-5s",":DOWN");
              if(${$tick . "ltdir"} == $flat) printf("%-5s",":FLAT");
              if(${$tick . "ltdir"} == $up) printf("%-5s",":UP");
              print "\t  SPREAD:";
              printf("%-03.3F",${$tick . "lspread"}); 
              print "%\t  RSI:";
              printf("%-06.3F",${$tick . "strsi"});
              printf("/%-06.3F",${$tick . "mtrsi"});
              printf("/%-06.3F",${$tick . "ltrsi"});

              if(${$tick . "tradeflag"} == $buyord) $cdastatus = "Buy Order";
              if(${$tick . "tradeflag"} == $sellready) $cdastatus = "Sell Ready";
              if(${$tick . "tradeflag"} == $sellready)
              {
                $ctp = round(((($value - ${$tick . "buyvalue"})/${$tick . "buyvalue"})*100),3);
                print "\t S:$cdastatus \tBV:${$tick . "buyvalue"} CTP:$ctp";
              }
              else
              {
                print "\tS:$cdastatus";
              }
              if(${$tick . "lasttrade"} == 1)
              {
                print "   LastTPC:${$tick . "lasttpc"}";
              }
              print "\n";
            }

            // Trading rules start here
            // ========================

            // CDA is trending up so set to buyprep
            if(${$tick . "tradeflag"} == $buyready AND ${$tick . "stdir"}==$up AND ${$tick . "mtdir"}==$up AND ${$tick . "ltdir"}==$up)
            {
              printf("%-9s",$key);
              print "Was Buyready, now Buyprep V:$value\n";
              ${$tick . "tradeflag"} = $buyprep;
            }

            // CDA was buyprep, now trending down, set to buyord if reasonable spread
            if(${$tick . "tradeflag"} == $buyprep AND ${$tick . "stdir"}==$down AND ${$tick . "mtdir"}==$down AND ${$tick . "ltdir"}==$down AND ${$tick . "lspread"} >= $minspread)
            {
              printf("%-9s",$key);
              print "Was Buyprep, now Buyord V:$value\n";
              ${$tick . "tradeflag"} = $buyord;
            }

            // CDA stopped trending down and is ready to buy
            if(${$tick . "tradeflag"} == $buyord AND ${$tick . "stdir"}==$up AND ${$tick . "mtdir"}!=$down)
            {
              if(${$tick . "ltrsi"} <= $minrsi)
              {
                if($cdaorders < $maxorders)
                {
                  printf("%-9s",$key);
                  print "Was Buyord, now Buy V:$value\n";
                  // Assume we buy at the current value
                  ${$tick . "buyvalue"} = $value;
                  ${$tick . "tradeflag"} = $sellok;
                  $cdaorders = $cdaorders + 1;
                  $fh = fopen($tradefile, "a") or die("Cannot open file");
                  fwrite($fh, "========================== \n");
                  fwrite($fh, "Runtime $run_time \n");
                  fwrite($fh, "Buy on $key BV:${$tick . "buyvalue"} \n");
                  fwrite($fh, "========================== \n");
                  fclose($fh);
                }
              }
              else
              {
                printf("%-9s",$key);
                print "RSI Check not meeting Minimum, resetting back to Buy Prep\n";
                ${$tick . "tradeflag"} = $buyprep;
              }
            }

            // Buy Order on CDA placed, do order tracking here to make sure order completes
            if(${$tick . "tradeflag"} == $sellok)
            {
              // Since we are not placing an order, we just assume it completed
              ${$tick . "tradeflag"} = $sellready;
            }

            // CDA is sellready and is no longer trending upwards - time to sell
            if(${$tick . "tradeflag"} == $sellready AND ${$tick . "stdir"}!=$up AND ${$tick . "mtdir"}!=$up)
            {
              // Assume we sell at the current value and not sell if not meeting a minimum
              $cdabuff = ${$tick . "buyvalue"} * $sellbuffer;
              if($value > $cdabuff)
              {
                ${$tick . "sellvalue"} = $value;
                ${$tick . "tradeflag"} = $selldone;
              }
              else
              {
                printf("%-9s",$key);
                print "Did not meet minimum sell amount\n";
              }
            }

            // CDA is selldone
            if(${$tick . "tradeflag"} == $selldone)
            {
              // Sell Order on CDA placed, do order tracking here to make sure order completes
              // Since we are not placing an order, we just assume it completed
              $q = round((((${$tick . "sellvalue"} - ${$tick . "buyvalue"})/${$tick . "buyvalue"})*100),3);
              $tpc = $q - 0.2;
              $rpc = round($rpc + ($tpc/$maxorders),3);
              ${$tick . "lasttrade"} = 1;
              ${$tick . "lasttpc"} = $tpc;
              ${$tick . "tradeflag"} = $buyready;
              $cdaorders = $cdaorders - 1;
              printf("%-9s",$key);
              print "Sell Done BV:${$tick . "buyvalue"} SV:${$tick . "sellvalue"} TPC:$tpc \n";
              $fh = fopen($tradefile, "a") or die("Cannot open file");
              fwrite($fh, "========================== \n");
              fwrite($fh, "Runtime $run_time \n");
              fwrite($fh, "Sell Done on $key BV:${$tick . "buyvalue"} SV:${$tick . "sellvalue"} TPC:$tpc RPC:$rpc \n");
              fwrite($fh, "========================== \n");
              fclose($fh);
            }
          }
        }
      }
    }
  }
  if ($replay == 0) sleep(5);
}
?>

#372

Last update for the day (I think)…

This one tracks average trade cycle per CDA. Each trade’s duration is tracked then kept in an array and averaged over time. It’s written out to the buy/sell log as ATC on the SELL action. Also shown as CTC during active trade and ATC once trade closes.

There may be bugs in the code, so definitely review closely.

<?php
require 'vendor/autoload.php';

// This bot will trade USDT to the other pairs and back to make more USDT
// It shall use all the trading pairs to make more UDST except the ones we tell it not to use

$interval = 5;                   // interval between price sampling
$init_stperiod = 50;             // Short Term Trend - must be less than $mtperiod
$init_mtperiod = 90;             // Medium Term Trend - must be less than $ltperiod
$init_ltperiod = 120;            // Long Term Trend
$tradefile = "BTC.txt";          // The Trade Logging file name
$minspread = 1.1;                // The minimum spread percentage needed for a trade
$minrsi = 45;                    // Relative Strength must be below this number to buy
$sellbuffer = 1.003;             // Create a buffer to hold CDA if sell is not profitable
$maxorders = 10;                 // Maximum number of concurrent orders

// Do not change any of the flags, we use this to signal the bot what to do and when
$buyready = 0;            // This flag signals the bot that the pair meets rules to buy
$buyprep = 1;             // This flag signals the bot to prepare to buy
$buyord = 2;              // This flag signals the bot to place an order
$sellok = 3;              // This flag signals the bot that the order was completed
$sellready = 4;           // This flag signals the bot to sell
$selldone = 5;            // This flag signals the bot the trade completed
$dontbuy = 6;             // This flag signals the bot we dont want to trade BCASH :P

// Trend Directions
$unset = 0;
$down = 1;
$flat = 2;
$up = 3;

// Standard variables and arrays we use
$replay = 1;
$i = 0;
$binance_prices = array();
$time_start = time();
$time_end = 0;
$run_time = 0;
$rpc = 0;
$tpc = 0;
$q = 0;
$cdaorders = 0;
$btcprice = 0;
$btctrend = array();
$bttrend = "WAIT";

// API call to fetch pricing data from Binance
function getprices()
{
  global $replay;
  global $pricefh;
  
  if($replay == 0)
  {
    $api = new Binance\API("<api key>","<secret>");
    $mp = $api->prices();
    $pricefh = fopen("BTC-prices.txt","a") or die("Cannot open file");
    fwrite($pricefh, serialize($mp) . "\n");
    fclose($pricefh);
  }
  else
  {
    if (!$pricefh)
    {
      $pricefh = fopen("BTC-prices.txt","r") or die("Cannot open replay file");
    }
    $mp = unserialize(fgets($pricefh));
    if ($mp == NULL) die("end of price data reached.\n");
  }
  return $mp;
}

if ($replay == 1) 
{
  $simulation_mode = "replaying"; 
}
else 
{
  $simulation_mode = "real-time";
  if(file_exists("BTC-prices.txt"))
  {
    unlink("BTC-prices.txt");
  }
}

// Start of the Loop - can run for months - press CTRL-C to stop
for($i = 0; $i <= 2000000; $i++)
{
  $time_end = time();
  if ($replay == 1) {
    $run_time = round(($i * $interval) / 60, 2);
  } else {
    $run_time = round((($time_end - $time_start)/60),2);
  }
  print "====================================\n";
  print "Iteration = $i ($simulation_mode) \n";
  print "Running Time: $run_time mins \n";
  print "Current BTC Price is: $btcprice T:$bttrend \n";
  print "Current Orders in progress = $cdaorders / $maxorders\n";
  print "Current running percentage = $rpc \n";
  print "====================================\n";

  // Fetch current prices from Binance
  $binance_prices = getprices();

  // Loop through the price data as key and value pairs
  foreach($binance_prices as $key => $value)
  {
    // Track BTC price for display
    if($key == "BTCUSDT")
    {
      $btcprice = $value;
      array_push($btctrend, $value);
      if($i >= ${$tick . "mtperiod"})
      {
        array_shift($btctrend);
        $btcavg = round((array_sum($btctrend)/${$tick . "ltperiod"}),8);
        if($value >= $btcavg)
        {
          $bttrend = "UP";
        }
        else
        {
          $bttrend = "DOWN";
        }
      }
    }
    // Only process pairs with BTC
    if(strpos($key, "BTC"))
    {

      // Convert the pair name to lower case in varibale $tick
      // for example the name "BTCUSDT" will become "btcusdt"

      $tick = strtolower($key);

      // For the first iteration, create arrays and varibales for the bot
      if($i == 0)
      {

        // Use the lower case name to form the leading part of varibales and arrays
        // for exmaple, using "btcusdt" and adding "st" for the short term array
        // will initialise an array called "btcusdtst"
        // as we loop thorugh the pairs, each one gets created for each pair
        // for exmaple "NEOUSDT" will become "neousdtst"

        ${$tick . "st"} = array();           // Short Term Array
        ${$tick . "mt"} = array();           // Medium Term Array
        ${$tick . "lt"} = array();           // Long Term Array
        
        ${$tick . "stperiod"} = $init_stperiod; // Short Term Duration
        ${$tick . "mtperiod"} = $init_mtperiod; // Medium Term Duration
        ${$tick . "ltperiod"} = $init_ltperiod; // Long Term Duration

        ${$tick . "stavg"} = 0;              // Short Term Moving Average
        ${$tick . "mtavg"} = 0;              // Medium Term Moving Average
        ${$tick . "ltavg"} = 0;              // Long Term Moving Average
        ${$tick . "stdir"} = $unset;         // Short Term Moving Trend
        ${$tick . "mtdir"} = $unset;         // Medium Term Moving Trend
        ${$tick . "ltdir"} = $unset;         // Long Term Moving Trend
        ${$tick . "lspread"} = 0;            // Long Term Spread
        ${$tick . "strsi"} = 0;              // Short Term Relative Strength Indicator for this pair
        ${$tick . "mtrsi"} = 0;              // Medium Term Relative Strength Indicator for this pair
        ${$tick . "ltrsi"} = 0;              // Long Term Relative Strength Indicator for this pair
        ${$tick . "tradeflag"} = $buyready;  // Set this pair to buyready
        ${$tick . "buyvalue"} = 0;           // record what we buy for on this pair
        ${$tick . "sellvalue"} = 0;          // record what we sell for on this pair
        ${$tick . "lasttrade"} = 0;          // record we have had one trade done
        ${$tick . "lasttpc"} = 0;            // record what percentage last the trade was
        ${$tick . "isset"} = 1;              // used to signal we are initialised for this pair
        ${$tick . "trade_start"} = 0;        // start of currently open trade
        ${$tick . "trade_cycles"} = array(); // track each trade cycle length
      }

      // We are not on the first loop anymore, we proceed with processing the data
      else
      {

        // Exclude List - these ones we do not trade

        if($key == "BCHABCBTC") ${$tick . "tradeflag"} = $dontbuy;
        if($key == "BCHSVBTC") ${$tick . "tradeflag"} = $dontbuy;
        if($key == "BCCBTC") ${$tick . "tradeflag"} = $dontbuy;

        // Check if the trading pair has been initialised
        // this covers if Binance add a new trading pair on USDT while we are running
        // if Binance adds new trading pairs while bot is running, we shall
        // ignore them and only use the ones since the bot was started and initialised

        if(isset(${$tick . "isset"}))
        {

          // Push data into arrays and shift arrays once we have enough data
          array_push(${$tick . "st"}, $value);
          array_push(${$tick . "mt"}, $value);
          array_push(${$tick . "lt"}, $value);
          if($i > ${$tick . "stperiod"}) array_shift(${$tick . "st"});
          if($i > ${$tick . "mtperiod"}) array_shift(${$tick . "mt"});
          if($i > ${$tick . "ltperiod"}) array_shift(${$tick . "lt"});

          // Wait until we have all the arrays populated with data
          if($i <= ${$tick . "ltperiod"})
          {
            if($key == "BCHSVBTC") print "Loading Arrays with data until Iteration ${$tick . "ltperiod"} - patience, Grasshopper...\n";
          }

          // Arrays are populated, so on with the processing
          else
          {

            // Calculate the Moving Average for the 3 arrays
            ${$tick . "stavg"} = round((array_sum(${$tick . "st"})/${$tick . "stperiod"}),8);
            ${$tick . "mtavg"} = round((array_sum(${$tick . "mt"})/${$tick . "mtperiod"}),8);
            ${$tick . "ltavg"} = round((array_sum(${$tick . "lt"})/${$tick . "ltperiod"}),8);

            // Check if the Short Term Trend is trending down, flat or up
            // We use the current price to see if it is above or below the short term moving average
            // We use "1" to signal it is trending down, "2" for flat, "3" for trending up
            if($value < ${$tick . "stavg"}) ${$tick . "stdir"} = $down;
            if($value == ${$tick . "stavg"}) ${$tick . "stdir"} = $flat;
            if($value > ${$tick . "stavg"}) ${$tick . "stdir"} = $up;

            // Check if the Medium Term Trend is trending down, flat or up
            // We use the short term moving average to see if it is above or below the medium term moving average
            // We use "1" to signal it is trending down, "2" for flat, "3" for trending up
            if(${$tick . "stavg"} < ${$tick . "mtavg"}) ${$tick . "mtdir"} = $down;
            if(${$tick . "stavg"} == ${$tick . "mtavg"}) ${$tick . "mtdir"} = $flat;
            if(${$tick . "stavg"} > ${$tick . "mtavg"}) ${$tick . "mtdir"} = $up;

            // Check if the Long Term Trend is trending down, flat or up
            // We use the medium term moving average to see if it is above or below the long term moving average
            // We use "1" to signal it is trending down, "2" for flat, "3" for trending up
            if(${$tick . "mtavg"} < ${$tick . "ltavg"}) ${$tick . "ltdir"} = $down;
            if(${$tick . "mtavg"} == ${$tick . "ltavg"}) ${$tick . "ltdir"} = $flat;
            if(${$tick . "mtavg"} > ${$tick . "ltavg"}) ${$tick . "ltdir"} = $up;

            // Calculate the Medium Term spread, which is the percentage difference between
            // the highest recorded price and the lowest recorded price in the Medium Term Array
            $mlow = min(${$tick . "lt"});
            $mhigh = max(${$tick . "lt"});
            ${$tick . "lspread"} = round(((1-($mlow/$mhigh))*100),3);
            
            // Calculate the Relative Strength Indicator on the Long Term Array
            // A Low RSI indicates a buy opportunity
            $rsitck = 0;
            ${$tick . "gain"} = array();
            ${$tick . "loss"} = array();
            foreach(${$tick . "lt"} as $cdaval)
            {
              if($rsitck == 0)
              {
                $cdagain = 0;
                $cdaloss = 0;
              }
              else
              {
                if($cdaval == $cdaprev)
                {
                  $cdagain = 0;
                  $cdaloss = 0;
                }
                elseif($cdaval > $cdaprev)
                {
                  $cdacalc = $cdaval - $cdaprev;
                  $cdagain = number_format($cdacalc,8);
                  $cdaloss = 0;
                }
                else
                {
                  $cdacalc = $cdaprev - $cdaval;
                  $cdaloss = number_format($cdacalc,8);
                  $cdagain = 0;
                }
              }
              array_push(${$tick . "gain"}, $cdagain);
              array_push(${$tick . "loss"}, $cdaloss);
              $cdaprev = $cdaval;
              $rsitck++;
            }

            // Long Term RSI
            ${$tick . "ltrsi"} = 100;
            $cdarsgain = (array_sum(${$tick . "gain"})) / ${$tick . "ltperiod"};
            $cdarsloss = (array_sum(${$tick . "loss"})) / ${$tick . "ltperiod"};
            if($cdarsloss > 0) ${$tick . "ltrsi"} = round(100-(100/(1+($cdarsgain/$cdarsloss))),3);

            // Medium Term RSI
            ${$tick . "mtrsi"} = 100;
            $gains = array_slice(${$tick . "gain"}, ${$tick . "ltperiod"} - ${$tick . "mtperiod"}, ${$tick . "mtperiod"});
            $losses = array_slice(${$tick . "loss"}, ${$tick . "ltperiod"} - ${$tick . "mtperiod"}, ${$tick . "mtperiod"});
            $cdarsgain = (array_sum($gains) / ${$tick . "mtperiod"});
            $cdarsloss = (array_sum($losses) / ${$tick . "mtperiod"});
            if($cdarsloss > 0) ${$tick . "mtrsi"} = round(100-(100/(1+($cdarsgain/$cdarsloss))),3);

            // Short Term RSI
            ${$tick . "strsi"} = 100;
            $gains = array_slice(${$tick . "gain"}, ${$tick . "ltperiod"} - ${$tick . "stperiod"}, ${$tick . "stperiod"});
            $losses = array_slice(${$tick . "loss"}, ${$tick . "ltperiod"} - ${$tick . "stperiod"}, ${$tick . "stperiod"});
            $cdarsgain = (array_sum($gains) / ${$tick . "stperiod"});
            $cdarsloss = (array_sum($losses) / ${$tick . "stperiod"});
            if($cdarsloss > 0) ${$tick . "strsi"} = round(100-(100/(1+($cdarsgain/$cdarsloss))),3);

            // Print out only ones in Buy Order and Sell Ready
            if(${$tick . "tradeflag"} == $buyord OR ${$tick . "tradeflag"} == $sellready)
            {
              printf("%-9s",$key);
              print "\tV:";
              printf("%-14.8F",$value);
              if(${$tick . "tradeflag"} == $sellready) {
                print "\tCTC:";
                printf("%-3.2F", ($run_time - ${$tick . "trade_start"}));
              } else {
                print "\tATC:";
                printf("%-3.2F", ${$tick . "trade_cycle_avg"});
              }
              print "\t  ST:";
              printf("%-14.8F",${$tick . "stavg"});
              if(${$tick . "stdir"} == $down) printf("%-5s",":DOWN");
              if(${$tick . "stdir"} == $flat) printf("%-5s",":FLAT");
              if(${$tick . "stdir"} == $up) printf("%-5s",":UP");
              print "\t  MT:";
              printf("%-14.8F",${$tick . "mtavg"});
              if(${$tick . "mtdir"} == $down) printf("%-5s",":DOWN");
              if(${$tick . "mtdir"} == $flat) printf("%-5s",":FLAT");
              if(${$tick . "mtdir"} == $up) printf("%-5s",":UP");
              print "\t  LT:";
              printf("%-14.8F",${$tick . "ltavg"});
              if(${$tick . "ltdir"} == $down) printf("%-5s",":DOWN");
              if(${$tick . "ltdir"} == $flat) printf("%-5s",":FLAT");
              if(${$tick . "ltdir"} == $up) printf("%-5s",":UP");
              print "\t  SPREAD:";
              printf("%-03.3F",${$tick . "lspread"}); 
              print "%\t  RSI:";
              printf("%-06.3F",${$tick . "strsi"});
              printf("/%-06.3F",${$tick . "mtrsi"});
              printf("/%-06.3F",${$tick . "ltrsi"});

              if(${$tick . "tradeflag"} == $buyord) $cdastatus = "Buy Order";
              if(${$tick . "tradeflag"} == $sellready) $cdastatus = "Sell Ready";
              if(${$tick . "tradeflag"} == $sellready)
              {
                $ctp = round(((($value - ${$tick . "buyvalue"})/${$tick . "buyvalue"})*100),3);
                print "\t S:$cdastatus \tBV:${$tick . "buyvalue"} CTP:$ctp";
              }
              else
              {
                print "\tS:$cdastatus";
              }
              if(${$tick . "lasttrade"} == 1)
              {
                print "   LastTPC:${$tick . "lasttpc"}";
              }
              print "\n";
            }

            // Trading rules start here
            // ========================

            // CDA is trending up so set to buyprep
            if(${$tick . "tradeflag"} == $buyready AND ${$tick . "stdir"}==$up AND ${$tick . "mtdir"}==$up AND ${$tick . "ltdir"}==$up)
            {
              printf("%-9s",$key);
              print "Was Buyready, now Buyprep V:$value\n";
              ${$tick . "tradeflag"} = $buyprep;
            }

            // CDA was buyprep, now trending down, set to buyord if reasonable spread
            if(${$tick . "tradeflag"} == $buyprep AND ${$tick . "stdir"}==$down AND ${$tick . "mtdir"}==$down AND ${$tick . "ltdir"}==$down AND ${$tick . "lspread"} >= $minspread)
            {
              printf("%-9s",$key);
              print "Was Buyprep, now Buyord V:$value\n";
              ${$tick . "tradeflag"} = $buyord;
            }

            // CDA stopped trending down and is ready to buy
            if(${$tick . "tradeflag"} == $buyord AND ${$tick . "stdir"}==$up AND ${$tick . "mtdir"}!=$down)
            {
              if(${$tick . "ltrsi"} <= $minrsi)
              {
                if($cdaorders < $maxorders)
                {
                  printf("%-9s",$key);
                  print "Was Buyord, now Buy V:$value\n";
                  // Assume we buy at the current value
                  ${$tick . "buyvalue"} = $value;
                  ${$tick . "tradeflag"} = $sellok;
                  ${$tick . "trade_start"} = $run_time;
                  $cdaorders = $cdaorders + 1;
                  $fh = fopen($tradefile, "a") or die("Cannot open file");
                  fwrite($fh, "========================== \n");
                  fwrite($fh, "Runtime $run_time \n");
                  fwrite($fh, "Buy on $key BV:${$tick . "buyvalue"} \n");
                  fwrite($fh, "========================== \n");
                  fclose($fh);
                }
              }
              else
              {
                printf("%-9s",$key);
                print "RSI Check not meeting Minimum, resetting back to Buy Prep\n";
                ${$tick . "tradeflag"} = $buyprep;
              }
            }

            // Buy Order on CDA placed, do order tracking here to make sure order completes
            if(${$tick . "tradeflag"} == $sellok)
            {
              // Since we are not placing an order, we just assume it completed
              ${$tick . "tradeflag"} = $sellready;
            }

            // CDA is sellready and is no longer trending upwards - time to sell
            if(${$tick . "tradeflag"} == $sellready AND ${$tick . "stdir"}!=$up AND ${$tick . "mtdir"}!=$up)
            {
              // Assume we sell at the current value and not sell if not meeting a minimum
              $cdabuff = ${$tick . "buyvalue"} * $sellbuffer;
              if($value > $cdabuff)
              {
                ${$tick . "sellvalue"} = $value;
                ${$tick . "tradeflag"} = $selldone;
              }
              else
              {
                printf("%-9s",$key);
                print "Did not meet minimum sell amount\n";
              }
            }

            // CDA is selldone
            if(${$tick . "tradeflag"} == $selldone)
            {
              // Sell Order on CDA placed, do order tracking here to make sure order completes
              // Since we are not placing an order, we just assume it completed
              $q = round((((${$tick . "sellvalue"} - ${$tick . "buyvalue"})/${$tick . "buyvalue"})*100),3);
              $tpc = $q - 0.2;
              $rpc = round($rpc + ($tpc/$maxorders),3);
              ${$tick . "lasttrade"} = 1;
              ${$tick . "lasttpc"} = $tpc;
              ${$tick . "tradeflag"} = $buyready;
              array_push(${$tick . "trade_cycles"} , ($run_time - ${$tick . "trade_start"})); 
              ${$tick . "trade_cycle_avg"} = round((array_sum(${$tick . "trade_cycles"})/count(${$tick . "trade_cycles"})),3);
              $cdaorders = $cdaorders - 1;
              printf("%-9s",$key);
              print "Sell Done BV:${$tick . "buyvalue"} SV:${$tick . "sellvalue"} TPC:$tpc ATC:${$tick . "trade_cycle_avg"}\n";
              $fh = fopen($tradefile, "a") or die("Cannot open file");
              fwrite($fh, "========================== \n");
              fwrite($fh, "Runtime $run_time \n");
              fwrite($fh, "Sell Done on $key BV:${$tick . "buyvalue"} SV:${$tick . "sellvalue"} TPC:$tpc RPC:$rpc ATC:${$tick . "trade_cycle_avg"}\n");
              fwrite($fh, "========================== \n");
              fclose($fh);
            }
          }
        }
      }
    }
  }
  if ($replay == 0) sleep($interval);
}
?>

1. Change the ST, MT and LT to be specific to each CDA separately. This facilitates the bot changing the trade cycle to be appropriate to the market for that CDA. We really shouldn’t be guessing the ST, MT and LT numbers ourselves, the bot should be doing it.
2. Record the time for the last trade cycle. We track a CDA from it’s low to it’s high and back to it’s next low over the period to see how long it takes, so we can use this timing to guesstimate the time it takes for a trade cycle and adjust that specific CDA’s ST, MT, LT to fit that period of time.
3. After entering a trade, track the gain or loss trend to determine the best time to exit the trade. Knowing when to exit is a lot of rules, we don’t want to exit too early or too late.
4. Add RSI for ST, MT and LT, not just LT. We should have all 3 and use them to see what the short and medium trend is over the longer trend.
5. Track how rapidly the price is going up or down in a time period specific to that CDA. If it trends up rapidly, it might be going through a pump and dump or it might be on a bull run or being dumped or there might be other shenanigans going on. All of which puts the CDA into a risk category where we should probably steer clear of it for a while.
6. Check the 24 hour high and lows, how close is the price to the 24 hour high or low. The closer it is to a 24 hour low, the more likely, given the right market trend, it will head back to it’s 24 hour high.
7. Check the percentage gain/loss on the 24 hour statistics, the higher the number, the more risk that CDA presents. We should be looking at ones that are reasonably stable with a range between -8% to +10%, if a CDA has had -30% or +40% - these are ones we probably want to avoid. We want to achieve regular good trades at reasonable percentages.
8. Add Volume using the Depth API call.
9. ST, MT and LT become dynamic values based on market activity, which actually tends to work best as a function of the Base CDA we are trading. For example, if we are trading the BTC to ALTS and back, we track the cycle of BTCUSDT to form the initial function that helps to determine the values for the BTC to ALTs periods.


#373

Hey guys,

I am sure I can start to figure out the rules etc we need. I can really see the value in getting a data warehouse though.

What language do people recommend me to get ok with to sort out pulling the data off binance?

I have down loaded @Mike_Fishy s recommendation but I got a little cofused with the add-on required. There was a python and another add-on required

I am confused so would appreciate understanding how these bits of software all function in a way I can understand. What do they do and how does it all hang together.

Cheers.

Crash.


#374

We are already pulling the data from Binance with PHP.

Which recommendation was this? A database?


#375

Sounds like a way forward.

Any thoughts folks on a central server holding this data for all to access or would the system start to crawl as more people made use of the service?


#376

Either red is or mongodb …I need to get to a point where I can understand what I am doing… so what are the building blocks for

Redis

MongoDB

Just so I can understand what I have to learn to make it work… There are much more intelligent people in this thread than me with much more experience of software

Some explanation would be good so I can start to figure it out.


#377

@mwlang has kindly offered to opensource the work he has already done with Redis / Ruby. Perhaps wait until he makes the code available and you can review it from there.


#378

Check out this video on Redis

Looks pretty awesome to me.

Crash


#379

Probably around 500Mb per day is what I would expect to be an average, will grow as new trading pairs are added.

@mwlang Redis is a good choice, but you are going to need a system with a truck load of RAM if you want to keep data longer than 7 days. That’s why I went MongoDB, I keep data for 6 months.

@Blynker The Binance API Keys which will be used for ordering and things later on, do need to be secured, but Binance has the option to generate keys that do not allow withdrawals, which can reduce the risk footprint a little. The keys should be stored in an encrypted file and the bot should prompt for the pass phrase at startup of the Monarch process. Data Collector doesn’t need keys.

@mwlang my preferred platform is Linux for it’s security and reliability. Running a Linux server in the cloud is cheap and effective, but can even run Linux on a basic laptop or PC at home and it will do an equivalent job.

@Blynker the code does take time to run, plus the 5 second sleep, it depends on how fast the CPU is. Not that important though as long as the period is relatively stable, it still all works. Cloud servers that have a high amount of time drift due to the virtualisation does cause some issues though.

PHP is super loose and fast for prototyping, use it to get the right ideas down and test, then port the ideas to a language of your choice. Since PHP resembles C++ quite closely, it can be easily ported.

@mwlang - good job mate :slight_smile:
Since you seem to bit further ahead, take a look at trading BTC to BNB at the right times, then also include BNB to ALTs in the trades and after each BNB to ALT trade, looks for the exit back to BTC (Double Dip).
I think you will be surprised at how effective it is.

Stay Fishy


#380

@Mike_Fishy wow 500MB per day, that’s a fair bit of data. Is there much of a benefit beyond 7 days of data?

Binance default is for the keys not to permit fund withdrawal and users have to specifically select it as an option (not recommended and not required).

On the timing I was just wondering why you don’t call the API at say 2 second intervals as it would still be well within the allowed limits.


#381

Ever since you mentioned that this was the primary mechanism you ballooned your profits, I’ve been toying with trying to work the math and timing out. Can you go into this a bit? What I tried to do was see about taking specific paths: BTC -> ALT -> BNB -> BTC. Or… BTC -> ALT -> ETH -> BNB -> BTC Or … BTC -> BNB -> ALT -> BTC. I just wasn’t finding a profitable path once you accounted for all the fees for each transaction, so you’ll have to give me some specifics and explain what the “right times” are.


#382

Woah…6 months of 5.second price action snapshots? If so, that certainly gives you a ton of back-testing capabilities, but I hadn’t really seen the need to keep more than two or three days worth and when “something interesting” happens, I snapshot the data in that timespan to replay/fixture files for long-term.


#383

Folks, I took a stab at DRYing up the code (i.e. Don’t Repeat Yourself). In working on the features this morning, I was getting lost in all the repeated code and missing some variables along the way when adding new stuff and having a tough time finding that one last variable I didn’t edit to clear out bugs I introduced.

There’s lots more that can be done, but in the interest of not totally shocking everyone learning to code and/or learning PHP, this just starts with introducing a Trend class and a Cda class. Trend handles a short, medium, long period of numbers (doesn’t have to be strictly prices, but for now that’s what it tracks and computes RSI a bit more efficiently while at it, computing only the next set of numbers as new prices are encountered. Cda holds the data for each CDA, formerly replacing all those ad-hoc variables like ${$tick . “rsi”}, etc.

<?php
require 'vendor/autoload.php';

// This bot will trade USDT to the other pairs and back to make more USDT
// It shall use all the trading pairs to make more UDST except the ones we tell it not to use

$interval = 5;            // interval between price sampling
$stperiod = 50;           // Short Term Trend - must be less than $mtperiod
$mtperiod = 90;           // Medium Term Trend - must be less than $ltperiod
$ltperiod = 120;          // Long Term Trend
$tradefile = "BTC.txt";   // The Trade Logging file name
$minspread = 1.1;         // The minimum spread percentage needed for a trade
$minrsi = 45;             // Relative Strength must be below this number to buy
$sellbuffer = 1.003;      // Create a buffer to hold CDA if sell is not profitable
$maxorders = 10;          // Maximum number of concurrent orders

// Do not change any of the flags, we use this to signal the bot what to do and when
$buyready = 0;            // This flag signals the bot that the pair meets rules to buy
$buyprep = 1;             // This flag signals the bot to prepare to buy
$buyord = 2;              // This flag signals the bot to place an order
$sellok = 3;              // This flag signals the bot that the order was completed
$sellready = 4;           // This flag signals the bot to sell
$selldone = 5;            // This flag signals the bot the trade completed
$dontbuy = 6;             // This flag signals the bot we dont want to trade BCASH :P

// Trend Directions
$unset = 0;               // Initial trend is indeterminent until set otherwise.
$down = 1;                // price action is trending downward
$flat = 2;                // price action is flat/unchanged
$up = 3;                  // price action is trending upward

// Standard variables and arrays we use
$replay = 1;
$i = 0;
$cdas = array();
$binance_prices = array();
$time_start = time();
$time_end = 0;
$run_time = 0;
$rpc = 0;
$tpc = 0;
$q = 0;
$cdaorders = 0;
$btcprice = 0;
$btctrend = array();
$bttrend = "WAIT";

class Trend {
  public $period = 0;
  public $label = '';
  public $values = array();
  public $direction = 0;
  public $rsi = 100;
  public $value = 0;
  public $prev_value = 0;
  protected $rsi_gains = array();
  protected $rsi_losses = array();

  function __construct ($label, $period) {
    $this->label = $label;
    $this->period = $period;
  }

  public function stat () {
    global $down;
    global $flat;
    global $up;

    print "\t $this->label:";
    printf("%-14.8F",$this->avg());
    if ($this->direction == $down) printf("%-5s",":DOWN");
    if ($this->direction == $flat) printf("%-5s",":FLAT");
    if ($this->direction == $up) printf("%-5s",":UP");
  }

  // Calculate the Relative Strength Indicator on the Array
  // A Low RSI indicates a buy opportunity
  function compute_rsi () {

    if (empty($this->rsi_gains) OR $this->value == $this->prev_value) {
      array_push($this->rsi_gains, 0);
      array_push($this->rsi_losses, 0);
    
    } elseif ($this->value > $this->prev_value) {
      array_push($this->rsi_gains, $this->value - $this->prev_value);
      array_push($this->rsi_losses, 0);

    } else {
      array_push($this->rsi_losses, $this->prev_value - $this->value);
      array_push($this->rsi_gains, 0);
    }

    if (count($this->rsi_gains) > $this->period) {
      array_shift($this->rsi_gains);
      array_shift($this->rsi_losses);
    }

    $gain_avg = array_sum($this->rsi_gains) / count($this->rsi_gains);
    $loss_avg = array_sum($this->rsi_losses) / count($this->rsi_gains);

    if ($loss_avg > 0) {
      $this->rsi = round(100-(100/(1+($gain_avg/$loss_avg))),3);

    } else {
      $this->rsi = 100;
    }
  }

  public function append ($value) {
    $this->prev_value = end($this->values);
    $this->value = $value;

    array_push($this->values, $value);
    if (count($this->values) > $this->period) array_shift($this->values);
    $this->compute_rsi();
  }

  public function avg () { 
    if (count($this->values) == 0) {
      return 0;
    } else {
      return round((array_sum($this->values) / count($this->values)), 8);
    }
  }

  // Calculate the spread, which is the percentage difference between
  // the highest recorded price and the lowest recorded price in the Trend Array
  public function spread () {
    $low = min($this->values);
    $high = max($this->values);
    return round(((1 - ($low/$high)) * 100), 3);
  }

  public function set_direction ($other) {
    global $down;
    global $flat;
    global $up;

    $avg = $this->avg();
    if ($other < $avg) $this->direction = $down;
    if ($other == $avg) $this->direction = $flat;
    if ($other > $avg) $this->direction = $up;
  }
}

class Cda {

  public $tick = '';                // The ticker symbol for this CDA
  public $st = NULL;                // Short Term Trend
  public $mt = NULL;                // Medium Term Trend
  public $lt = NULL;                // Long Term Trend
  public $tradeflag = 0;            // Set this pair to buyready
  public $buyvalue = 0;             // record what we buy for on this pair
  public $sellvalue = 0;            // record what we sell for on this pair
  public $lasttrade = 0;            // record we have had one trade done
  public $lasttpc = 0;              // record what percentage last the trade was
  public $isset = 1;                // used to signal we are initialised for this pair
  public $trade_start = 0;          // start of currently open trade
  public $trade_cycles = array();   // track each trade cycle length

  function __construct($tick) {
    global $stperiod;
    global $mtperiod;
    global $ltperiod;

    $this->tick = $tick;
    $this->st = new Trend('ST', $stperiod);
    $this->mt = new Trend('MT', $mtperiod);
    $this->lt = new Trend('LT', $ltperiod); 
  }

  function dontbuy() {
    global $dontbuy;
    $this->tradeflag = $dontbuy;
  }

  function update_price($value) {
    $this->st->append($value);
    $this->mt->append($value);
    $this->lt->append($value);
  }
}

// API call to fetch pricing data from Binance
function getprices()
{
  global $replay;
  global $pricefh;
  
  if($replay == 0)
  {
    $api = new Binance\API("<api key>","<secret>");
    $mp = $api->prices();
    $pricefh = fopen("BTC-prices.txt","a") or die("Cannot open file");
    fwrite($pricefh, serialize($mp) . "\n");
    fclose($pricefh);
  }
  else
  {
    if (!$pricefh)
    {
      $pricefh = fopen("BTC-prices.txt","r") or die("Cannot open replay file");
    }
    $mp = unserialize(fgets($pricefh));
    if ($mp == NULL) die("end of price data reached.\n");
  }
  return $mp;
}

if ($replay == 1) 
{
  $simulation_mode = "replaying"; 
}
else 
{
  $simulation_mode = "real-time";
  if(file_exists("BTC-prices.txt"))
  {
    unlink("BTC-prices.txt");
  }
}

// Start of the Loop - can run for months - press CTRL-C to stop
for($i = 0; $i <= 2000000; $i++)
{
  $time_end = time();
  if ($replay == 1) {
    $run_time = round(($i * $interval) / 60, 2);
  } else {
    $run_time = round((($time_end - $time_start)/60),2);
  }
  print "====================================\n";
  print "Iteration = $i ($simulation_mode) \n";
  print "Running Time: $run_time mins \n";
  print "Current BTC Price is: $btcprice T:$bttrend \n";
  print "Current Orders in progress = $cdaorders / $maxorders\n";
  print "Current running percentage = $rpc \n";
  print "====================================\n";

  // Fetch current prices from Binance
  $binance_prices = getprices();

  // Loop through the price data as key and value pairs
  foreach($binance_prices as $key => $value)
  {
    // Track BTC price for display
    if($key == "BTCUSDT")
    {
      $btcprice = $value;
      array_push($btctrend, $value);
      if($i >= $cda->mt->period)
      {
        array_shift($btctrend);
        $btcavg = round((array_sum($btctrend)/$cda->lt->period),8);
        if($value >= $btcavg)
        {
          $bttrend = "UP";
        }
        else
        {
          $bttrend = "DOWN";
        }
      }
    }
    // Only process pairs with BTC
    if(strpos($key, "BTC"))
    {

      // Convert the pair name to lower case in varibale $tick
      // for example the name "BTCUSDT" will become "btcusdt"

      // Use the lower case name to form the leading part of varibales and arrays
      // for exmaple, using "btcusdt" and adding "st" for the short term array
      // will initialise an array called "btcusdtst"
      // as we loop thorugh the pairs, each one gets created for each pair
      // for exmaple "NEOUSDT" will become "neousdtst"
      $tick = strtolower($key);

      // Check if the trading pair has been initialised
      // this covers if Binance add a new trading pair on USDT while we are running
      // if Binance adds new trading pairs while bot is running, we shall
      // ignore them and only use the ones since the bot was started and initialised
      if (!isset($cdas[$tick])) $cdas[$tick] = new Cda($tick);

      $cda = $cdas[$tick];

      // Exclude List - these ones we do not trade

      if($key == "BCHABCBTC") $cda->dontbuy();
      if($key == "BCHSVBTC") $cda->dontbuy();
      if($key == "BCCBTC") $cda->dontbuy();

      // Push data into arrays and shift arrays once we have enough data
      $cda->update_price($value);
      $cda->st->set_direction($value);
      $cda->mt->set_direction($cda->st->avg());
      $cda->lt->set_direction($cda->mt->avg());

      // Wait until we have all the arrays populated with data
      if($i <= $cda->lt->period) {
        if($key == "BCHSVBTC") print "Loading Arrays with data until Iteration " . $cda->lt->period . " - patience, Grasshopper...\n";
      }

      // Arrays are populated, so on with the processing
      else
      {

        // Print out only ones in Buy Order and Sell Ready
        if($cda->tradeflag == $buyord OR $cda->tradeflag == $sellready) 
        {
          printf("%-9s",$key);
          print "\tV:";
          printf("%-14.8F",$value);

          if($cda->tradeflag == $sellready) {
            print "\tCTC:";
            printf("%-3.2F", ($run_time - $cda->trade_start));
          } else {
            print "\tATC:";
            printf("%-3.2F", $cda->trade_cycle_avg);
          }

          print "\t" . $cda->st->stat();
          print "\t" . $cda->mt->stat();
          print "\t" . $cda->lt->stat();

          print "\t  SPREAD:";
          printf("%-03.3F",$cda->lt->spread()); 
          print "%\t  RSI:";
          printf("%-06.3F",$cda->st->rsi);
          printf("/%-06.3F",$cda->mt->rsi);
          printf("/%-06.3F",$cda->lt->rsi);

          if($cda->tradeflag == $buyord) $cdastatus = "Buy Order";
          if($cda->tradeflag == $sellready) $cdastatus = "Sell Ready";
          if($cda->tradeflag == $sellready)
          {
            $ctp = round(((($value - $cda->buyvalue)/$cda->buyvalue)*100),3);
            print "\t S:$cdastatus \tBV:$cda->buyvalue CTP:$ctp";
          }
          else
          {
            print "\tS:$cdastatus";
          }
          if($cda->lasttrade == 1)
          {
            print "   LastTPC:$cda->lasttpc";
          }
          print "\n";
        }

        // Trading rules start here
        // ========================

        // CDA is trending up so set to buyprep
        if($cda->tradeflag == $buyready AND $cda->st->direction==$up AND $cda->mt->direction==$up AND $cda->lt->direction==$up)
        {
          printf("%-9s",$key);
          print "Was Buyready, now Buyprep V:$value\n";
          $cda->tradeflag = $buyprep;
        }

        // CDA was buyprep, now trending down, set to buyord if reasonable spread
        if($cda->tradeflag == $buyprep AND $cda->st->direction==$down AND $cda->mt->direction==$down AND $cda->lt->direction==$down AND $cda->lt->spread() >= $minspread)
        {
          printf("%-9s",$key);
          print "Was Buyprep, now Buyord V:$value\n";
          $cda->tradeflag = $buyord;
        }

        // CDA stopped trending down and is ready to buy
        if($cda->tradeflag == $buyord AND $cda->st->direction==$up AND $cda->mt->direction!=$down)
        {
          if($cda->lt->rsi <= $minrsi)
          {
            if($cdaorders < $maxorders)
            {
              printf("%-9s",$key);
              print "Was Buyord, now Buy V:$value\n";
              // Assume we buy at the current value
              $cda->buyvalue = $value;
              $cda->tradeflag = $sellok;
              $cda->trade_start = $run_time;
              $cdaorders = $cdaorders + 1;
              $fh = fopen($tradefile, "a") or die("Cannot open file");
              fwrite($fh, "========================== \n");
              fwrite($fh, "Runtime $run_time \n");
              fwrite($fh, "Buy on $key BV:$cda->buyvalue \n");
              fwrite($fh, "========================== \n");
              fclose($fh);
            }
          }
          else
          {
            printf("%-9s",$key);
            print "RSI Check not meeting Minimum, resetting back to Buy Prep\n";
            $cda->tradeflag = $buyprep;
          }
        }

        // Buy Order on CDA placed, do order tracking here to make sure order completes
        if($cda->tradeflag == $sellok)
        {
          // Since we are not placing an order, we just assume it completed
          $cda->tradeflag = $sellready;
        }

        // CDA is sellready and is no longer trending upwards - time to sell
        if($cda->tradeflag == $sellready AND $cda->st->direction!=$up AND $cda->mt->direction!=$up)
        {
          // Assume we sell at the current value and not sell if not meeting a minimum
          $cdabuff = $cda->buyvalue * $sellbuffer;
          if($value > $cdabuff)
          {
            $cda->sellvalue = $value;
            $cda->tradeflag = $selldone;
          }
          else
          {
            printf("%-9s",$key);
            print "Did not meet minimum sell amount\n";
          }
        }

        // CDA is selldone
        if($cda->tradeflag == $selldone)
        {
          // Sell Order on CDA placed, do order tracking here to make sure order completes
          // Since we are not placing an order, we just assume it completed
          $q = round(((($cda->sellvalue - $cda->buyvalue)/$cda->buyvalue)*100),3);
          $tpc = $q - 0.2;
          $rpc = round($rpc + ($tpc/$maxorders),3);
          $cda->lasttrade = 1;
          $cda->lasttpc = $tpc;
          $cda->tradeflag = $buyready;
          array_push($cda->trade_cycles, ($run_time - $cda->trade_start)); 
          $cda->trade_cycle_avg = round((array_sum($cda->trade_cycles)/count($cda->trade_cycles)),3);
          $cdaorders = $cdaorders - 1;
          printf("%-9s",$key);
          print "Sell Done BV:$cda->buyvalue SV:$cda->sellvalue TPC:$tpc ATC:$cda->trade_cycle_avg\n";
          $fh = fopen($tradefile, "a") or die("Cannot open file");
          fwrite($fh, "========================== \n");
          fwrite($fh, "Runtime $run_time \n");
          fwrite($fh, "Sell Done on $key BV:$cda->buyvalue SV:$cda->sellvalue TPC:$tpc RPC:$rpc ATC:$cda->trade_cycle_avg\n");
          fwrite($fh, "========================== \n");
          fclose($fh);
        }

      }
    }
  }
  if ($replay == 0) sleep($interval);
}
?>