Welcome, Guest
You have to register before you can post on our site.

Username
  

Password
  





Search Forums



(Advanced Search)

Forum Statistics
» Members: 39,322
» Latest member: DishisdesignJewellery
» Forum threads: 929
» Forum posts: 6,494

Full Statistics

Online Users
There are currently 345 online users.
» 0 Member(s) | 345 Guest(s)

Latest Threads
Gekko 0.6 released
Forum: Announcements
Last Post: lsm99dna
08-06-2025, 08:15 PM
» Replies: 124
» Views: 334,083
An official Gekko service...
Forum: Announcements
Last Post: Ruslanjoshua
07-24-2025, 06:27 AM
» Replies: 111
» Views: 248,290
New Gekko UI in the works
Forum: Announcements
Last Post: celemtine
07-03-2025, 07:24 AM
» Replies: 185
» Views: 326,151
Gekko with malware spotte...
Forum: Announcements
Last Post: pugoing
07-01-2025, 02:29 AM
» Replies: 212
» Views: 277,731
Gekko development status ...
Forum: Announcements
Last Post: sanmarconns
10-21-2024, 06:34 PM
» Replies: 744
» Views: 1,482,291
How to add Binance Future...
Forum: Technical Support
Last Post: Xavier32
10-07-2021, 02:20 PM
» Replies: 47
» Views: 134,651
Bittrex Configuration hel...
Forum: Bittrex
Last Post: yirzolusto
10-07-2021, 07:39 AM
» Replies: 6
» Views: 23,029
[Question] Why does gekko...
Forum: General Discussion
Last Post: cryptocurrency0
10-06-2021, 01:16 PM
» Replies: 16
» Views: 57,455
a couple of technical Que...
Forum: Technical Support
Last Post: mtom78632
10-06-2021, 11:08 AM
» Replies: 25
» Views: 72,137
crex24
Forum: Other exchanges
Last Post: marketingseo
10-05-2021, 09:47 AM
» Replies: 216
» Views: 515,548

 
  Generate binance-markets.json
Posted by: docdrow - 06-27-2018, 09:27 PM - Forum: Third Party Software - Replies (5)

Hey all,

  I'm sure someone out there has already done this, or perhaps Gekko will eventually acquire the ability to do this.  For now though I've whipped up this little program that will generate binance-markets.json for you.


Code:
/* generate binance-markets.json for Gekko
* Written by George Shearer --- doc@drow.org
* Notes: There's not much error checking here, I just threw this together because I got tired of manually updating the file.
* I hope others can benefit from this.
*
* Thank you Mike van Rossum and all other Gekko contributors
*
* You can build this on most any modern linux platform:
*
* gcc -Wall -s -o genmarkets genmarkets.c -lcurl -ljson-c */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <json-c/json.h>
#include <curl/curl.h>

#define SUCCESS 0
#define FAIL    1

#define OUTPUT_FILE   "binance-markets.json"
#define BINANCE_URL   "https://api.binance.com/api/v1/exchangeInfo"

#define MAX_MARKETS 4000
#define MAX_CURRENCIES 50
#define MAX_ASSETS 2000

struct market
{
 char currency[10];
 char asset[10];
 double min_price;
 double min_qty;
 double stepsize;
 double min_notional;
 int currency_precision;
 int asset_precision;
} markets[MAX_MARKETS];

struct currency
{
 char currency[10];
} currencies[MAX_CURRENCIES];

struct asset
{
 char asset[10];
} assets[MAX_ASSETS];

struct curl_fetch_st {
   char *payload;
   size_t size;
};

int total_currencies = 0;
int total_assets = 0;
int total_markets = 0;

/* callback for curl fetch */
size_t curl_callback (void *contents, size_t size, size_t nmemb, void *userp) {
   size_t realsize = size * nmemb;                             /* calculate buffer size */
   struct curl_fetch_st *p = (struct curl_fetch_st *) userp;   /* cast pointer to fetch struct */

   /* expand buffer */
   p->payload = (char *) realloc(p->payload, p->size + realsize + 1);

   /* check buffer */
   if (p->payload == NULL) {
     /* this isn't good */
     fprintf(stderr,"ERROR: Failed to expand buffer in curl_callback");
     /* free buffer */
     free(p->payload);
     /* return */
     return(-1);
   }

   /* copy contents to buffer */
   memcpy(&(p->payload[p->size]), contents, realsize);

   /* set new buffer size */
   p->size += realsize;

   /* ensure null termination */
   p->payload[p->size] = 0;

   /* return size */
   return(realsize);
}

