From 449ce20445bc0c4d21d6ac7e8a0d1c9cae4de01a Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 25 Apr 2018 20:09:51 +0200 Subject: [PATCH] Backtest Mode Running --- .vs/AACloud/v15/.suo | Bin 94720 -> 96256 bytes Assistant.js | 428 +++++++++++++++++++------------ Context.js | 55 ++-- Run.js | 2 +- Trading Bot Process Main Loop.js | 16 +- this.config.json | 4 +- 6 files changed, 311 insertions(+), 194 deletions(-) diff --git a/.vs/AACloud/v15/.suo b/.vs/AACloud/v15/.suo index 8411265a36494d475a6f311833b46f562701a416..4417d86ae8d29deaa2b52a7abd9912efa892a19c 100644 GIT binary patch delta 4654 zcmeI03s98T7018x@$CZ3vMjJ53d-Vht-EGXd_)0vVR@*atkFCSB7}#JF-Tc722nSu zCian5_FAqbiKUw~+9Zv-o4B@h%v#e7wN4{xnz2@^q?u`(IvFO~raDvU|L!ATY8*Q0 zV?tFE%#o3 z?Zky&(I!~jyI8y2E~m-tukw;OG#xC3mGzY8Fsq!vw#2yOFNmHfd5n}%oh^!L0UmC$ zoVaPMLdlx+9X#cOnP3~J1BGBNNB|Q+H+xE%nfz^NVg^xWLoZ={N@imfG+m^|25AA$ zf%)J8Fb6CJcCZk{0x`n(BXV6tJ_H#D#QJN2XcH}!%&E#Ay#&^rNKF~!dN3WV?sR6V zRzX<~CGGFgP7%2o6&;<vnL!o>S&$on!(XM6n%W zc(Emupcep^6=$tbLm34YfzeBb`Xtk1N3{qr{nt=Y)K#$uziXtZ3`3;s1-nj zTC6}}zmCgJbmGYdM3}$IC_X!R2DI;i3a}E)V!83Dv+jc?f+YeZdfy9~3(7$>M9ZMp z08uYWeWx=uS%j$(w1PEYEqgY8LbY&N6c;RQ;1LkE3=SHhKMLBxR*(*&b@36MWJ!`i z3{Zdys6irNS-K3P4oVWxgJh5bQh@=8$^BN{%jD6vsm_oj4X{VGTj>|_*e+xJ>wpiyOUtvD$ffxPtYI;$4#NJ8qQy#*RObKX&bP@_4gYmUc=!iGv~< zMJ{dZZ9QBXmy9OLux8qj%P@%I(OgqRl-S=iCcRLG!>kGWSa0K8Z?nc@>Q%I8ADzp8 z&15FOvHddn_Oy|@302-?*53I9cUIF~{=2sKsXS&!D6fb+9__~3%u89ZWfe18E{lll zRz?;PbvPpa8&9ca-|_L-D?RpV|1M&uEQNmBJ4oIaWbZ!N=h2g~1OYAuWuP1^1dG68 zPyv{go%MFeBXhk+4ms%l#6jP|Q=}r1+<0F+nbksDF8Ob~G+FT*_diFh*i_&>toV-V z<>XzcdjdQOc7tM;b8Iy^*ki{sXC1`z%Rn4>5p+?0i04BQ+hNEfzz1H9*nWaMMK2z4 zrV4j`Z@xk7jo0(Ny(4$qv(phL(nA@D?uox`=QqfETK0NnU+@;+{MSxu72laEHEI3M zw+|6JT%}?84{r9m&$(kf=|;ct&CBEo>izn628fwt9jm&iWj*KL4Gg4HOFT1PGR4#) zPPsKym-ujuB#9Fwq=n`5|A6b#Nz2^*%ieZsmZWn4=f@m9!LiE0wd@G z9P9%kTZk<2GydEnrkYD!IeB~lfQxoF}?{u=vQq~a)K02~89 z0zU>X9dV8#c6PIl>&8<*onr$ZwJ~=?Jv()Eg+J?BLrg*w(y++FvC=`lQAKs^(2nF7 zm&@qwRd{DA=f;QH7S{JtwP& zElIB%w#e!0UAg>Z0ZmA{o$(fT+k28P=u zgFrA*8LfZ_Y4z7 z^jLXs4xd;?S+V!Or4g}YM+T;`vLYQ1+Q`I~88YY*PIek(7v^XE>z&$fE@SU@|IXAA z+NPmj;}>sgyOjCia@X>{Nz>+ao1|gM`yh+-GPdH6&=zT<0Z3bNds)p zr4)8wZ%P1OR1>RuS_wE7QwK49S{(1Kq+{&8ZreXM;%Jl~a6d#rg$JAQVzE7dUwiH+ zO+Zsa$B6B`G%nDyn2bXManCABXG?da@IO}5J9KpDaCI9dv%=rv6|yKD-;SH%=WeN3 onulfAFfuIojA_=BPaf$g_Jxbsp{oXec0&%a8GnA9m39~Y1K*{0umAu6 delta 3382 zcmdUxdr;KZ7037db{AM!mWAbIAYs8+*7)dx@r8N(APZtd${NMg5P=FNZcztrs?8QGL}lF2weQtOx*wfw=v{E^9EM;x1(y8WJgq(*JW zcJfaz^WmI(?!CWz?z!jQyWR<-cigzgS*Yk@#j4XygK7jf!r?Gm_28*tcb;NVl|Q}P zd*RR24vV@=bSvG;H4@j9+9V4sRY2^oE)-5BMLetbn$8m}C`p10IcEOcqo}O_;(mi` z+5nV?z#OmzG=WFJ0+0fxf?tYP40F==LgV3F=0X3VIAh3bO@PMpvQ>qg1^l28RD${7 z2VgN+0vZALJs6Wef-C{eU>RWlp9b6~UMhs!STJP~tckIf^^mhc9VqW}=TU9nvVyFx zSZjRTy{K>RbLUw1K)&RcwD-UhU=1h{B_`Y4hoM!2)qqFyesIK|1ao6HHzY4AURL(G zZRssAwgL{`I`FJ`(==7x42`>WLVCc~Se8>CXW!?a zsR-|sBCV+QQr598q>{C0X_x1(d0(yQTybXFjIwg0zDAHF;_sEVWatYOK*4j7ro9-?&SZ4P)1=Ok)c1v(8F0gRCpT@%7IHH zw{N8vgnH5vwA51(rFxKLn{}PK23@&sjrg#qQM0#=QNTA!QD5_J@->ntsH12%-c}S} zJE9RnH160tg}sXQ)@k9_NC_MqB{8ZmN#X6`iw{MFmv;R(8~D!m44VDDF}=TE2^_U5 z>Dl;(3H%K7f|o>Lppg!U=L31=kD>l+05`&}m>hzviRninhd}_mAG7@qG8of;JM5lH zcOCP<9!}q5m&kX{s#*V_MV6{v9W@(#N9>yY>=^lfulT=G0w32UP@3jGIYj<*3foQ_ z@JLuh-DhU8|I>5Y#CdC?|F{wu>s9oKlHAlri)H>QvZR({yE&~{R*J%lXXGbSX`UFn zkSV=esUo!%_FS~HEWJZJ-~X9_WeM6J^?z&-O&5LohWIo`S|}r(?-qVKxGT#tbbVJ2 zucbWk(dCE5p~k4M>P5R~`ZPhPpAXSlG4Z)ahPtQ`T~_LyI;WQRMXNq}BsqE+88$Brg`1gloorUTRKZk=NWW;r36^`-Dm7H{au=&^y?BF%f4Qyt^yQmmW^iXZ?W1fJ^?3P7OmTe^_gtx_(#e}LwZo(}eX*ZJ#b@`3{~;gv!<-LZ zePr(=TYJl%bILS}p`LWvXr%e^eP1Kq3S0gk;>ka?;^hwU97H@xS~DnLj@oF4JYl9e zS^Vlp%h*U46gzP7`H>T=$SS{FLW}yX6cQ7Y`SM^AhBj7EVR3X{`AvQ9h8dHh)hiiPeD59Se)1HKF^@Q4qy;jx90$`QDT8uoi=-zg zEENkqf~5K`7xHpsM*jC777`MH3#wEzDHK#!lb3?t3JNK~&{NO{L$wI4`ZNMH?TR&M zZK4hmV~cXXyJh#Urs(8@n4AW^&7?>bQ?nOdk`?W^r#`CAl_%EHRq1VoBb$3{L7R(a zk%WuzxsyI1>Xla-=&?a{GlqK`D0I8yM-9>8zF(Ayq070_*G}yi7I_$O@q76>mw3)M v2Jw*(lv|skFMUc&Z>Rg^{(9PTmjnI@(Udjc diff --git a/Assistant.js b/Assistant.js index 90c2f40e7..188bf1394 100644 --- a/Assistant.js +++ b/Assistant.js @@ -1,4 +1,4 @@ -exports.newAssistant = function newAssistant(BOT, DEBUG_MODULE) { +exports.newAssistant = function newAssistant(BOT, DEBUG_MODULE, UTILITIES) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; @@ -30,6 +30,8 @@ logger.fileName = MODULE_NAME; logger.bot = bot; + let utilities = UTILITIES.newUtilities(bot); + let exchangePositions = []; // These are the open positions at the exchange at the account the bot is authorized to use. let openPositions = []; // These are the open positions the bot knows it made by itself. @@ -72,8 +74,9 @@ let dataSet = thisObject.dataDependencies.dataSets.get(key); let timePeriod = "01-min"; + let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2); let fileName = '' + global.MARKET.assetA + '_' + global.MARKET.assetB + '.json'; - let filePath = global.PLATFORM_CONFIG.marketRateProvider.product + "/" + global.PLATFORM_CONFIG.marketRateProvider.dataSet + "/" + timePeriod; + let filePath = global.PLATFORM_CONFIG.marketRateProvider.product + "/" + global.PLATFORM_CONFIG.marketRateProvider.dataSet + "/" + timePeriod + "/" + dateForPath; dataSet.getTextFile(filePath, fileName, onFileReceived, true); @@ -88,46 +91,73 @@ try { candleArray = JSON.parse(text); - let lastCandleRecord = candleArray[candleArray.length - 1]; // The last candle contains at its close value the market rate. + if (bot.backTestingMode === true) { - let candle = { - open: lastCandleRecord[2], - close: lastCandleRecord[3], - min: lastCandleRecord[0], - max: lastCandleRecord[1], - begin: lastCandleRecord[4], - end: lastCandleRecord[5] - }; + logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Backtest Mode detected."); - marketRate = candle.close; + /* We need to find the candle at which the process is currently running. */ - /* - Now we verify that this candle is not too old. Lets say no more than 2 minutes old. This could happen if the datasets for - any reason stops being updated. - */ + for (let i = 0; i < candleArray.length; i++) { - if (candle.begin < bot.processDatetime.valueOf() - 2 * 60 * 1000) { + let candle = { + open: candleArray[i][2], + close: candleArray[i][3], + min: candleArray[i][0], + max: candleArray[i][1], + begin: candleArray[i][4], + end: candleArray[i][5] + }; - logger.write("[ERROR] initialize -> Candles more than two minutes old. Retrying later."); - callBack(global.DEFAULT_RETRY_RESPONSE); - return; - } + if (bot.processDatetime.valueOf() >= candle.begin && bot.processDatetime.valueOf() < candle.end) { - /* If we are in Exchange Simulation Mode then there is not much to do here. */ + marketRate = (candle.open + candle.close) / 2; - if (bot.backTesting === true) { + /* The Backtest Mode simulates that every trade posted is executed. + In order to do this, we will take all open orders from the context and create a trades array similar to the one returned by the Exchange. */ - logger.write("[WARN] initialize -> Backtesting Mode detected. -> Skipping validateExchangeSyncronicity."); - callBackFunction(global.DEFAULT_OK_RESPONSE); + validateExchangeSyncronicity(); + return; + } + } + + logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> No candle found for the current Process Datetime."); + logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> bot.processDatetime = " + bot.processDatetime); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); return; - } else { + } else { // We are running LIVE Mode. + + logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Live Mode detected."); + + let lastCandleRecord = candleArray[candleArray.length - 1]; // The last candle contains at its close value the market rate. + + let candle = { + open: lastCandleRecord[2], + close: lastCandleRecord[3], + min: lastCandleRecord[0], + max: lastCandleRecord[1], + begin: lastCandleRecord[4], + end: lastCandleRecord[5] + }; + + marketRate = candle.close; + + /* + Now we verify that this candle is not too old. Lets say no more than 2 minutes old. This could happen if the datasets for + any reason stops being updated. + */ + + if (candle.begin < bot.processDatetime.valueOf() - 2 * 60 * 1000) { + + logger.write("[ERROR] initialize -> Candles more than two minutes old. Retrying later."); + callBack(global.DEFAULT_RETRY_RESPONSE); + return; + } validateExchangeSyncronicity(); return; } - } catch (err) { logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> err = " + err.message); logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); @@ -211,7 +241,19 @@ */ - exchangeAPI.getOpenPositions(global.MARKET, onResponse); + if (bot.backTestingMode === true) { + + if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Backtest Mode Detected."); } + + let exchangePositions = []; // We simulate all positions were executed. + + onResponse(global.DEFAULT_OK_RESPONSE, exchangePositions); + } else { + + if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Live Mode Detected."); } + exchangeAPI.getOpenPositions(global.MARKET, onResponse); + + } function onResponse(err, pExchangePositions) { @@ -390,75 +432,6 @@ } } } - - function confirmOrderWasExecuted(pTrades) { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Entering function."); } - if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> pTrades = " + JSON.stringify(pTrades)); } - - /* - - To confirm everything is ok, we will add all the amounts on trades asociated to the order and - they must be equal to the one on file. Otherwise something very strange could have happened, - in which case we will halt the bot execution. - - */ - - let sumAssetA = 0; - let sumAssetB = 0; - - for (let k = 0; k < pTrades.length; k++) { - - sumAssetA = sumAssetA + pTrades[k].amountA; - sumAssetB = sumAssetB + pTrades[k].amountB; - - } - - /* We add the fees */ - - sumAssetA = sumAssetA + exchangePositions[j].fee; - - if ( - position.amountA !== sumAssetA || - position.amountB !== sumAssetB - ) { - logger.write("[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Cannot be confirmed that the order was executed. It must be manually cancelled by the user or cancelled by the exchange itself."); - logger.write("[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Verify also that you were not running under a Exchange Simulation and you turned it off without deleting the Status Report file."); - logger.write("[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> If the process was abruptally cancelled and then restarted, it is possible that now is not sincronized with the exchange."); - logger.write("[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> In any case, to continue, you must delete the Status Report file so as to start over. Also, you must manually delete the orders at the exchange."); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - - /* - - Confirmed that order was executed. Next thing to do is to remember the trades and change its status. - - */ - - position.status = "executed"; - - applyTradesToContext(pTrades); - - let newTransaction = { - type: position.type + " executed", - position: position - }; - - context.executionContext.transactions.push(newTransaction); - - /* All done. */ - - next(); - - } catch (err) { - logger.write("[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - } } /* Position not found: we need to know if the order was executed. */ @@ -478,8 +451,49 @@ */ - exchangeAPI.getExecutedTrades(pPositionId, onResponse); + if (bot.backTestingMode === true) { + if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Backtest Mode Detected."); } + + let trades = []; + + /* We look for the position at the executionContext */ + + for (let i = 0; i < context.executionContext.positions.length; i++) { + + let thisPosition = context.executionContext.positions[i]; + + if (thisPosition.id === pPositionId) { + + let trade = { + id: Math.trunc(Math.random(1) * 1000000), + type: thisPosition.type, + rate: thisPosition.rate, + amountA: thisPosition.amountA, + amountB: thisPosition.amountB, + fee: 0, + date: (new Date()).valueOf() + } + + trades.push(trade); + + onResponse(global.DEFAULT_OK_RESPONSE, trades); + + return; + } + } + + logger.write("[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Position not found at Executioin Context."); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + + } else { + + if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Live Mode Detected."); } + exchangeAPI.getExecutedTrades(pPositionId, onResponse); + + } + function onResponse(err, pTrades) { try { @@ -513,7 +527,150 @@ } } + function confirmOrderWasExecuted(pTrades) { + try { + + if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Entering function."); } + if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> pTrades = " + JSON.stringify(pTrades)); } + + /* + + To confirm everything is ok, we will add all the amounts on trades asociated to the order and + they must be equal to the one on file. Otherwise something very strange could have happened, + in which case we will halt the bot execution. + + */ + + let sumAssetA = 0; + let sumAssetB = 0; + + for (let k = 0; k < pTrades.length; k++) { + + sumAssetA = sumAssetA + pTrades[k].amountA; + sumAssetB = sumAssetB + pTrades[k].amountB; + + } + + /* We add the fees */ + + //sumAssetA = sumAssetA + exchangePositions[j].fee; + + if ( + position.amountA !== sumAssetA || + position.amountB !== sumAssetB + ) { + logger.write("[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Cannot be confirmed that the order was executed. It must be manually cancelled by the user or cancelled by the exchange itself."); + logger.write("[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Verify also that you were not running under a Exchange Simulation and you turned it off without deleting the Status Report file."); + logger.write("[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> If the process was abruptally cancelled and then restarted, it is possible that now is not sincronized with the exchange."); + logger.write("[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> In any case, to continue, you must delete the Status Report file so as to start over. Also, you must manually delete the orders at the exchange."); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + + /* + + Confirmed that order was executed. Next thing to do is to remember the trades and change its status. + + */ + + position.status = "executed"; + + applyTradesToContext(pTrades); + + let newTransaction = { + type: position.type + " executed", + position: position + }; + + context.executionContext.transactions.push(newTransaction); + + /* All done. */ + + next(); + + } catch (err) { + logger.write("[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function applyTradesToContext(pTrades) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> applyTradesToContext -> Entering function."); } + + /* Here we apply the trades that already happened at the exchange to the balance and available balance of the bot. We also calculate its profits. */ + + for (k = 0; k < pTrades.length; k++) { + + let trade = pTrades[k]; + + position.trades.push(trade); + + context.newHistoryRecord.newTrades++; + + /* Calculate Balances */ + + if (trade.type === 'buy') { + + context.executionContext.balance.assetA = context.executionContext.balance.assetA - trade.amountA; + context.executionContext.balance.assetB = context.executionContext.balance.assetB + trade.amountB; + + context.executionContext.availableBalance.assetB = context.executionContext.availableBalance.assetB + trade.amountB; + } + + if (trade.type === 'sell') { + + context.executionContext.balance.assetA = context.executionContext.balance.assetA + trade.amountA; + context.executionContext.balance.assetB = context.executionContext.balance.assetB - trade.amountB; + + context.executionContext.availableBalance.assetA = context.executionContext.availableBalance.assetA + trade.amountA; + } + } + + /* Calculate Profits */ + + if (context.executionContext.investment.assetA > 0) { + + context.executionContext.profits.assetA = (context.executionContext.balance.assetA - context.executionContext.investment.assetA) / context.executionContext.investment.assetA; + } + + if (context.executionContext.investment.assetB > 0) { + + context.executionContext.profits.assetB = (context.executionContext.balance.assetB - context.executionContext.investment.assetB) / context.executionContext.investment.assetB; + } + + context.newHistoryRecord.profitsAssetA = context.executionContext.profits.assetA; + context.newHistoryRecord.profitsAssetB = context.executionContext.profits.assetB; + + /* Calculate Combined Profits */ + + if (context.executionContext.investment.assetA > 0) { + + let convertedAssetsB = (context.executionContext.balance.assetB - context.executionContext.investment.assetB) / marketRate; + + context.executionContext.combinedProfits.assetA = (context.executionContext.balance.assetA + convertedAssetsB - context.executionContext.investment.assetA) / context.executionContext.investment.assetA; + } + + if (context.executionContext.investment.assetB > 0) { + + let convertedAssetsA = (context.executionContext.balance.assetA - context.executionContext.investment.assetA) * marketRate; + + context.executionContext.combinedProfits.assetB = (context.executionContext.balance.assetB + convertedAssetsA - context.executionContext.investment.assetB) / context.executionContext.investment.assetB; + } + + context.newHistoryRecord.combinedProfitsA = context.executionContext.combinedProfits.assetA; + context.newHistoryRecord.combinedProfitsB = context.executionContext.combinedProfits.assetB; + + } catch (err) { + logger.write("[ERROR] ordersExecutionCheck -> loopBody -> applyTradesToContext -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } } catch (err) { logger.write("[ERROR] ordersExecutionCheck -> loopBody -> err = " + err.message); @@ -548,70 +705,6 @@ } - function applyTradesToContext(pTrades) { - - /* Here we apply the trades that already happened at the exchange to the balance and available balance of the bot. We also calculate its profits. */ - - for (k = 0; k < pTrades.length; k++) { - - position.pTrades.push(pTrades[k]); - - context.newHistoryRecord.newTrades++; - - /* Calculate Balances */ - - if (trade.type === 'buy') { - - context.executionContext.balance.assetA = context.executionContext.balance.assetA - trade.amountA; - context.executionContext.balance.assetB = context.executionContext.balance.assetB + trade.amountB; - - context.executionContext.availableBalance.assetB = context.executionContext.availableBalance.assetB + trade.amountB; - } - - if (trade.type === 'sell') { - - context.executionContext.balance.assetA = context.executionContext.balance.assetA + trade.amountA; - context.executionContext.balance.assetB = context.executionContext.balance.assetB - trade.amountB; - - context.executionContext.availableBalance.assetA = context.executionContext.availableBalance.assetA + trade.amountA; - } - } - - /* Calculate Profits */ - - if (context.executionContext.investment.assetA > 0) { - - context.executionContext.profits.assetA = (context.executionContext.balance.assetA - context.executionContext.investment.assetA) / context.executionContext.investment.assetA; - } - - if (context.executionContext.investment.assetB > 0) { - - context.executionContext.profits.assetB = (context.executionContext.balance.assetB - context.executionContext.investment.assetB) / context.executionContext.investment.assetB; - } - - context.newHistoryRecord.profitsAssetA = context.executionContext.profits.assetA; - context.newHistoryRecord.profitsAssetB = context.executionContext.profits.assetB; - - /* Calculate Combined Profits */ - - if (context.executionContext.investment.assetA > 0) { - - let convertedAssetsB = (context.executionContext.balance.assetB - context.executionContext.investment.assetB) / marketRate; - - context.executionContext.combinedProfits.assetA = (context.executionContext.balance.assetA + convertedAssetsB - context.executionContext.investment.assetA) / context.executionContext.investment.assetA; - } - - if (context.executionContext.investment.assetB > 0) { - - let convertedAssetsA = (context.executionContext.balance.assetA - context.executionContext.investment.assetA) * marketRate; - - context.executionContext.combinedProfits.assetB = (context.executionContext.balance.assetB + convertedAssetsA - context.executionContext.investment.assetB) / context.executionContext.investment.assetB; - } - - context.newHistoryRecord.combinedProfitsA = context.executionContext.combinedProfits.assetA; - context.newHistoryRecord.combinedProfitsB = context.executionContext.combinedProfits.assetB; - } - } catch (err) { logger.write("[ERROR] ordersExecutionCheck -> err = " + err.message); callBack(global.DEFAULT_FAIL_RESPONSE); @@ -683,7 +776,13 @@ /* All validations passed, we proceed. */ - if (bot.backTesting === true) { + if (bot.backTestingMode === true) { + + if (pRate !== marketRate) { + + logger.write("[ERROR] putPosition -> Input Validations -> putPosition Rate can not be different to marketRate while in Backtesting Mode. "); + onResponse(global.DEFAULT_FAIL_RESPONSE, positionId); + } let positionId = Math.trunc(Math.random(1) * 1000000); if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); } @@ -776,7 +875,7 @@ if (FULL_LOG === true) { logger.write("[INFO] movePosition -> pPosition = " + JSON.stringify(pPosition)); } if (FULL_LOG === true) { logger.write("[INFO] movePosition -> pNewRate = " + pNewRate); } - if (bot.backTesting === true) { + if (bot.backTestingMode === true) { let positionId = Math.trunc(Math.random(1) * 1000000); if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); } @@ -896,7 +995,6 @@ context.newHistoryRecord.sellAvgRate = (sumSellWeightedRates / sumSellWeights || 0); } - function getPositions() { return JSON.parse(JSON.stringify(context.executionContext.positions)); } diff --git a/Context.js b/Context.js index 0c90ce503..1fc66f01f 100644 --- a/Context.js +++ b/Context.js @@ -240,7 +240,23 @@ if (FULL_LOG === true) { logger.write("[INFO] initialize -> getExecutionContext -> Entering function."); } - let date = new Date(thisObject.statusReport.lastExecution); + let date; + let runType = thisObject.statusReport.runType; + + switch (runType) { + + case "Backtest": { + date = new Date(thisObject.statusReport.backtestRuns[thisObject.statusReport.runIndex].lastExecution); + thisObject.statusReport.backtestRuns[thisObject.statusReport.runIndex].lastExecution = bot.processDatetime; + break; + } + + case "Live": { + date = new Date(thisObject.statusReport.liveRuns[thisObject.statusReport.runIndex].lastExecution); + thisObject.statusReport.liveRuns[thisObject.statusReport.runIndex].lastExecution = bot.processDatetime; + break; + } + } let fileName = "Execution.Context." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex + ".json"; let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2) + '/' + utilities.pad(date.getUTCHours(), 2) + '/' + utilities.pad(date.getUTCMinutes(), 2); @@ -312,7 +328,7 @@ let runType; - if (bot.backTesting === true) { + if (bot.backTestingMode === true) { runType = "Backtest"; } else { runType = "Live"; @@ -329,26 +345,28 @@ backtestRuns: [] }; - } else { // The Status Report does exist, we just need to move to the next runIndex. + } - thisObject.statusReport.runType = runType; - let runContent = { - lastExecution: bot.processDatetime - }; + let runContent = { + beginDatetime: (new Date(bot.timePeriod.beginDatetime)).valueOf(), + endDatetime: (new Date(bot.timePeriod.endDatetime)).valueOf(), + lastExecution: bot.processDatetime + }; - switch (runType) { + thisObject.statusReport.runType = runType; - case "Backtest": { - thisObject.statusReport.backtestRuns.push(runContent); - thisObject.statusReport.runIndex = thisObject.statusReport.backtestRuns.lenght - 1; - break; - } + switch (runType) { - case "Live": { - thisObject.statusReport.liveRuns.push(runContent); - thisObject.statusReport.runIndex = thisObject.statusReport.liveRuns.lenght - 1; - break; - } + case "Backtest": { + thisObject.statusReport.backtestRuns.push(runContent); + thisObject.statusReport.runIndex = thisObject.statusReport.backtestRuns.length - 1; + break; + } + + case "Live": { + thisObject.statusReport.liveRuns.push(runContent); + thisObject.statusReport.runIndex = thisObject.statusReport.liveRuns.length - 1; + break; } } @@ -579,6 +597,7 @@ if (FULL_LOG === true) { logger.write("[INFO] saveThemAll -> writeStatusReport -> Entering function."); } + statusReportModule.file = thisObject.statusReport; statusReportModule.save(callBack); } diff --git a/Run.js b/Run.js index 735db9fc4..6a6824758 100644 --- a/Run.js +++ b/Run.js @@ -323,7 +323,7 @@ for (let p = 0; p < global.PLATFORM_CONFIG.executionList.length; p++) { if (processConfig.startMode.timePeriod.run === "true") { - botConfig.backTesting = true; + botConfig.backTestingMode = true; botConfig.hasTheBotJustStarted = true; botConfig.timePeriod = processConfig.startMode.timePeriod; diff --git a/Trading Bot Process Main Loop.js b/Trading Bot Process Main Loop.js index 124dfa0de..43e74dd8f 100644 --- a/Trading Bot Process Main Loop.js +++ b/Trading Bot Process Main Loop.js @@ -66,7 +66,7 @@ const BLOB_STORAGE = require(ROOT_DIR + 'Blob Storage'); const DEBUG_MODULE = require(ROOT_DIR + 'Debug Log'); const POLONIEX_CLIENT_MODULE = require(ROOT_DIR + 'Poloniex API Client'); - const EXCHANGE_API = require(ROOT_DIR + 'RealExchangeAPI'); + const EXCHANGE_API = require(ROOT_DIR + 'ExchangeAPI'); const CONTEXT = require(ROOT_DIR + 'Context'); const DATASOURCE = require(ROOT_DIR + 'Datasource'); const ASSISTANT = require(ROOT_DIR + 'Assistant'); @@ -77,7 +77,7 @@ /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ - if (bot.backTesting === true) { + if (bot.backTestingMode === true) { if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Backtesting Mode detected."); } @@ -87,7 +87,11 @@ bot.processDatetime = new Date(bot.timePeriod.beginDatetime); // Set the starting time as the configured beginDatetime. - let endDatetime = new Date(bot.timePeriod.beginDatetime); + } else { + + bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance one minute in time. + + let endDatetime = new Date(bot.timePeriod.endDatetime); if (bot.processDatetime.valueOf() > endDatetime.valueOf()) { @@ -96,10 +100,6 @@ callBackFunction(global.DEFAULT_OK_RESPONSE); return; } - - } else { - - bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance one minute in time. } } else { // We are running Live! @@ -280,7 +280,7 @@ if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> initializeAssistant -> Entering function."); } - assistant = ASSISTANT.newAssistant(bot, DEBUG_MODULE); + assistant = ASSISTANT.newAssistant(bot, DEBUG_MODULE, UTILITIES); assistant.initialize(context, exchangeAPI, dataDependencies, onInizialized); function onInizialized(err) { diff --git a/this.config.json b/this.config.json index ca677d9db..3533409fe 100644 --- a/this.config.json +++ b/this.config.json @@ -7,7 +7,7 @@ }, "executionList": [ { - "enabled": "false", + "enabled": "true", "botPath": "../Bots/AAMasters/AAMariam-Trading-Bot", "process": "Trading-Process" }, @@ -32,7 +32,7 @@ "process": "Poloniex-Hole-Fixing" }, { - "enabled": "true", + "enabled": "false", "botPath": "../Bots/AAMasters/AACharly-Extraction-Bot", "process": "Poloniex-Live-Trades" },