Merge remote-tracking branch 'Gauss/master' into develop

This commit is contained in:
Luis Fernando Molina committed 2020-11-20 20:18:54 +01:00
commit 7af9e8a332
11 files changed
+1770

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+11
View File
@@ -0,0 +1,11 @@
.vs/*
obj/*
bin/*
*.config
*.log
*.cache
node_modules/*
bkp/*
+46
View File
@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team using the online form in the contacts page of the superalgos.org website. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
+45
View File
@@ -0,0 +1,45 @@
# Contributing
We are thrilled you are interested in contributing to this repository. Your help may be essential to the project's success and we want you in!
Superalgos is a collective project, currently at a very early stage. Although this repository is not open source software yet, we do welcome contributions from the community.
Contributions are deemed _voluntary work_. There might be incentives in place for certain kinds of contributions. Contact us for more information in this regard.
In any case, contributors agree to provide an express grant of patent rights for their contributions. Submitting Pull Requests to this repository represents an express agreement with the terms of our Balanced Contributor Intellectual Property Agreement. This agreement is a direct implementation of GitHub's [Balanced Employee Intellectual Property Agreement 1.0.1](https://github.com/github/balanced-employee-ip-agreement/blob/master/Balanced_Employee_IP_Agreement.md).
# Balanced Contributor Intellectual Property Agreement 1.0.1
This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person or team submitting Pull Requests to this repository and Superalgos.org ("Project").
**What is this?** This is the Project's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Project.
**Why is this?** The Project needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Project having the legal rights to the products and services it is providing so that the Project can continue operating and doing business.
The Project also believes that it's important to be clear on what it doesn't own. The Project doesn't want you looking over your shoulder every time you work on something personal or worrying that the Project will someday seize your open source non-lethal mousetrap simulation software. In other words, the Project isn't interested in appropriating your personal projects.
**Read this.** Please read this document and be sure you understand it before you submit any Pull Requests. Due to legal risk and corporate obligations, the Project cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Project's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on.
Cool? Then, by submitting your first Pull Request, and as a condition of your contribution, you agree to the following:
1. **What the Project owns.** The Project owns any IP ("Project IP") that you create, or help create _as its contributor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Project product or service at the time you developed, invented, or created it, (ii) developed for use by the Project, or (iii) developed or promoted with existing Project IP or with the Project's endorsement. You hereby grant and assign, and will grant and assign, to the Project all rights and interests in all Project IP.
"Project IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship.
2. **What the Project doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Project doesn't own it. In other words, the Project doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1.
3. **Contribution of your IP to Project projects.** If you include your own IP – such as IP you created prior to working for the Project – into a Project product or service, it's still yours, of course, but you grant the Project a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Project would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well.
4. **Your contributions to non-Project projects.** The Project recognizes that you may be engaged in work that requires you to submit IP to entities other than the Project, such as open source projects used by the Project. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Project and so should be run through Legal first.
5. **Voluntary Works** All Project IP that you are involved with or create as part of your work for the Project is voluntary work which does not require compensation. There might be incentives in place for certain kinds of contributions. Feel free to contact the Project to learn more.
6. **No conflicts.** You agree that you don't have any outstanding agreements or obligations that conflict with those in this Agreement, and that you won't enter into conflicting agreements in the future.
7. **IP protection.** The Project might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Project IP or IP under License (together, "All IP"), and, if the Project asks, to provide those records to the Project. You agree to help the Project secure and defend its rights in All IP. For your help the Project will compensate you at a reasonable rate. If the Project is unable to secure your help, you authorize the Project to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP.
8. **Confidentiality.** As an contributor or contractor of the Project, you will have access to sensitive confidential information that is important to the Project's business. You are responsible for keeping this information confidential, including after you end your work for the Project.
"Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Project because it is not publicly known.
The Project's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Project's Confidential Information for the purpose of performing your contribution and for the benefit of the Project. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

+39
View File
@@ -0,0 +1,39 @@
COPYRIGHT NOTICE
© Copyright 2018 Superalgos.org
LICENSE NOTICE
Superalgos is a collaborative project currently at a very early stage. Because certain aspects of
the project are still not clearly defined, we have chosen to --temporarily-- grant a restrictive
license for the use of this repository, until those aspects are clarified and a proper open source
license can be granted.
Superalgos.org is publishing the code of this software for several reasons. However, it shall not be considered free software yet. Superalgos is a collaborative project in the crypto space and this
software is still proprietary software.
The reasons for opening up the code of this software at this stage are the following:
* We believe that transparency is paramount to achieving our goals, so we want everyone to
be able to understand what this software does and how. Opening up the code of this software
allows anyone to take a close look.
* We welcome contributions from the community. Contributions are regarded as voluntary work.
There may be incentives in place for certain kinds of contributions. Contact us to learn more.
Contributors agree to provide an express grant of patent rights for their contributions.
LICENSE WORDING
Superalgos.org grants a limited license for the following uses:
1. CODE REVIEW AND AUDITS. You are granted permission to clone this repository to analyze,
review or audit the code.
2. COLLABORATING WITH Superalgos.org. You are granted permission to fork this repository
with the sole purpose of correcting bugs and developing functionality in agreement with
Superalgos.org. By contributing to this repository you explicitely agree to the terms
detailed in CONTRIBUTING.MD.
PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY.
+758
View File
@@ -0,0 +1,758 @@
var LRCIndicator = function (settings) {
this.input = 'price'
this.depth = settings
this.result = false
this.age = 0
this.history = []
this.x = []
/*
* Do not use array(depth) as it might not be implemented
*/
for (var i = 0; i < this.depth; i++) {
this.history.push(0.0)
this.x.push(i)
}
// log.debug("Created LRC indicator with h: ", this.depth);
}
LRCIndicator.prototype.update = function (price) {
// We need sufficient history to get the right result.
if (this.result === false && this.age < this.depth) {
this.history[this.age] = price
this.age++
this.result = false
// log.debug("Waiting for sufficient age: ", this.age, " out of ", this.depth);
return
}
this.age++
// shift history
for (var i = 0; i < (this.depth - 1); i++) {
this.history[i] = this.history[i + 1]
}
this.history[this.depth - 1] = price
this.calculate(price)
// log.debug("Checking LRC: ", this.result.toFixed(8), "\tH: ", this.age);
return
}
/*
* Least squares linear regression fitting.
*/
function linreg (values_x, values_y) {
var sum_x = 0
var sum_y = 0
var sum_xy = 0
var sum_xx = 0
var count = 0
/*
* We'll use those variables for faster read/write access.
*/
var x = 0
var y = 0
var values_length = values_x.length
if (values_length != values_y.length) {
throw new Error('The parameters values_x and values_y need to have same size!')
}
/*
* Nothing to do.
*/
if (values_length === 0) {
return [[], []]
}
/*
* Calculate the sum for each of the parts necessary.
*/
for (var v = 0; v < values_length; v++) {
x = values_x[v]
y = values_y[v]
sum_x += x
sum_y += y
sum_xx += x * x
sum_xy += x * y
count++
}
/*
* Calculate m and b for the formula: y = x * m + b
*
*/
var m = (count * sum_xy - sum_x * sum_y) / (count * sum_xx - sum_x * sum_x)
var b = (sum_y / count) - (m * sum_x) / count
return [m, b]
}
/*
* Handle calculations
*/
LRCIndicator.prototype.calculate = function (price) {
// get the reg
var reg = linreg(this.x, this.history)
// y = a * x + b
this.result = ((this.depth - 1) * reg[0]) + reg[1]
}
exports.newUserBot = function newUserBot (bot, logger, COMMONS, UTILITIES, fileStorage) {
const FULL_LOG = false
const LOG_FILE_CONTENT = false
const USE_PARTIAL_LAST_CANDLE = true // When running live the last candle generated is a partial candle.
const ONE_MIN_IN_MILISECONDS = 60 * 1000
const MODULE_NAME = 'User Bot'
const EXCHANGE_NAME = 'Poloniex'
thisObject = {
initialize: initialize,
start: start
}
let utilities = UTILITIES.newCloudUtilities(bot, logger)
let statusDependencies
return thisObject
function initialize (pStatusDependencies, pMonth, pYear, callBackFunction) {
try {
logger.fileName = MODULE_NAME
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] initialize -> Entering function.') }
statusDependencies = pStatusDependencies
callBackFunction(global.DEFAULT_OK_RESPONSE)
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] initialize -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
/*
This process will do the following:
For every period on the market:
a. Get olivia files for the current execution time
i. If the file exist, validate we can get 63 candles
1) 63 candles exist, proceed
2) 63 candles doesn't exist, call for previous execution day until have 63
ii. File doesn't exist, wait until get the files
b. Once we have the 63 candles, calculate LRC
c. Save LRC points
*/
function start (callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> Entering function.') }
let market = global.MARKET
let reportFilePath = EXCHANGE_NAME + '/Processes/' + bot.process
let executionTime
let lastCandles
let dateForLastOliviaFile
let dailyFileCache = new Map()
let lrcPointsFileCache = new Map()
getContextVariables()
function getContextVariables () {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> Entering function.') }
let reportKey = 'AAMasters' + '-' + 'AAOlivia' + '-' + 'Multi-Period-Daily' + '-' + 'dataSet.V1'
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> reportKey = ' + reportKey) }
if (statusDependencies.statusReports.get(reportKey).status === 'Status Report is corrupt.') {
logger.write(MODULE_NAME, '[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.')
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
let thisReport = statusDependencies.statusReports.get(reportKey).file
if (thisReport === undefined || thisReport.lastExecution === undefined) {
logger.write(MODULE_NAME, '[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = ' + reportKey)
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: 'Dependency not ready.'
}
logger.write(MODULE_NAME, '[WARN] start -> getContextVariables -> customOK = ' + customOK.message)
callBackFunction(customOK)
return
} else {
dateForLastOliviaFile = new Date(thisReport.lastExecution)
}
reportKey = 'AAVikings' + '-' + 'AAGauss' + '-' + 'Multi-Period-Daily' + '-' + 'dataSet.V1'
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> reportKey = ' + reportKey) }
if (statusDependencies.statusReports.get(reportKey).status === 'Status Report is corrupt.') {
logger.write(MODULE_NAME, '[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.')
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
thisReport = statusDependencies.statusReports.get(reportKey).file
if (thisReport.lastCandles !== undefined) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> thisReport.lastCandles: ' + JSON.stringify(thisReport.lastCandles)) };
lastCandles = thisReport.lastCandles
buildLRCPoints()
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> First time running the bot.') }
lastCandles = []
for (n = 0; n < global.dailyFilePeriods.length; n++) {
lastCandles.push(0)
}
lastCandles[10] = new Date(Date.UTC(2015, 6, 1)).valueOf()
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> Starting at: ' + new Date(lastCandles[10]).toISOString()) }
buildLRCPoints()
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> getContextVariables -> err = ' + err.message)
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, '[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ')
logger.write(MODULE_NAME, '[HINT] start -> getContextVariables -> Dependencies loaded -> keys = ' + JSON.stringify(statusDependencies.keys))
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function buildLRCPoints () {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> Entering function.') }
let writeReport = false
advanceTime()
function advanceTime () {
try {
logger.newInternalLoop(bot.codeName, bot.process)
executionTime = new Date(lastCandles[10] + ONE_MIN_IN_MILISECONDS)
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> advanceTime -> New processing time @ ' + executionTime.toISOString()) }
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> advanceTime -> Last Candles: ' + JSON.stringify(lastCandles)) };
if (isOnHeadOfMarket(executionTime, 10)) {
callBackFunction(global.DEFAULT_OK_RESPONSE)
} else {
periodsLoop()
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> advanceTime -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function isOnHeadOfMarket (timeToValidate, period) {
let candleBeginIndex = 4
let lastCandlePeriodOnCache
let lastDayIndex = period + '/' + timeToValidate.getUTCFullYear() + '/' + utilities.pad(timeToValidate.getUTCMonth() + 1, 2) + '/' + utilities.pad(timeToValidate.getUTCDate(), 2)
if (dailyFileCache.has(lastDayIndex)) {
let currentFile = dailyFileCache.get(lastDayIndex)
if (currentFile === undefined || currentFile[currentFile.length - 1] === undefined) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] isOnHeadOfMarket -> Period ' + global.dailyFilePeriods[period][1] + ' on cache. Checking olivia last execution: ' + new Date(dateForLastOliviaFile).toISOString()) }
let isOnHead = (timeToValidate.valueOf() > dateForLastOliviaFile.valueOf())
if (isOnHead && FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] isOnHeadOfMarket -> Head of the market found on Olivia ' + global.dailyFilePeriods[period][1] + ' @ ' + new Date(dateForLastOliviaFile).toISOString() + '. Execution: ' + new Date(timeToValidate).toISOString()) }
return isOnHead
}
lastCandlePeriodOnCache = currentFile[currentFile.length - 1][candleBeginIndex]
let isOnHead = (timeToValidate.valueOf() > lastCandlePeriodOnCache.valueOf())
if (isOnHead && FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] isOnHeadOfMarket -> Head of the market found on cache ' + global.dailyFilePeriods[period][1] + ' @ ' + new Date(lastCandlePeriodOnCache).toISOString() + '. Execution: ' + new Date(timeToValidate).toISOString()) }
return isOnHead
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] isOnHeadOfMarket -> Period ' + global.dailyFilePeriods[period][1] + ' not found on cache. Checking olivia last execution: ' + new Date(dateForLastOliviaFile).toISOString()) }
let isOnHead = (timeToValidate.valueOf() > dateForLastOliviaFile.valueOf())
if (isOnHead && FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] isOnHeadOfMarket -> Head of the market found on Olivia ' + global.dailyFilePeriods[period][1] + ' @ ' + new Date(dateForLastOliviaFile).toISOString() + '. Execution: ' + new Date(timeToValidate).toISOString()) }
return isOnHead
}
}
function periodsLoop () {
let n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody()
function loopBody () {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> periodsLoop -> loopBody -> Entering function. Loop: ' + n) }
const outputPeriod = global.dailyFilePeriods[n][0]
const folderName = global.dailyFilePeriods[n][1]
const nextExecution = lastCandles[n] + outputPeriod
isTimeToRun()
function isTimeToRun () {
if (executionTime.valueOf() >= nextExecution) {
getLRCPoints()
} else {
setTimeout(function () { controlLoop() }, 0)
}
}
function getLRCPoints () {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> Entering function.') }
const maxLRCDepth = 63
const maxBackwardsCount = 3
let backwardsCount = 0
let candleArray = []
let queryDate = new Date(executionTime)
let candleFile = getDailyFile(queryDate, onDailyFileReceived)
function onDailyFileReceived (err, candleFile) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> onDailyFileReceived.') }
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
for (let i = 0; i < candleFile.length; i++) {
let candle = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined
}
candle.min = candleFile[i][0]
candle.max = candleFile[i][1]
candle.open = candleFile[i][2]
candle.close = candleFile[i][3]
candle.begin = candleFile[i][4]
candle.end = candleFile[i][5]
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, '[INFO] Candle Date: ' + new Date(candle.begin).toISOString() + '. Process Date: ' + executionTime.toISOString()) }
let lastCandle = executionTime.valueOf() - (outputPeriod * maxLRCDepth)
if (candleArray.length < maxLRCDepth && candle.begin > lastCandle && candle.begin <= executionTime.valueOf()) {
candleArray.push(candle)
}
}
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> Candle Array Length: ' + candleArray.length) }
if (candleArray.length >= maxLRCDepth) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> All candles available proceed with LRC calculations.') }
let lrcPoint = performLRCCalculations(candleArray)
savePoint(lrcPoint)
} else if (backwardsCount <= maxBackwardsCount) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> Getting file for day before.') }
queryDate.setDate(queryDate.getDate() - 1)
getDailyFile(queryDate, onDailyFileReceived)
backwardsCount++
} else {
logger.write(MODULE_NAME, '[WARN] start -> getLRCPoints -> Not enough history to calculate LRC.')
// If the control candle is not ready, advance the execution.
if (n === 10) {
lastCandles[10] += ONE_MIN_IN_MILISECONDS
}
setTimeout(function () { controlLoop() }, 0)
}
}
}
function getDailyFile (dateTime, onDailyFileReceived) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> getDailyFile -> Entering function.') }
/**
* Let's first check if we can use our own process cache in order to reduce network requests.
* Otherwise, continue with the one from the servers.
*
*/
let datePath = dateTime.getUTCFullYear() + '/' + utilities.pad(dateTime.getUTCMonth() + 1, 2) + '/' + utilities.pad(dateTime.getUTCDate(), 2)
let cachePosition = n + '/' + datePath
if (dailyFileCache.has(cachePosition)) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> getDailyFile -> Getting the file from local cache.') }
let cache = dailyFileCache.get(cachePosition)
onDailyFileReceived(global.DEFAULT_OK_RESPONSE, cache)
} else {
let filePath = 'AAMasters/AAOlivia.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Candles/Multi-Period-Daily/' + folderName + '/' + datePath
let fileName = market.assetA + '_' + market.assetB + '.json'
fileStorage.getTextFile(bot.devTeam, filePath + '/' + fileName, onFileReceived)
}
function onFileReceived (err, text) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> getDailyFile -> onFileReceived > Entering Function.') }
let candleFile = JSON.parse(text)
dailyFileCache.set(cachePosition, candleFile)
onDailyFileReceived(global.DEFAULT_OK_RESPONSE, candleFile)
} else {
logger.write(MODULE_NAME, '[WARN] start -> getLRCPoints -> getDailyFile -> onFileReceived -> Failed to get the file. Dependency not ready.')
// If the control candle is not ready, advance the execution.
if (n === 10) {
lastCandles[10] += ONE_MIN_IN_MILISECONDS
}
setTimeout(function () { controlLoop() }, 0)
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> getLRCPoints -> getDailyFile -> err = ' + err.message)
callBackFunction(global.DEFAULT_RETRY_RESPONSE)
}
}
function performLRCCalculations (candleArray) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> performLRCCalculations -> Entering function.') }
/*
* It's needed to order since it's possible that we need to get another file and it will put an older candle at the end of the array.
*/
candleArray.sort(function (a, b) {
return a.begin - b.begin
})
if (USE_PARTIAL_LAST_CANDLE === false) candleArray = candleArray.slice(0, candleArray.length - 1)
let lrcPoints = []
lrcPoints.push(0) // firstCandleBeginTime 0
lrcPoints.push(0) // firstCandleEndTime 1
lrcPoints.push(0) // minimumPointValue 2
lrcPoints.push(0) // middlePointValue 3
lrcPoints.push(0) // maximumPointValue 4
if (LOG_FILE_CONTENT === true) { lrcPoints.push(candleArray) } // Entire candle array 5
calculateLRC(candleArray, lrcPoints)
let lastCandle = candleArray[0]
let firstCandle = candleArray[candleArray.length - 1]
lrcPoints[0] = firstCandle.begin
lrcPoints[1] = firstCandle.end
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> performLRCCalculations -> LRC Points calculation results: ' + JSON.stringify(lrcPoints))
}
return lrcPoints
}
function calculateLRC (candlesArray, lrcPoints) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> calculateLRC -> Entering function.') }
let lrcMinIndicator = new LRCIndicator(15)
let lrcMidIndicator = new LRCIndicator(30)
let lrcMaxIndicator = new LRCIndicator(60)
for (let i = 0; i < candlesArray.length; i++) {
let tempCandle = candlesArray[i]
let averagePrice = (tempCandle.min + tempCandle.max + tempCandle.open + tempCandle.close) / 4
lrcMinIndicator.update(averagePrice)
lrcMidIndicator.update(averagePrice)
lrcMaxIndicator.update(averagePrice)
}
/*
* Only if there is enough history the result will be calculated
*/
if (lrcMinIndicator.result != false && lrcMidIndicator.result != false && lrcMaxIndicator.result != false) {
lrcPoints[2] = lrcMinIndicator.result
lrcPoints[3] = lrcMidIndicator.result
lrcPoints[4] = lrcMaxIndicator.result
return lrcPoints
} else {
logger.write(MODULE_NAME, '[ERROR] start -> getLRCPoints -> calculateLRC -> There is not enough history to calculate the LRC.')
}
}
function savePoint (lrcPoint) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> Entering function.') }
/**
* Let's first check if we can use our own process cache in order to reduce network requests.
* Otherwise, continue with the one from the servers.
*
*/
let datePath = executionTime.getUTCFullYear() + '/' + utilities.pad(executionTime.getUTCMonth() + 1, 2) + '/' + utilities.pad(executionTime.getUTCDate(), 2)
let filePath = bot.filePathRoot + '/Output/LRC-Points/Multi-Period-Daily/' + folderName + '/' + datePath
let fileName = market.assetA + '_' + market.assetB + '.json'
let cachePosition = n + '/' + datePath
if (!lrcPointsFileCache.has(cachePosition)) {
lrcPointsFileCache.set(cachePosition, [])
}
lrcPointsFileCache.get(cachePosition).push(lrcPoint)
lastCandles[n] = lrcPoint[0]
let timeToValidate = new Date(executionTime.valueOf() + outputPeriod)
if (isOnHeadOfMarket(timeToValidate, n) || isLastDayExecution()) {
fileStorage.getTextFile(bot.devTeam, filePath + '/' + fileName, onFileRetrieved)
} else {
// Move to the next period without writing report
setTimeout(function () { controlLoop() }, 0)
}
function onFileRetrieved (err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileRetrieved -> Entering function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
if (err.message === 'File does not exist.') {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileRetrieved -> Daily file does not exist, creating folders.') }
createFile()
return
} else {
logger.write(MODULE_NAME, '[ERROR] start -> savePoint -> onFileRetrieved -> err = ' + err.message)
callBackFunction(err)
return
}
}
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileRetrieved -> Daily file exist, appending content.') }
let existingFileContent = JSON.parse(text)
let finalContent = appendLRCPoints(existingFileContent)
appendToFile(finalContent)
}
function createFile () {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> createFile -> Entering function.') }
let fileContent = JSON.stringify(lrcPointsFileCache.get(cachePosition))
fileStorage.createTextFile(bot.devTeam, filePath + '/' + fileName, fileContent, onFileCreated)
function onFileCreated (err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileCreated -> Entering function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] start -> savePoint -> onFileCreated -> err = ' + err.message)
callBackFunction(err)
return
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileCreated -> Content written = ' + JSON.stringify(fileContent))
}
saveCleanAndMoveForward()
}
}
function appendToFile (fileContent) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> appendToFile -> Entering function.') }
fileStorage.createTextFile(bot.devTeam, filePath + '/' + fileName, JSON.stringify(fileContent), onExistingFileUpdated)
function onExistingFileUpdated (err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onExistingFileUpdated -> Entering function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] start -> savePoint -> onExistingFileUpdated -> err = ' + err.message)
callBackFunction(err)
return
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onExistingFileUpdated -> Content written = ' + JSON.stringify(fileContent))
}
saveCleanAndMoveForward()
}
}
function appendLRCPoints (existingLRCPoints) {
if (existingLRCPoints === undefined) {
return lrcPointsFileCache.get(cachePosition)
}
for (i = 0; i < lrcPointsFileCache.get(cachePosition).length; i++) {
let lrcPoint = lrcPointsFileCache.get(cachePosition)[i]
let lastCandleOnFile = existingLRCPoints[existingLRCPoints.length - 1]
if (lastCandleOnFile[0] < lrcPoint[0]) {
existingLRCPoints.push(lrcPoint)
} else {
logger.write(MODULE_NAME, '[WARN] start -> savePoint -> appendLRCPoints -> The LCR Point calculated existed on the file storage period ' + folderName + ', not saving: ' + JSON.stringify(lrcPoint))
}
}
return existingLRCPoints
}
function saveCleanAndMoveForward () {
writeReport = true
cleanUpCache(lrcPointsFileCache, executionTime)
cleanUpCache(dailyFileCache, executionTime)
setTimeout(function () { controlLoop() }, 0)
}
function cleanUpCache (fileCache, dateTime) {
let date = new Date(dateTime)
let datePath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2)
let cachePosition = n + '/' + datePath
// Remove and go back one day
if (fileCache.has(cachePosition)) {
fileCache.delete(cachePosition)
date.setDate(date.getDate() - 1)
cleanUpCache(fileCache, date)
}
}
function isLastDayExecution () {
let date = new Date(executionTime.valueOf())
date.setUTCHours(23)
let lastMinute = 60 - (outputPeriod / 60 / 1000)
date.setUTCMinutes(lastMinute)
date.setUTCSeconds(0)
date.setUTCMilliseconds(0)
if (executionTime >= date.valueOf()) {
return true
}
else {
return false
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> savePoint -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
}
}
function controlLoop () {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> periodsLoop -> controlLoop -> Entering function. Checking Period: ' + global.dailyFilePeriods[n][1]) }
n++
if (n < global.dailyFilePeriods.length) {
loopBody()
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> periodsLoop -> controlLoop -> Last period reached.') }
if (writeReport) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> periodsLoop -> controlLoop -> About to write the report.') }
writeReport = false
let lastOneMinuteCandle = lastCandles[10]
writeDataRange(lastOneMinuteCandle, onWritten)
function onWritten (err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> periodsLoop -> controlLoop -> onWritten -> Entering function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] writeDataRanges -> writeDataRanges -> onWritten -> err = ' + err.message)
callBack(err)
return
}
writeStatusReport(advanceTime)
}
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> periodsLoop -> controlLoop -> Advancing time.') }
advanceTime()
}
}
}
}
}
function writeDataRange (lastOneMinuteCandle, callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> writeDataRange -> Entering function.') }
let dataRange = {
begin: 1424373300000,
end: lastOneMinuteCandle.valueOf()
}
let fileContentDataRange = JSON.stringify(dataRange)
let fileName = 'Data.Range.' + market.assetA + '_' + market.assetB + '.json'
let filePath = bot.filePathRoot + '/Output/LRC-Points/' + bot.process
fileStorage.createTextFile(bot.devTeam, filePath + '/' + fileName, fileContentDataRange + '\n', onFileCreated)
function onFileCreated (err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> writeDataRange -> onFolderCreated -> onFileCreated -> Entering function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] start -> writeDataRange -> onFolderCreated -> onFileCreated -> err = ' + err.message)
callBack(err)
return
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, '[INFO] start -> writeDataRange -> onFolderCreated -> onFileCreated -> Content written = ' + fileContentDataRange)
}
callBack(global.DEFAULT_OK_RESPONSE)
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> writeDataRange -> err = ' + err.message)
callBack(global.DEFAULT_FAIL_RESPONSE)
}
}
function writeStatusReport (callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> writeStatusReport -> Entering function.') }
let key = bot.devTeam + '-' + bot.codeName + '-' + bot.process + '-' + 'dataSet.V1'
let statusReport = statusDependencies.statusReports.get(key)
statusReport.file.lastExecution = bot.processDatetime
statusReport.file.lastCandles = lastCandles
statusReport.save(callBack)
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> writeStatusReport -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
}
+662
View File
@@ -0,0 +1,662 @@
var LRCIndicator = function (settings) {
this.input = 'price'
this.depth = settings
this.result = false
this.age = 0
this.history = []
this.x = []
/*
* Do not use array(depth) as it might not be implemented
*/
for (var i = 0; i < this.depth; i++) {
this.history.push(0.0)
this.x.push(i)
}
// log.debug("Created LRC indicator with h: ", this.depth);
}
LRCIndicator.prototype.update = function (price) {
// We need sufficient history to get the right result.
if (this.result === false && this.age < this.depth) {
this.history[this.age] = price
this.age++
this.result = false
// log.debug("Waiting for sufficient age: ", this.age, " out of ", this.depth);
return
}
this.age++
// shift history
for (var i = 0; i < (this.depth - 1); i++) {
this.history[i] = this.history[i + 1]
}
this.history[this.depth - 1] = price
this.calculate(price)
// log.debug("Checking LRC: ", this.result.toFixed(8), "\tH: ", this.age);
return
}
/*
* Least squares linear regression fitting.
*/
function linreg(values_x, values_y) {
var sum_x = 0
var sum_y = 0
var sum_xy = 0
var sum_xx = 0
var count = 0
/*
* We'll use those variables for faster read/write access.
*/
var x = 0
var y = 0
var values_length = values_x.length
if (values_length != values_y.length) {
throw new Error('The parameters values_x and values_y need to have same size!')
}
/*
* Nothing to do.
*/
if (values_length === 0) {
return [[], []]
}
/*
* Calculate the sum for each of the parts necessary.
*/
for (var v = 0; v < values_length; v++) {
x = values_x[v]
y = values_y[v]
sum_x += x
sum_y += y
sum_xx += x * x
sum_xy += x * y
count++
}
/*
* Calculate m and b for the formula: y = x * m + b
*
*/
var m = (count * sum_xy - sum_x * sum_y) / (count * sum_xx - sum_x * sum_x)
var b = (sum_y / count) - (m * sum_x) / count
return [m, b]
}
/*
* Handle calculations
*/
LRCIndicator.prototype.calculate = function (price) {
// get the reg
var reg = linreg(this.x, this.history)
// y = a * x + b
this.result = ((this.depth - 1) * reg[0]) + reg[1]
}
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) {
const FULL_LOG = false
const LOG_FILE_CONTENT = false
const USE_PARTIAL_LAST_CANDLE = true // When running live the last candle generated is a partial candle.
const ONE_HOUR_IN_MILISECONDS = 60 * 60 * 1000
const MODULE_NAME = 'User Bot'
thisObject = {
initialize: initialize,
start: start
}
let statusDependencies
return thisObject
function initialize(pStatusDependencies, pMonth, pYear, callBackFunction) {
try {
logger.fileName = MODULE_NAME
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] initialize -> Entering function.') }
statusDependencies = pStatusDependencies
callBackFunction(global.DEFAULT_OK_RESPONSE)
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] initialize -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
/*
This process will do the following:
For every period on the market:
a. Get olivia files for the current execution time
i. If the file exist, validate we can get 63 candles
1) 63 candles exist, proceed
2) 63 candles doesn't exist, call for previous execution day until have 63
ii. File doesn't exist, wait until get the files
b. Once we have the 63 candles, calculate LRC
c. Save LRC points
*/
function start(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> Entering function.') }
let market = global.MARKET
let reportFilePath = global.EXCHANGE_NAME + '/Processes/' + bot.process
let executionTime
let lastCandles
let marketFileCache = new Map()
let lrcPointsFileCache = new Map()
getContextVariables()
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> Entering function.') }
let reportKey = 'AAMasters' + '-' + 'AAOlivia' + '-' + 'Multi-Period-Market' + '-' + 'dataSet.V1'
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> reportKey = ' + reportKey) }
if (statusDependencies.statusReports.get(reportKey).status === 'Status Report is corrupt.') {
logger.write(MODULE_NAME, '[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.')
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
let thisReport = statusDependencies.statusReports.get(reportKey).file
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, '[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = ' + reportKey)
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: 'Dependency not ready.'
}
logger.write(MODULE_NAME, '[WARN] start -> getContextVariables -> customOK = ' + customOK.message)
callBackFunction(customOK)
return
}
reportKey = 'AAVikings' + '-' + 'AAGauss' + '-' + 'Multi-Period-Market' + '-' + 'dataSet.V1'
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> reportKey = ' + reportKey) }
if (statusDependencies.statusReports.get(reportKey).status === 'Status Report is corrupt.') {
logger.write(MODULE_NAME, '[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.')
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
thisReport = statusDependencies.statusReports.get(reportKey).file
if (thisReport.lastCandles !== undefined) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> thisReport.lastCandles: ' + JSON.stringify(thisReport.lastCandles)) };
lastCandles = thisReport.lastCandles
buildLRCPoints()
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> First time running the bot.') }
lastCandles = []
for (n = 0; n < global.marketFilesPeriods.length; n++) {
lastCandles.push(0)
}
lastCandles[7] = new Date(Date.UTC(2015, 1, 20)).valueOf()
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getContextVariables -> Starting at: ' + new Date(lastCandles[7]).toISOString()) }
buildLRCPoints()
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> getContextVariables -> err = ' + err.message)
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, '[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ')
logger.write(MODULE_NAME, '[HINT] start -> getContextVariables -> Dependencies loaded -> keys = ' + JSON.stringify(statusDependencies.keys))
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function buildLRCPoints() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> Entering function.') }
let writeReport = false
advanceTime()
function advanceTime() {
try {
logger.newInternalLoop(bot.codeName, bot.process)
executionTime = new Date(lastCandles[7] + ONE_HOUR_IN_MILISECONDS)
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> advanceTime -> New processing time @ ' + executionTime.toISOString()) }
/* Validation that we are not going past the head of the market. */
if (isExecutionOnHeadOfMarket()) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> advanceTime -> Head of the market found @ ' + executionTime.toISOString()) }
callBackFunction(global.DEFAULT_OK_RESPONSE) // Here is where we finish processing and wait for the platform to run this module again.
return
}
periodsLoop()
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> advanceTime -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function isExecutionOnHeadOfMarket() {
let candleBeginIndex = 4
let lastCandlePeriodOnCache
if (!marketFileCache.has(7)) {
return false
}
let currentFile = marketFileCache.get(7)
if (currentFile === undefined || currentFile[currentFile.length - 1] === undefined) {
return false
} else {
lastCandlePeriodOnCache = currentFile[currentFile.length - 1][candleBeginIndex]
}
return (executionTime.valueOf() >= lastCandlePeriodOnCache.valueOf())
}
function periodsLoop() {
let n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody()
function loopBody() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> periodsLoop -> loopBody -> Entering function. Time: ' + executionTime.toISOString() + '. Loop: ' + n) }
const outputPeriod = global.marketFilesPeriods[n][0]
const folderName = global.marketFilesPeriods[n][1]
const nextExecution = lastCandles[n] + outputPeriod
isTimeToRun()
function isTimeToRun() {
if (executionTime.valueOf() >= nextExecution && !isPeriodOnHeadOfMarket(executionTime)) {
getLRCPoints()
} else {
setTimeout(function () { controlLoop() }, 0)
}
}
function getLRCPoints() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> Entering function.') }
const maxLRCDepth = 63
const maxBackwardsCount = 3
let backwardsCount = 0
let candleArray = []
let candleFile = getMarketFile(onMarketFileReceived)
function onMarketFileReceived(err, candleFile) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> onMarketFileReceived.') }
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
for (let i = 0; i < candleFile.length; i++) {
let candle = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined
}
candle.min = candleFile[i][0]
candle.max = candleFile[i][1]
candle.open = candleFile[i][2]
candle.close = candleFile[i][3]
candle.begin = candleFile[i][4]
candle.end = candleFile[i][5]
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, '[INFO] Candle Date: ' + new Date(candle.begin).toISOString() + '. Process Date: ' + executionTime.toISOString()) }
let lastCandle = executionTime.valueOf() - (outputPeriod * maxLRCDepth)
if (candleArray.length < maxLRCDepth && candle.begin > lastCandle && candle.begin <= executionTime.valueOf()) {
candleArray.push(candle)
}
}
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> Candle Array Length: ' + candleArray.length) }
if (candleArray.length >= maxLRCDepth) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> All candles available proceed with LRC calculations.') }
let lrcPoint = performLRCCalculations(candleArray)
savePoint(lrcPoint)
} else {
logger.write(MODULE_NAME, '[WARN] start -> getLRCPoints -> Not enough history to calculate LRC. Moving to Next Period.')
// If the control candle is not ready, advance the execution.
if (n === 7) {
lastCandles[7] += ONE_HOUR_IN_MILISECONDS
}
setTimeout(function () { controlLoop() }, 0)
}
}
}
function getMarketFile(onMarketFileReceived) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> getMarketFile -> Entering function.') }
/**
* Let's first check if we can use our own process cache in order to reduce network requests.
* Otherwise, continue with the one from the servers.
*
*/
if (marketFileCache.has(n)) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> getMarketFile -> Getting the file from local cache.') }
onMarketFileReceived(global.DEFAULT_OK_RESPONSE, marketFileCache.get(n))
} else {
let filePath = 'AAMasters/AAOlivia.1.0/AACloud.1.1/' + global.EXCHANGE_NAME + '/dataSet.V1/Output/Candles/Multi-Period-Market/' + folderName
let fileName = market.assetA + '_' + market.assetB + '.json'
fileStorage.getTextFile(bot.devTeam, filePath + '/' + fileName, onFileReceived)
}
function onFileReceived(err, text) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> getMarketFile -> onFileReceived > Entering Function.') }
let candleFile = JSON.parse(text)
marketFileCache.set(n, candleFile) // We keep a copy of the results for local use
onMarketFileReceived(global.DEFAULT_OK_RESPONSE, candleFile)
} else {
logger.write(MODULE_NAME, '[WARN] start -> getLRCPoints -> getMarketFile -> onFileReceived -> Failed to get the file. Dependency not ready.')
// if the dependency is not ready advance the control candle one period.
if (n === 7) {
lastCandles[7] += ONE_HOUR_IN_MILISECONDS
}
setTimeout(function () { controlLoop() }, 0)
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> getLRCPoints -> getMarketFile -> err = ' + err.message)
callBackFunction(global.DEFAULT_RETRY_RESPONSE)
}
}
function performLRCCalculations(candleArray) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> performLRCCalculations -> Entering function.') }
/*
* It's needed to order since it's possible that we need to get another file and it will put an older candle at the end of the array.
*/
candleArray.sort(function (a, b) {
return a.begin - b.begin
})
if (USE_PARTIAL_LAST_CANDLE === false) candleArray = candleArray.slice(0, candleArray.length - 1)
let lrcPoints = []
lrcPoints.push(0) // firstCandleBeginTime 0
lrcPoints.push(0) // firstCandleEndTime 1
lrcPoints.push(0) // minimumPointValue 2
lrcPoints.push(0) // middlePointValue 3
lrcPoints.push(0) // maximumPointValue 4
if (LOG_FILE_CONTENT === true) { lrcPoints.push(candleArray) } // Entire candle array 5
calculateLRC(candleArray, lrcPoints)
let lastCandle = candleArray[0]
let firstCandle = candleArray[candleArray.length - 1]
lrcPoints[0] = firstCandle.begin
lrcPoints[1] = firstCandle.end
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> performLRCCalculations -> LRC Points calculation results: ' + JSON.stringify(lrcPoints))
}
return lrcPoints
}
function calculateLRC(candlesArray, lrcPoints) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> getLRCPoints -> calculateLRC -> Entering function.') }
let lrcMinIndicator = new LRCIndicator(15)
let lrcMidIndicator = new LRCIndicator(30)
let lrcMaxIndicator = new LRCIndicator(60)
for (let i = 0; i < candlesArray.length; i++) {
let tempCandle = candlesArray[i]
let averagePrice = (tempCandle.min + tempCandle.max + tempCandle.open + tempCandle.close) / 4
lrcMinIndicator.update(averagePrice)
lrcMidIndicator.update(averagePrice)
lrcMaxIndicator.update(averagePrice)
}
/*
* Only if there is enough history the result will be calculated
*/
if (lrcMinIndicator.result != false && lrcMidIndicator.result != false && lrcMaxIndicator.result != false) {
lrcPoints[2] = lrcMinIndicator.result
lrcPoints[3] = lrcMidIndicator.result
lrcPoints[4] = lrcMaxIndicator.result
return lrcPoints
} else {
logger.write(MODULE_NAME, '[ERROR] start -> getLRCPoints -> calculateLRC -> There is not enough history to calculate the LRC.')
}
}
function savePoint(lrcPoint) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> Entering function.') }
let filePath = bot.filePathRoot + '/Output/LRC-Points/Multi-Period-Market/' + folderName
let fileName = market.assetA + '_' + market.assetB + '.json'
if (!lrcPointsFileCache.has(n)) {
lrcPointsFileCache.set(n, [])
}
lrcPointsFileCache.get(n).push(lrcPoint)
lastCandles[n] = lrcPoint[0]
let timeToValidate = new Date(executionTime.valueOf() + outputPeriod)
if (isPeriodOnHeadOfMarket(timeToValidate)) {
fileStorage.getTextFile(bot.devTeam, filePath + '/' + fileName, onFileRetrieved)
} else {
// Move to the next period without writing report
setTimeout(function () { controlLoop() }, 0)
}
function onFileRetrieved(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint ->onFileRetrieved -> Entering function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
if (err.message === 'File does not exist.') {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileRetrieved -> Market file does not exist, creating folders.') }
createFile()
return
} else {
logger.write(MODULE_NAME, '[ERROR] start -> savePoint -> onFileRetrieved -> err = ' + err.message)
callBackFunction(err)
return
}
}
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileRetrieved -> Market file exist, appending content.') }
let existingFileContent = JSON.parse(text)
let finalContent = appendLRCPoints(existingFileContent)
appendToFile(finalContent)
}
function createFile() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> createFile -> Entering function.') }
let fileContent = JSON.stringify(lrcPointsFileCache.get(n))
fileStorage.createTextFile(bot.devTeam, filePath + '/' + fileName, fileContent, onFileCreated)
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileCreated -> Entering function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] start -> savePoint -> onFileCreated -> err = ' + err.message)
callBackFunction(err)
return
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onFileCreated -> Content written = ' + JSON.stringify(existingContent))
}
writeReport = true
controlLoop()
}
}
function appendToFile(fileContent) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> appendToFile -> Entering function.') }
fileStorage.createTextFile(bot.devTeam, filePath + '/' + fileName, JSON.stringify(fileContent), onExistingFileUpdated)
function onExistingFileUpdated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onExistingFileUpdated -> Entering function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] start -> savePoint -> onExistingFileUpdated -> err = ' + err.message)
callBackFunction(err)
return
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, '[INFO] start -> savePoint -> onExistingFileUpdated -> Content written = ' + JSON.stringify(fileContent))
}
writeReport = true
controlLoop()
}
}
function appendLRCPoints(existingLRCPoints) {
if (existingLRCPoints === undefined) {
return lrcPointsFileCache.get(n)
}
for (i = 0; i < lrcPointsFileCache.get(n).length; i++) {
let lrcPoint = lrcPointsFileCache.get(n)[i]
let lastCandleOnFile = existingLRCPoints[existingLRCPoints.length - 1]
if (lastCandleOnFile[0] < lrcPoint[0]) {
existingLRCPoints.push(lrcPoint)
} else {
logger.write(MODULE_NAME, '[WARN] start -> savePoint -> appendLRCPoints -> The LCR Point calculated existed on the file storage, not saving: ' + JSON.stringify(lrcPoint))
}
}
return existingLRCPoints
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> savePoint -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
}
function isPeriodOnHeadOfMarket(timeToValidate) {
let candleBeginIndex = 4
if (!marketFileCache.has(n)) {
return false
}
let currentFile = marketFileCache.get(n)
if (currentFile[currentFile.length - 1] === undefined) {
return false
}
let lastCandlePeriodOnCache = currentFile[currentFile.length - 1][candleBeginIndex]
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> isPeriodOnHeadOfMarket -> Execution: ' + new Date(timeToValidate).toISOString() + '. lastCandlePeriodOnCache: ' + new Date(lastCandlePeriodOnCache).toISOString()) }
if (timeToValidate >= lastCandlePeriodOnCache) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> isPeriodOnHeadOfMarket -> Head of the market found @ ' + new Date(lastCandlePeriodOnCache).toISOString()) }
return true
} else {
return false
}
}
}
function controlLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> buildLRCPoints -> periodsLoop -> controlLoop -> Entering function.') }
n++
if (n < global.marketFilesPeriods.length) {
loopBody()
} else {
if (writeReport) {
writeReport = false
writeStatusReport(advanceTime)
} else {
advanceTime()
}
}
}
}
}
function writeStatusReport(callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> writeStatusReport -> Entering function.') }
let key = bot.devTeam + '-' + bot.codeName + '-' + bot.process + '-' + bot.dataSetVersion
let statusReport = statusDependencies.statusReports.get(key)
statusReport.file.lastExecution = bot.processDatetime
statusReport.file.lastCandles = lastCandles
statusReport.save(callBack)
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> writeStatusReport -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
}
+41
View File
@@ -0,0 +1,41 @@
# I'm Gauss, an i-bot!
AKA an indicator algobot running on the AAPlatform by Advanced Algos Ltd.
### My Specialty
I produce linear regression curves on prices determined by the 15, 30 and 60 minutes resolution candles.
### Ideal for
My datasets are ideal for statistical analysis of prices and for identifying trends.
### Details
| **Name** | **Type** | **Version** | **Release Date** | **Current dataSet** |
|----------|----------|----------|----------|----------|
| Gauss | Indicator | 1.0 | 24 May 2018 | dataSet.V1 |
# My Products
## Dataset 1: Pending
### Current Dataset Scope
* **Exchanges**: Poloniex
* **Markets**: USDT-BTC
* **Range**: Market Start Date – Current Time (-0 to 59 secs)
### Current Dataset
Pending.
### Compatible Plotters
[AAMasters Plotters-Linear-Regression-Curve](https://github.com/AAMasters/Plotters-Linear-Regression-Curve)
### Dependencies
[AAMasters Olivia](https://github.com/AAMasters/AAOlivia-Indicator-Bot)
### Data as a Service (DaaS) Fees
No fees.
# Disclaimer
THE AA MASTERS BOTS AND THEIR ASSOCIATED PRODUCTS AND SERVICES ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, SUITABILITY FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
IN NO EVENT WILL ADVANCED ALGOS BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY USE OF THE AACLOUD, THE AA ARENA COMPETITION, THE AA MASTERS BOTS, OR ANY OTHER ASSOCIATED SERVICE OR WEB SITE, INCLUDING, WITHOUT LIMITATION, ANY LOST PROFITS, BUSINESS INTERRUPTION, LOSS OF FUNDS, PROGRAMS OR OTHER DATA OR OTHERWISE, EVEN IF WE ARE EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+13
View File
@@ -0,0 +1,13 @@
{
"name": "AAGauss",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Matias-Benitez",
"license": "ISC",
"dependencies": {
}
}
+153
View File
@@ -0,0 +1,153 @@
{
"displayName": "Gauss",
"codeName": "AAGauss",
"type": "Indicator",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"devTeam": "AAVikings",
"profilePicture": "Gauss.png",
"dataSetVersion": "dataSet.V1",
"processes": [
{
"name": "Multi-Period-Market",
"description": "Produces files with the LRC Points for the entire market.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"devTeam": "AAVikings",
"bot": "AAGauss",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Market",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAMasters",
"bot": "AAOlivia",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Market",
"dataSetVersion": "dataSet.V1"
}
]
},
{
"name": "Multi-Period-Daily",
"description": "Produces files with the LRC Points on a daily format.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"timePeriod": 1800000,
"statusDependencies": [
{
"devTeam": "AAVikings",
"bot": "AAGauss",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Daily",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAMasters",
"bot": "AAOlivia",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Daily",
"dataSetVersion": "dataSet.V1"
}
]
}
],
"products": [
{
"codeName": "LRCPoints",
"displayName": "LRCPoints",
"description": "Two sets of files per period that contains LRC points.",
"storageAccount": "aagauss",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "AAVikings/AAGauss.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/LRC-Points/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "AAVikings/AAGauss.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/LRC-Points/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "AAVikings/AAGauss.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/LRC-Points/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
}
],
"plotter": {
"devTeam": "AAVikings",
"codeName": "PlotterLinearRegressionCurve",
"moduleName": "LRC"
}
}
]
}