/* fetch and return url body via curl */
CURLcode curl_fetch_url(CURL *ch, const char *url, struct curl_fetch_st *fetch) {
   CURLcode rcode;                   /* curl result code */

   /* init payload */
   fetch->payload = (char *) calloc(1, sizeof(fetch->payload));

   /* check payload */
   if (fetch->payload == NULL) {
       /* log error */
       fprintf(stderr, "ERROR: Failed to allocate payload in curl_fetch_url");
       /* return error */
       return CURLE_FAILED_INIT;
   }

   /* init size */
   fetch->size = 0;

   /* set url to fetch */
   curl_easy_setopt(ch, CURLOPT_URL, url);

   /* set calback function */
   curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, curl_callback);

   /* pass fetch struct pointer */
   curl_easy_setopt(ch, CURLOPT_WRITEDATA, (void *) fetch);

   /* set default user agent */
   curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl-agent/1.0");

   /* set timeout */
   curl_easy_setopt(ch, CURLOPT_TIMEOUT, 5);

   /* enable location redirects */
   curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, 1);

   /* set maximum allowed redirects */
   curl_easy_setopt(ch, CURLOPT_MAXREDIRS, 1);

   /* fetch the url */
   rcode = curl_easy_perform(ch);

   /* return */
   return rcode;
}

int
do_curl(const char *url, char **storage)
{
 CURL *curl_handle;
 CURLcode curl_result;

 struct curl_fetch_st curl_fetch;
 struct curl_fetch_st *cf = &curl_fetch;

 curl_handle = curl_easy_init();
 curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 20L);
 curl_result = curl_fetch_url(curl_handle, url, cf);
 curl_easy_cleanup(curl_handle);

 /* check return code */
 if (curl_result != CURLE_OK || cf->size < 1)
 {
   fprintf(stderr, "do_curl(): Error grabbing '%s': %s", url, curl_easy_strerror(curl_result));
   return(1);
 }

 /* check payload */
 if (cf->payload != NULL) {
   *storage = cf->payload;
   return(SUCCESS);
 } else {
   fprintf(stderr, "do_curl(): Error storing payload from '%s'\n",url);
   return(FAIL);
 }
}

void
add_currency(char *currency)
{
 int x = 0;
 for(;x < total_currencies && x < MAX_CURRENCIES;x++)
   if(!strcmp(currencies[x].currency, currency))
     return;
 total_currencies++;
 strncpy(currencies[x].currency, currency, 10);
}

void
add_asset(char *asset)
{
 int x = 0;
 for(;x < total_assets && x < MAX_ASSETS;x++)
   if(!strcmp(assets[x].asset, asset))
     return;
 total_assets++;
 strncpy(assets[x].asset, asset, 10);
}

int
get_binance_info(void)
{
 char *data;
 json_object *bjson;
 enum json_tokener_error jerr = json_tokener_success;

 printf("Connecting to %s ... ",BINANCE_URL); fflush(stdout);

 if(do_curl(BINANCE_URL, &data))
 {
   puts("failed");
   return(FAIL);
 }
 puts("success");

 bjson = json_tokener_parse_verbose(data, &jerr);
 free(data);

 if(jerr == json_tokener_success)
 {
   json_object *symbols;

   if((symbols = json_object_object_get(bjson, "symbols"))!=NULL)
   {
     int x;
     
     total_markets = json_object_array_length(symbols);

     for(x = 0; x < total_markets && x < MAX_MARKETS; x++)
     {
       json_object *tmpmkt = json_object_array_get_idx(symbols, x);
       json_object *filters;

       strncpy(markets[x].currency, json_object_get_string(json_object_object_get(tmpmkt, "quoteAsset")), 10);
       markets[x].currency_precision = atoi(json_object_get_string(json_object_object_get(tmpmkt, "quotePrecision")));
       add_currency(markets[x].currency);

       strncpy(markets[x].asset, json_object_get_string(json_object_object_get(tmpmkt, "baseAsset")), 10);
       markets[x].asset_precision = atoi(json_object_get_string(json_object_object_get(tmpmkt, "baseAssetPrecision")));
       add_asset(markets[x].asset);

       if((filters = json_object_object_get(tmpmkt, "filters"))!=NULL)
       {
         int y, numfilters = json_object_array_length(filters);

         for(y = 0; y < numfilters; y++)
         {
           json_object *tmpfilter = json_object_array_get_idx(filters, y);
           const char *filter_type = json_object_get_string(json_object_object_get(tmpfilter, "filterType"));

           if(!strcmp(filter_type, "PRICE_FILTER"))
             markets[x].min_price = atof(json_object_get_string(json_object_object_get(tmpfilter, "minPrice")));
           else if(!strcmp(filter_type, "MIN_NOTIONAL"))
             markets[x].min_notional = atof(json_object_get_string(json_object_object_get(tmpfilter, "minNotional")));
           else if(!strcmp(filter_type, "LOT_SIZE"))
           {
             markets[x].min_qty = atof(json_object_get_string(json_object_object_get(tmpfilter, "minQty")));
             markets[x].stepsize = atof(json_object_get_string(json_object_object_get(tmpfilter, "stepSize")));
           }
         }
       }
     }
     printf("Loaded %d markets\n",total_markets);
   }
 }
 json_object_put(bjson);
 return(SUCCESS);
}

