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

This commit is contained in:
Luis Fernando Molina committed 2020-11-20 20:21:26 +01:00
commit bed4f5053d
12 files changed
+961

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/*
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>AAArtudito-Trading-Bot</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "AAArtudito", "AAArtudito.njsproj", "{A54D4656-3DB8-407A-9333-E65115E994AB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A54D4656-3DB8-407A-9333-E65115E994AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A54D4656-3DB8-407A-9333-E65115E994AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A54D4656-3DB8-407A-9333-E65115E994AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A54D4656-3DB8-407A-9333-E65115E994AB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B4BDAEAF-E863-458A-87C1-CC6D8C490784}
EndGlobalSection
EndGlobal
+89
View File
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{a54d4656-3db8-407a-9333-e65115e994ab}</ProjectGuid>
<ProjectHome />
<ProjectView>ShowAllFiles</ProjectView>
<StartupFile>User.Bot.js</StartupFile>
<WorkingDirectory>
</WorkingDirectory>
<OutputPath>.</OutputPath>
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<Name>Project</Name>
<LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
<ItemGroup>
<Content Include="package-lock.json" />
<Content Include="package.json" />
<Content Include="README.md" />
<Content Include="logs\Dummy File.txt" />
<Content Include="obj\Debug\AABruce.njsproj.FileListAbsolute.txt" />
<Content Include="obj\Debug\AACarol.njsproj.FileListAbsolute.txt" />
<Content Include="obj\Debug\AACharly.njsproj.FileListAbsolute.txt" />
<Content Include="this.bot.config.json" />
</ItemGroup>
<ItemGroup>
<Folder Include="bin" />
<Folder Include="logs" />
<Folder Include="logs\AA Masters" />
<Folder Include="logs\AA Masters\Indicator" />
<Folder Include="logs\AA Masters\Indicator\Bruce" />
<Folder Include="logs\AA Masters\Indicator\Bruce\1.0.0" />
<Folder Include="logs\AA Masters\Indicator\Bruce\1.0.0\Multi-Period-Daily-Candles-Volumes" />
<Folder Include="logs\AA Masters\Indicator\Bruce\1.0.0\One-Min-Daily-Candles-Volumes" />
<Folder Include="Trading-Process\" />
<Folder Include="obj" />
<Folder Include="obj\Debug" />
</ItemGroup>
<ItemGroup>
<Compile Include="Trading-Process\User.Bot.js" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<!--Do not delete the following Import Project. While this appears to do nothing it is a marker for setting TypeScript properties before our import that depends on them.-->
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="False" />
<Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsTools.targets" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>0</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:48022/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>True</UseCustomServer>
<CustomServerUrl>http://localhost:1337</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
<WebProjectProperties>
<StartPageUrl>
</StartPageUrl>
<StartAction>CurrentPage</StartAction>
<AspNetDebugging>True</AspNetDebugging>
<SilverlightDebugging>False</SilverlightDebugging>
<NativeDebugging>False</NativeDebugging>
<SQLDebugging>False</SQLDebugging>
<ExternalProgram>
</ExternalProgram>
<StartExternalURL>
</StartExternalURL>
<StartCmdLineArguments>
</StartCmdLineArguments>
<StartWorkingDirectory>
</StartWorkingDirectory>
<EnableENC>False</EnableENC>
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

+24
View File
@@ -0,0 +1,24 @@
Financial Beings License
Collective Copyright by the Advanced Algos Community
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software including without limitation the rights to use, copy, modify,
merge, publish, distribute and sublicense copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following
conditions:
1. Using, copying, modifying, publishing, distributing and sublicensing rights
are restricted to the Advanced Algos Platform, meaning the rights granted can
only be exercised through the Advanced Algos Service.
2. The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+52
View File
@@ -0,0 +1,52 @@
# I'm Artudito, a t-bot!
AKA a trading algobot running on the AAPlatform by Advanced Algos Ltd.
### My Specialty
I work on the USDT-BTC market with a strategy based on linear regression curves.
### Ideal for
Trading short-lived but established trends.
### Details
| **Name** | **Type** | **Version** | **Release Date** | **Current dataSet** |
|----------|----------|----------|----------|----------|
| Artudito |Trading | 1.0 | May 2018 | dataSet.V1 |
# My Strategy
### Markets
* **Exchanges**: Poloniex
* **Markets**: USDT-BTC
### Description
I use the analysis performed by [AAMasters Gauss i-bot](https://github.com/AAMasters/AAGauss-Indicator-Bot) on recent historic prices in which he applies a [linear regression]( https://en.wikipedia.org/wiki/Linear_regression) to a set of price values in the 15, 30 and 60 minutes candles to obtain model linear regression curves. Based on those curves, I apply my own ruleset.
#### Basic Rule Set
* If the tilt of the linear regression curve points up on the 15, 30, and 60 minutes scale then I go long.
* If the tilt of the linear regression curve points down on the 15, 30, and 60 minutes scale then I go short.
* If the linear regression curve of the 15 and 30 minutes scale change direction, I exit.
#### Examples
* The asset is trending up on the 15, 30 and 60 minutes scale. Then the 15 minutes curve turns down and the 30 minutes curve goes flat: I exit.
* The asset is trending down on all three indicators. Then the 15 minutes curve turns up and the 30 minutes curve turns flat or upwards: I enter.
* The tilt of the 15, 30 and 60 minutes curves is flat: I don't enter or exit.
### Compatible Plotters
[AAMasters Plotters-Trading](https://github.com/AAMasters/Plotters-Trading)
### Dependencies
* [AAMasters Bruce](https://github.com/AAMasters/AABruce-Indicator-Bot)
* [AAMasters Olivia](https://github.com/AAMasters/AAOlivia-Indicator-Bot)
* [AAMasters Gauss](https://github.com/AAMasters/AAGauss-Indicator-Bot)
### Trading as a Service (TaaS) 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.
+521
View File
@@ -0,0 +1,521 @@
exports.newUserBot = function newUserBot (bot, logger) {
/*
Creating a new instance from the platform of this bot:
- bot: An instance of the bot configuration
- logger: An instance of the platform logger that allows both: to save logs
on cloud execution and shows logs on the browser console on browser execution
*/
// Variable used for logs, which will be passed to the logger instance
const MODULE_NAME = 'User Bot'
// Debug log level
const FULL_LOG = true
// Info log level
const LOG_INFO = true
/*
The reference to the Trading Platform Advanced Algos Assistant that will
allow to:
dataDependencies: Retrieve all dependencies to other bots defined on the configuration
putPosition: Put a buy or sell position at the exchange (only limit orders at this point)
movePosition: Move an existing position
getPositions: Obtain all the positions existing on the exchange
getBalance: Obtain the total balance on the exchange known by the platform (at this point 0.001 BTC)
getAvailableBalance: Obtain the current available balance on the exchange known by the platform
getInvestment: Obtain initial investment known by the platform (at this point 0.001 BTC)
getProfits: Get current profits at this point in time
getCombinedProfits: Get current profits
getROI: Get current ROI
getMarketRate: Get current market rate
getTicker: Gets the current highest bid and lowest ask on the exchange
sendMessage: Put a visual message on the platform (different zoom levels [1-10] can be handled)
rememberThis: Store a string variable across executions by key value pairs
remindMeOf: Get a stored string value by key
sendEmail: Send an email during execution as notifications
*/
let assistant
/*
This is a dependency example to other bots, in this case to the the
LRC Indicator.
*/
let gaussStorage
/*
This objects returns two public functions that will be used to integrate
with the platform.
*/
let thisObject = {
initialize: initialize,
start: start
}
return thisObject
/*
The initialize function must be implemented by all trading bots.
pAssistant: the instance of the platform which methods will allow to put getPositions
pGenes: This variable will be used during initialization to get the parameters
from the platform. At the moment it is not used but in future releases
it will allow to create different clones of the Algobot, to form an Algonet.
*/
function initialize (pAssistant, pGenes, callBackFunction) {
try {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] initialize -> Entering function.') }
// Integration with platform logger, when running on cloud
logger.fileName = MODULE_NAME
/*
We keep a local reference to the assistant that allows to put
positions on the exchange, created and managed by the platform
*/
assistant = pAssistant
/*
On 'assistant.dataDependencies.dataSets' we will receive the data files
retrieved for those defined on the bot configuration.
Note that the key is:
TeamName-BotName-BotProductFolder-BotProcessName-BotDataSetVersion
In this case, this particular bot will use Gauss indicator; other
indicators are available. You can use any indicator you want,
or create a new one if you should need to.
*/
let key = 'AAVikings-AAGauss-LRC-Points-Multi-Period-Daily-dataSet.V1'
gaussStorage = assistant.dataDependencies.dataSets.get(key)
/*
Once Completed we must return the global.DEFAULT_OK_RESPONSE
*/
callBackFunction(global.DEFAULT_OK_RESPONSE)
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] initialize -> onDone -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
/*
The start function is called by the platform for executing the bot every 1 minute
*/
function start (callBackFunction) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> Entering function.') }
/*
This breakpoint will be called once the web platform reaches the graphical stop point.
You can find more details on how this works here:
https://advancedalgos.net/documentation-interface.shtml
*/
if (global.AT_BREAKPOINT === true) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Plot Breakpoint Hit."); } // Place breakpoint in this line
}
const market = global.MARKET
const CHANNEL_DOWN = -1
const CHANNEL_UP = 1
const NO_CHANNEL = 0
/*
Here we call the main function that will check buy, sell or move conditions.
*/
businessLogic(onDone)
/*
This function will be called after we complete all validations and operations,
we make sure everything was ok before returning the control to the platform.
The platform will check this 3 situations:
global.DEFAULT_OK_RESPONSE: Proceed with normal execution
global.DEFAULT_RETRY_RESPONSE: Retry after 10 seconds
global.DEFAULT_FAIL_RESPONSE: Finish in failure state
(this allows us to check the logs and fix execution errors when they occur)
*/
function onDone (err) {
try {
switch (err.result) {
case global.DEFAULT_OK_RESPONSE.result: {
logger.write(MODULE_NAME, '[INFO] start -> onDone -> Execution finished well. :-)')
callBackFunction(global.DEFAULT_OK_RESPONSE)
return
}
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
logger.write(MODULE_NAME, '[ERROR] start -> onDone -> Retry Later. Requesting Execution Retry.')
callBackFunction(global.DEFAULT_RETRY_RESPONSE)
return
}
case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
logger.write(MODULE_NAME, '[ERROR] start -> onDone -> Operation Failed. Aborting the process. err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> onDone -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
/*
We will check the direction of the channel and based on that create a buy or sell
position in the market.
Here is a detailed explanation of the bot:
https://github.com/AAVikings/AAArtudito-Trading-Bot/blob/master/README.md
*/
function businessLogic (callBack) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> Entering function.') }
// Simulation for forcing buy or sell on each execution without checking the rules
let forceBuyAndSell = false
if(forceBuyAndSell){
if (Math.random(1) <= 0.5){
createBuyPosition(callBack);
}else{
createSellPosition(callBack);
}
}else{
// Here we really check the bot business logic
getChannelTilt(firstTiltCheck);
}
function firstTiltCheck (err, channelTilt) {
try {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> firstTiltCheck -> Entering Function.') }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] start -> businessLogic -> firstTiltCheck -> err = ' + err.message)
callBack(global.DEFAULT_FAIL_RESPONSE)
} else {
if (channelTilt == 1) {
createBuyPosition(callBack)
} else if (channelTilt == -1) {
createSellPosition(callBack)
} else {
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> firstTiltCheck -> Nothing to do, there isn't a buy or sell opportunity.") }
callBack(global.DEFAULT_OK_RESPONSE)
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> businessLogic -> firstTiltCheck -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
}
/*
Here we will get the file from the indicator bot, that already has all values calculated.
This will provide more efficiency and allow other bots to consume the services as well.
*/
function getChannelTilt (callBack) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> getChannelTilt -> Entering function.') }
let lrcPoint, previousLRCPoint
let queryDate = new Date(bot.processDatetime)
getLRCPointsFile(queryDate, onLRCPointsFileReceived)
function onLRCPointsFileReceived (err, lrcPointsFile) {
try {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> getChannelTilt -> onLRCPointsFileReceived.') }
let lastIndexLrcPointsFile = lrcPointsFile.length - 1
let lastAvailableDateTime = lrcPointsFile[lastIndexLrcPointsFile][0]
if (bot.processDatetime.valueOf() <= lastAvailableDateTime || !isExecutionToday()) {
for (let i = 0; i < lrcPointsFile.length; i++) {
if (bot.processDatetime.valueOf() >= lrcPointsFile[i][0] && bot.processDatetime.valueOf() < lrcPointsFile[i][1]) {
lrcPoint = {
begin: lrcPointsFile[i][0],
end: lrcPointsFile[i][1],
min: lrcPointsFile[i][2],
mid: lrcPointsFile[i][3],
max: lrcPointsFile[i][4]
}
if (i >= 1) {
let previous = i - 1
previousLRCPoint = {
begin: lrcPointsFile[previous][0],
end: lrcPointsFile[previous][1],
min: lrcPointsFile[previous][2],
mid: lrcPointsFile[previous][3],
max: lrcPointsFile[previous][4]
}
applyBotRules(lrcPoint, previousLRCPoint, callBack)
} else {
queryDate.setDate(queryDate.getDate() - 1)
getLRCPointsFile(queryDate, onPreviousLRCPointsFileReceived)
}
return
}
}
logger.write(MODULE_NAME, '[ERROR] start -> businessLogic -> getChannelTilt -> onLRCPointsFileReceived. The expected LRC Point was not found: ' + bot.processDatetime.valueOf())
callBack(global.DEFAULT_FAIL_RESPONSE)
} else {
// Running live we will process last available LRCPoint only if it's delayed 25 minutes top
let maxTolerance = 25 * 60 * 1000
if (bot.processDatetime.valueOf() <= (lastAvailableDateTime + maxTolerance)) {
lrcPoint = {
begin: lrcPointsFile[lastIndexLrcPointsFile][0],
end: lrcPointsFile[lastIndexLrcPointsFile][1],
min: lrcPointsFile[lastIndexLrcPointsFile][2],
mid: lrcPointsFile[lastIndexLrcPointsFile][3],
max: lrcPointsFile[lastIndexLrcPointsFile][4]
}
if (lastIndexLrcPointsFile >= 1) {
let previous = lastIndexLrcPointsFile - 1
previousLRCPoint = {
begin: lrcPointsFile[previous][0],
end: lrcPointsFile[previous][1],
min: lrcPointsFile[previous][2],
mid: lrcPointsFile[previous][3],
max: lrcPointsFile[previous][4]
}
applyBotRules(lrcPoint, previousLRCPoint, callBack)
} else {
queryDate.setDate(queryDate.getDate() - 1)
getLRCPointsFile(queryDate, onPreviousLRCPointsFileReceived)
}
} else {
if (LOG_INFO === true) logger.write(MODULE_NAME, '[WARN] start -> getChannelTilt -> onLRCPointsFileReceived. Available candle older than 5 minutes. Skipping execution.')
callBack(global.DEFAULT_OK_RESPONSE, NO_CHANNEL)
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> getChannelTilt -> onLRCPointsFileReceived -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
/*
Depending on the time requested, the necessary information could be on the
file from the day before.
*/
function onPreviousLRCPointsFileReceived (err, lrcPointsFile) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> getChannelTilt -> onLRCPointsFileReceived.') }
let previous = lrcPointsFile.length - 1
previousLRCPoint = {
begin: lrcPointsFile[previous][0],
end: lrcPointsFile[previous][1],
min: lrcPointsFile[previous][2],
mid: lrcPointsFile[previous][3],
max: lrcPointsFile[previous][4]
}
applyBotRules(lrcPoint, previousLRCPoint, callBack)
}
}
/*
We have all the information needed to proceed to applying all the rules.
*/
function applyBotRules (lrcPoint, previousLRCPoint, callBack) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> applyBotRules -> Entering Function.') }
let channelTilt = NO_CHANNEL
let ruleApplied = ''
if (lrcPoint.max < lrcPoint.mid && lrcPoint.min > lrcPoint.mid && lrcPoint.mid < lrcPoint.min) {
if (lrcPoint.min > previousLRCPoint.min && lrcPoint.mid > previousLRCPoint.mid && lrcPoint.max > previousLRCPoint.max) {
// The channel points UP
channelTilt = CHANNEL_UP
ruleApplied += 'Rule_1.'
}
}
if (lrcPoint.min < lrcPoint.mid && lrcPoint.max > lrcPoint.mid && lrcPoint.mid < lrcPoint.max) {
if (lrcPoint.min < previousLRCPoint.min && lrcPoint.mid < previousLRCPoint.mid && lrcPoint.max < previousLRCPoint.max) {
// The channel points DOWN
channelTilt = CHANNEL_DOWN
ruleApplied += 'Rule_2.'
}
}
if (lrcPoint.min < previousLRCPoint.min && lrcPoint.mid <= previousLRCPoint.mid) {
// 15 AND 30 changed direction from up to down
channelTilt = CHANNEL_DOWN
ruleApplied += 'Rule_3a.'
}
if (lrcPoint.min > previousLRCPoint.min && lrcPoint.mid >= previousLRCPoint.mid) {
// 15 AND 30 changed direction from down to up
channelTilt = CHANNEL_UP
ruleApplied += 'Rule_3b.'
}
let logMessage = bot.processDatetime.toISOString() + '. Rule: ' + ruleApplied + '. Tilt: ' + channelTilt + ' (' + Number(lrcPoint.min).toLocaleString() + '|' + Number(lrcPoint.mid).toLocaleString() + '|' + Number(lrcPoint.max).toLocaleString() + ')'
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> getChannelTilt -> applyBotRules -> Results: ' + logMessage) }
callBack(global.DEFAULT_OK_RESPONSE, channelTilt)
}
/*
Here we will get the file from the indicator bot, that already has all values calculated.
This will provide more efficiency and allow other bots to consume the services as well.
*/
function getLRCPointsFile (dateTime, callback) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> getLRCPointsFile -> Entering function.') }
let datePath = dateTime.getUTCFullYear() + '/' + pad(dateTime.getUTCMonth() + 1, 2) + '/' + pad(dateTime.getUTCDate(), 2)
let filePath = 'LRC-Points/Multi-Period-Daily/30-min/' + datePath
let fileName = market.assetA + '_' + market.assetB + '.json'
/*
bot.botCache: allows us to keep a map (key value pairs) between executions,
so we don't need to go to the storage to retrieve this value.
If the value already exist on the cache we will get it from there,
otherwise it will be retrieved from the bot storage.
*/
if (bot.startMode === 'Backtest' && bot.botCache.has(filePath)) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> getLRCPointsFile -> Getting the file from local cache.') }
cleanUpCache(bot.botCache, 'LRC-Points/Multi-Period-Daily/30-min', dateTime)
callback(global.DEFAULT_OK_RESPONSE, bot.botCache.get(filePath))
} else {
/*
The structure of the indicator files and the retrieved file itself
is explained on the indicator bot readme file.
*/
gaussStorage.getTextFile(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 -> getChannelTilt -> getLRCPointsFile -> onFileReceived > Entering Function.') }
let lrcPointsFile = JSON.parse(text)
bot.botCache.set(filePath, lrcPointsFile)
callback(global.DEFAULT_OK_RESPONSE, lrcPointsFile)
} else {
logger.write(MODULE_NAME, '[ERROR] start -> getChannelTilt -> getLRCPointsFile -> onFileReceived -> Failed to get the file. Will abort the process and request a retry.')
callBackFunction(global.DEFAULT_RETRY_RESPONSE)
return
}
}
function cleanUpCache (fileCache, path, dateTimeCacheFile) {
let date = new Date(dateTimeCacheFile)
date.setDate(date.getDate() - 2)
let datePath = date.getUTCFullYear() + '/' + pad(date.getUTCMonth() + 1, 2) + '/' + pad(date.getUTCDate(), 2)
let cachePosition = path + '/' + datePath
// Remove and go back one day
if (fileCache.has(cachePosition)) {
fileCache.delete(cachePosition)
date.setDate(date.getDate() - 1)
cleanUpCache(fileCache, date)
}
}
}
function createBuyPosition (callBack) {
try {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createBuyPosition -> Entering function.') }
// We get the current positions we have at the exchange
let positions = assistant.getPositions()
let assetABalance = assistant.getAvailableBalance().assetA
let currentRate = assistant.getTicker().ask // *.9
let amountA = assistant.getAvailableBalance().assetA
let amountB = Number((amountA / currentRate).toFixed(8))
if (positions.length > 0 && positions[0].type === 'buy' && positions[0].status !== 'executed') {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createBuyPosition -> Moving an existing BUY position to a new price: $' + Number(currentRate).toLocaleString()) }
let message = 'Moving an existing buy position to a new price: $' + Number(currentRate).toLocaleString()
message += '. Combined ROI on current execution: ' + getCombinedProfit() + '%. '
assistant.sendMessage(6, 'Moving Position', message)
message = bot.processDatetime.toISOString() + ' - ' + message
assistant.sendEmail('Alerts', message, bot.emailSubscriptions)
assistant.movePosition(positions[0], currentRate, callBack)
} else if (assetABalance > 0) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createBuyPosition -> Artuditu put a new BUY position at price: $' + Number(currentRate).toLocaleString()) }
let message = 'Creating a new buy position. Price: $' + Number(currentRate).toLocaleString()
message += '. Combined ROI on current execution: ' + getCombinedProfit() + '%. '
assistant.sendMessage(7, 'Buying', message)
message = bot.processDatetime.toISOString() + ' - ' + message
assistant.sendEmail('Alerts', message, bot.emailSubscriptions)
assistant.putPosition('buy', currentRate, amountA, amountB, callBack)
} else {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createBuyPosition -> Not enough available balance to buy.') }
callBack(global.DEFAULT_OK_RESPONSE)
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> businessLogic -> createBuyPosition -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function createSellPosition (callBack) {
try{
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createSellPosition -> Entering function.') }
// We get the current positions we have at the exchange
let positions = assistant.getPositions()
let assetBBalance = assistant.getAvailableBalance().assetB
let currentRate = assistant.getTicker().bid // *1.1
let amountB = assistant.getAvailableBalance().assetB
let amountA = amountB * currentRate
if (positions.length > 0 && positions[0].type === 'sell' && positions[0].status !== 'executed') {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createSellPosition -> Artuditu is moving an existing SELL position to a new price: $' + Number(currentRate).toLocaleString()) }
let message = 'Moving an existing sell position to a new price: $' + Number(currentRate).toLocaleString()
message += '. Combined ROI on current execution: ' + getCombinedProfit() + '%. '
assistant.sendMessage(6, 'Moving Position', message)
message = bot.processDatetime.toISOString() + ' - ' + message
assistant.sendEmail('Alerts', message, bot.emailSubscriptions)
assistant.movePosition(positions[0], currentRate, callBack)
} else if (assetBBalance > 0) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createSellPosition -> Artuditu put a new SELL position at price: $' + Number(currentRate).toLocaleString()) }
let message = 'Creating a new sell position. Price: $' + Number(currentRate).toLocaleString()
message += '. Combined ROI on current execution: ' + getCombinedProfit() + '%. '
assistant.sendMessage(7, 'Selling', message)
message = bot.processDatetime.toISOString() + ' - ' + message
assistant.sendEmail('Alerts', message, bot.emailSubscriptions)
assistant.putPosition('sell', currentRate, amountA, amountB, callBack)
} else {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createSellPosition -> There is not enough available balance to sell.') }
callBack(global.DEFAULT_OK_RESPONSE)
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> businessLogic -> createBuyPosition -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function pad (str, max) {
str = str.toString()
return str.length < max ? pad('0' + str, max) : str
}
function isExecutionToday () {
let localDate = new Date()
let today = new Date(Date.UTC(
localDate.getUTCFullYear(),
localDate.getUTCMonth(),
localDate.getUTCDate()))
return (today.getUTCFullYear() === bot.processDatetime.getUTCFullYear()
&& today.getUTCMonth() === bot.processDatetime.getUTCMonth()
&& today.getUTCDate() === bot.processDatetime.getUTCDate())
}
function getCombinedProfit () {
if (assistant.getCombinedProfits() !== undefined) {
if (assistant.getCombinedProfits().assetA > 0) {
return Number(assistant.getCombinedProfits().assetA).toLocaleString()
} else {
return Number(assistant.getCombinedProfits().assetB).toLocaleString()
}
}
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "AAArtudito",
"version": "1.0.0",
"lockfileVersion": 1
}
+12
View File
@@ -0,0 +1,12 @@
{
"name": "AAArtudito",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Matias-Benitez",
"license": "ISC",
"dependencies": {}
}
+209
View File
@@ -0,0 +1,209 @@
{
"displayName": "Artudito",
"codeName": "AAArtudito",
"type": "Trading",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"devTeam": "AAVikings",
"profilePicture": "Artudito.png",
"dataSetVersion": "dataSet.V1",
"genes": [
{
"name": "stopLoss",
"description": "This represent the percentage of the stop loss configuration. It could be from 0 (disabled) to 100.",
"lowerLimit": 0,
"upperLimit": 5
}
],
"processes": [
{
"name": "Trading-Process",
"description": "Simple trading strategy to be used as a template.",
"startMode": {
"live": {
"run": "true",
"resumeExecution": "false"
},
"backtest": {
"run": "false",
"beginDatetime": "2018-04-11T00:00:00.000Z",
"endDatetime": "2018-06-17T23:59:59.000Z",
"waitTime": 1
},
"competition": {
"run": "false",
"beginDatetime": "2018-07-04T00:00:00.000Z",
"endDatetime": "2018-07-06T23:59:59.000Z",
"resumeExecution": "true"
}
},
"normalWaitTime": 10000,
"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": "AAVikings",
"bot": "AAGauss",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Market",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAVikings",
"bot": "AAArtudito",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Trading-Process",
"dataSetVersion": "dataSet.V1"
}
],
"dataDependencies": [
{
"devTeam": "AAVikings",
"bot": "AAGauss",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "LRC-Points",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Daily"
},
{
"devTeam": "AAVikings",
"bot": "AAGauss",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "LRC-Points",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AABruce",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Candles",
"dataSetVersion": "dataSet.V1",
"dataSet": "One-Min"
},
{
"devTeam": "AAMasters",
"bot": "AAOlivia",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Candles",
"dataSet": "Multi-Period-Daily",
"dataSetVersion": "dataSet.V1"
}
]
}
],
"products": [
{
"codeName": "Live Trading History",
"displayName": "Live Trading History",
"shortDisplayName": "Live",
"description": "General information about Artudito trading history.",
"storageAccount": "aaartudito",
"dataSets": [
{
"codeName": "Oficial History",
"type": "File Sequence",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs", "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process",
"fileName": "Execution.History.Live.@Sequence.json"
}
],
"exchangeList": [
{
"name": "Poloniex"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTrading",
"moduleName": "History"
}
},
{
"codeName": "Backtest Trading History",
"displayName": "Backtest Trading History",
"shortDisplayName": "Backtest",
"description": "General information about Artudito backtest trading history.",
"storageAccount": "aaartudito",
"dataSets": [
{
"codeName": "Backtest History",
"type": "File Sequence",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs", "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process",
"fileName": "Execution.History.Backtest.@Sequence.json"
}
],
"exchangeList": [
{
"name": "Poloniex"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTrading",
"moduleName": "History"
}
},
{
"codeName": "Competition Trading History",
"displayName": "Competition Trading History",
"shortDisplayName": "Competition",
"description": "General information about Artudito competition trading history.",
"storageAccount": "aaartudito",
"dataSets": [
{
"codeName": "Competition History",
"type": "File Sequence",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs", "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process",
"fileName": "Execution.History.Competition.@Sequence.json"
}
],
"exchangeList": [
{
"name": "Poloniex"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTrading",
"moduleName": "History"
}
}
],
"emailSubscriptions": "vikings.alerts@gmail.com"
}