void
gen_gekko_markets(const char *output)
{
 int x = 0;
 FILE *fp = fopen(OUTPUT_FILE, "w");

 fprintf(fp, "{\n  \"currencies\": [");
 for(; x < total_currencies; x++)
 {
   if(x)
     fprintf(fp, ",");
   fprintf(fp, " \"%s\"", currencies[x].currency);
 }

 fprintf(fp, " ],\n  \"assets\": [");
 for(x = 0; x < total_assets; x++)
 {
   if(x)
     fprintf(fp, ",");
   fprintf(fp, " \"%s\"", assets[x].asset);
 }
 fprintf(fp, " ],\n  \"markets\": [\n");

 for(x = 0; x < total_markets; x++)
 {
   char fmt[500], temp[100];

   if(x)
     fprintf(fp, ",\n");

   strcpy(fmt, "    {\n      \"pair\": [\n        \"%s\",\n        \"%s\"\n      ],\n      \"minimalOrder\": {\n        \"amount\": %f,\n        \"price\": %");
   sprintf(temp, "0.%df", markets[x].asset_precision);
   strcat(fmt, temp);
   strcat(fmt, ",\n        \"order\": %f\n      }\n    }");
   fprintf(fp, fmt, markets[x].currency, markets[x].asset, markets[x].min_qty, markets[x].min_price, markets[x].min_notional);
 }

 fprintf(fp, "\n  ]\n}\n");
 fclose(fp);
}

int
main(int argc, char *argv[])
{
 memset((void *)markets, 0, sizeof(struct market) * MAX_MARKETS);
 memset((void *)currencies, 0, sizeof(struct currency) * MAX_CURRENCIES);
 memset((void *)assets, 0, sizeof(struct asset) * MAX_ASSETS);

 if(get_binance_info() == SUCCESS)
 {
   printf("Generating gekko file \"%s\" ...", OUTPUT_FILE); fflush(stdout);
   gen_gekko_markets(OUTPUT_FILE);
   puts("done");
   exit(SUCCESS);
 }
 exit(FAIL);
}


  [SHARE] PSAR Indicator and Strat
Posted by: PatTrends - 06-27-2018, 05:22 PM - Forum: Strategy Development - No Replies

Hey all,

I've really been enjoying Gekko lately and thought I would share a PSAR Indicator/Strat I wrote.

PSAR, by itself, is an awful strategy - particularly in consolidating markets. The included strategy is provided more as an example, and simply buys and sells on trend reversals. That said, PSAR is a great way to set dynamic stop-losses (as opposed to static values or percentage-based trailing stop-losses).

While I didn't specifically port the Tulip code, I did verify my indicator generates the same results. I also heavily commented the indicator so newer users can try to figure out what its doing. If you can improve my code, let me know!

PSAR.toml

Code:
acceleration = 0.02
maximum = 0.2
persistence = 2



strategies/PSAR.js

Code:
// PSAR strat
// PT 06/26/18

// let's create our own method
var method = {};

// prepare everything our method needs
method.init = function() {
 this.name = 'PSAR';
 this.inTrade = false;

 this.requiredHistory = 100;

 // define the indicators we need
 this.addIndicator('psar', 'PSAR', this.settings);


};

// what happens on every new candle?
method.update = function(candle) {
 // nothing!
};


method.log = function(candle) {
 // nothing!
};

method.check = function() {
 var psar = this.indicators.psar;
 var trend = psar.trend;
 var trendDuration = psar.duration;
 // var psarResult = psar.psar;


 //Sell Signal
 if(trend == "down" && trendDuration >= this.settings.persistence && this.inTrade){
   this.advice('short');
   this.inTrade = false;

 }

 //Buy Signal
 if(trend == "up" && trendDuration >= this.settings.persistence && !this.inTrade){
   this.advice('long');
   this.inTrade = true;
 }

};

module.exports = method;



indicators/PSAR.js

Code:
//PSAR
//PT-06/26/2018

var Indicator = function(config) {
 this.input = 'candle';
 this.acceleration = config.acceleration;
 this.accelerationStep = config.acceleration;
 this.persistence = config.persistence;
 this.maximum = config.maximum;
 this.psar = null;
 this.initPsar = null;
 this.psarEpAcc = null;
 this.extremePoint = null;
 this.trend = null;
 this.previousTrend = null;
 this.duration = 0;

 this.candleHistory = [];

};


Indicator.prototype.update = function(candle) {

 this.candleHistory.push(candle);

 if(this.candleHistory.length >= 3) {

   //Define candles
   // var candle = this.candleHistory[2];
   var previousCandle = this.candleHistory[1];
   var prevPreviousCandle = this.candleHistory[0];

   //first run
   if (this.psar === null) {
     this.extremePoint = prevPreviousCandle.low;
     this.psar = prevPreviousCandle.high;
     this.previousTrend = 'down';
   }


   //PSAR EQUATION
   this.psarEpAcc = (this.psar - this.extremePoint) * this.acceleration;

   //PAST TREND LOGIC
   if (this.previousTrend == 'down') {

     //calculate initial psar
     this.initPsar = Math.max((this.psar - this.psarEpAcc), previousCandle.high, prevPreviousCandle.high);

     //calculate PSAR
     this.psar = (candle.high < this.initPsar) ? this.initPsar : this.extremePoint;

     //increase duration count
     this.duration++;

   }

   if (this.previousTrend == 'up') {

     //calculate initial psar
     this.initPsar = Math.min((this.psar - this.psarEpAcc), previousCandle.low, prevPreviousCandle.low);

     //calculate PSAR
     this.psar = (candle.low > this.initPsar) ? this.initPsar : this.extremePoint;

     //increase duration count
     this.duration++;

   }

   //Define trend
   this.trend = (this.psar > candle.close) ? 'down' : 'up';

   //CURRENT TREND LOGIC
   if(this.trend == 'down'){
     //calculate Extreme point
     this.extremePoint = Math.min(this.extremePoint, candle.low);
   }
   if(this.trend =='up'){
     //calculate Extreme point
     this.extremePoint = Math.max(this.extremePoint, candle.high);
   }

   //UPDATE LOGIC
   //If the trend stays the same and the extreme point doesn't equal the previous extreme point and the acceleration factor is less than the maximum factor...
   if (this.trend == this.previousTrend && this.extremePoint != this.previousExtremePoint && this.acceleration <= this.maximum) {

     //Increment the acceleration value
     this.acceleration += this.accelerationStep;
   }

   //If the trend stays the same and the extreme point value stays the same...
   if (this.trend == this.previousTrend && this.extremePoint == this.previousExtremePoint) {
     //Acceleration stays the same
   }

   // If the current trend doesn't equal the previous trend...
   if (this.trend != this.previousTrend) {

     //Set the acceleration to the initial value
     this.acceleration = this.accelerationStep;

     //reset duration count
     this.duration = 0;
   }

   //set values/array for next run
   this.previousExtremePoint = this.extremePoint;
   this.previousTrend = this.trend;
   this.candleHistory.shift();
 }

};

module.exports = Indicator;


  Issue on Kraken
Posted by: flychicken - 06-26-2018, 05:03 AM - Forum: Technical Support - Replies (2)

[kraken.js] (getTrades) returned an error, retrying: Service:Unavailable


  Gekko 0.6 released
Posted by: askmike - 06-24-2018, 10:31 AM - Forum: Announcements - Replies (124)

[EDIT]
0.6 is OUT! Get the release here
[/EDIT]


Gekko 0.6.0 is a major upgrade of the Gekko project, you can find some background information here.

The release of 0.6.0 is now finally ready for testing! Note that this release only includes:

- rewritten core & event logic.
- rewritten core plugins such as baseStrategy, performanceAnalyzer, paperTrader and (live) trader.
- Addition of the Gekko Broker library for live order execution.

LIMITATIONS:

- It does not support all exchanges previously supported by Gekko, see here for a list: https://github.com/askmike/gekko/blob/de....md#status


To test the 0.6 pre release:

- Update Gekko to the latest release.
- Make sure you run a new Node version (minimum is updated from 6.0 to 8.11.2)
- Besides the usual installation, please also install Gekko Broker dependencies, see here: https://github.com/askmike/gekko/blob/pr...pendencies

If you find any issue with the new release please open an issue on github (or open a new thread on the forum).


  How to create an automated trailing stop limit order?
Posted by: Elorpar - 06-24-2018, 08:09 AM - Forum: Automated Trading - Replies (1)

Let's say I have a stock trading at $100. If it gets to $120 I want to have a trailing stop limit order initiated with a trailing amount of $1 let's say.
I only want the trailing stop limit to take effect once the stock reaches $120.
How to create an strategy to automate this process?


  How to create an automated trailing stop limit order?
Posted by: Elorpar - 06-24-2018, 08:08 AM - Forum: Strategy Development - No Replies


Let's say I have a stock trading at $100. If it gets to $120 I want to have a trailing stop limit order initiated with a trailing amount of $1 let's say.
I only want the trailing stop limit to take effect once the stock reaches $120.
How to create an strategy to automate this process?


  Does gekko store data in memory or database?
Posted by: gsxr - 06-22-2018, 03:16 AM - Forum: General Discussion - Replies (5)

I had run --import to load initial data of over 5000 mintues using CLI.

I found the postgresql database loaded with some records successfully (with 6718 records).

Live paper trader was successfully run with slow moving average indicator loaded with 1000 x 5 min candles.

I did make sure config.candleWriter = { enabled: true }

I supposed new candle data will be inserted to database for all the subsequent ticker update. However, I found the number of records did not increased.

After a day of running, I restarted gekko live trader using the same config file. Datasets were not stitched with following log...

---- DEBUG LOG ----
The trading method requests 5000 minutes of historic data. Checking availablity..
Available local data:
from: 5000 minutes ago
to: 1176 minutes ago
Usable local data available, trying to match with exchange data..
Preventing Gekko from requesting 1191 minutes of history.
Fetching exchange data since 240 minutes ago
Available exchange data:
from: 240 minutes ago
to: 180 minutes ago
Unable to stitch datasets.
Not seeding locally available data to the trading method.
The exchange does not return enough data. 0 minutes are still missing.
---- END OF LOG ----

It seems like that 1176 minutes running of live paper trader did not save candle data into database but only in memory. Therefore that period of data was lost after gekko restarted.

I also don't understand why gekko fetch exchange data from 240 min to 180 min? I have seen this happened many times before.

Since it doesn't make sense to wait for a 5000 minutes warmup, my workaround is to run --import using CLI again to load the data after every gekko restart.

Does anyone know if this is a constraint of did I miss anything in the configuration for gekko to save live data into the database?

Thanks for advice.


Bug [TUT] CONFIG-FILE CONVERTOR FOR CLI GEKKO
Posted by: susitronix - 06-21-2018, 04:25 PM - Forum: Guides - Replies (1)

for the CLI-gekko (terminal gekko) we must set up the sample-config.js.

our strategie has its "my_strategie.toml" file, but in the config IT MUST BE AS JSON-style CODE.

if you do not use the backtest Machine from xFFFFF,
you can simply use an online tool that makes the conversation for you.
this is very easy and never makes an error.!!!

go to this page:
toml-json convertor

here simply copie paste your .toml code

Code:
[___trendatron___]
#<<<>>><<<>>>#
__longPos = false

#>>><<<>>><<#
[_backtest_start_]
__________day = 1
______month = 4
_____hour = 18
_minute = 38
candle_size = 1
#<<<>>><<<>>>#

[RSI]
optInTimePeriod = 30

[trsRSI]
high = 68
low = 30

[___stop_abs____]
stop_abs = -1.7
abs_exp = -250

now copie/paste the created .json code into your sample-config.js, as you can learn from my other Tutorial:
Code:
{
 "___trendatron___": {
   "__longPos": false
 },
 "_backtest_start_": {
   "__________day": 1,
   "______month": 4,
   "_____hour": 18,
   "_minute": 38,
   "candle_size": 1
 },
 "RSI": {
   "optInTimePeriod": 30
 },
 "trsRSI": {
   "high": 68,
   "low": 30
 },
 "___stop_abs____": {
   "stop_abs": -1.7,
   "abs_exp": -250
 }
}


TUT#5
just scroll down and find the thread :-)

thanks to xFFFFF, Matias Korhonen, and Ankasem
hope this helps


  Backtest error??
Posted by: Kris191 - 06-18-2018, 05:03 AM - Forum: Technical Support - Replies (1)

Code:
/root/gekko/node_modules/sqlite3/lib/trace.js:27
                   throw err;
                   ^

TypeError: Cannot read property 'currency' of undefined
   at PerformanceAnalyzer.calculateReportStatistics (/root/gekko/plugins/performanceAnalyzer/performanceAnalyzer.js:143:30)
   at PerformanceAnalyzer.bound [as calculateReportStatistics] (/root/gekko/node_modules/lodash/dist/lodash.js:729:21)
   at PerformanceAnalyzer.finalize (/root/gekko/plugins/performanceAnalyzer/performanceAnalyzer.js:180:23)
   at PerformanceAnalyzer.bound [as finalize] (/root/gekko/node_modules/lodash/dist/lodash.js:729:21)
   at /root/gekko/core/gekkoStream.js:46:25
   at /root/gekko/node_modules/async/dist/async.js:3205:16
   at replenish (/root/gekko/node_modules/async/dist/async.js:1030:17)
   at iterateeCallback (/root/gekko/node_modules/async/dist/async.js:1015:17)
   at /root/gekko/node_modules/async/dist/async.js:988:16
   at /root/gekko/core/gekkoStream.js:47:12
--> in Database#all('\n    SELECT * from candles_BTC_TRX\n    WHERE start <= 1529297820 AND start >= 1529298600\n    ORDER BY start ASC\n  ', [Function])
   at Reader.get (/root/gekko/plugins/sqlite/reader.js:98:11)
   at Reader.bound [as get] (/root/gekko/node_modules/lodash/dist/lodash.js:729:21)
   at Market.get (/root/gekko/core/markets/backtest.js:61:15)
   at Market.bound [as get] (/root/gekko/node_modules/lodash/dist/lodash.js:729:21)
   at Market.processCandles (/root/gekko/core/markets/backtest.js:105:10)
   at bound (/root/gekko/node_modules/lodash/dist/lodash.js:729:21)
   at Statement.<anonymous> (/root/gekko/plugins/sqlite/reader.js:108:5)
 Anyone now the reason for this error when a CLI backtest has finished? bT has been working but today it isnt,

Can someone help?


  Gekko CLI - Unable to buy for ETH/USD on Gemini
Posted by: crypto49er - 06-17-2018, 07:45 PM - Forum: Technical Support - Replies (1)

Hi all,

I know Gemini isn't a popular exchange (at least not talked about much in this forum). I'm trying to set it because I want to have a "set it and forget it" place to let Gekko do its thing and I will just check once a month on the performance of the strat.

So I just started live trading on Gekko CLI on Gemini the other day and I'm getting this error message. Anyone else experience this error message before? Any fixes? I will share my solution if I find it. Thanks.

Code:
2018-06-17 15:10:31 (ERROR):    unable to buy Error: Invalid quantity for symbol ETHUSD: 0.10618643
    at Request._callback (/home/jack/gekko/node_modules/gemini-exchange-coffee-api/lib/gemini.js:64:19)
    at Request.self.callback (/home/jack/gekko/node_modules/gemini-exchange-coffee-api/node_modules/request/request.js:123:22)
    at Request.emit (events.js:160:13)
    at Request.<anonymous> (/home/jack/gekko/node_modules/gemini-exchange-coffee-api/node_modules/request/request.js:893:14)
    at Request.emit (events.js:165:20)
    at IncomingMessage.<anonymous> (/home/jack/gekko/node_modules/gemini-exchange-coffee-api/node_modules/request/request.js:844:12)
    at IncomingMessage.emit (events.js:165:20)
    at endReadableNT (_stream_readable.js:1101:12)
    at process._tickCallback (internal/process/next_tick.js:152:19) undefined