diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..82c487bdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ + +.vs/* +obj/* + +bin/* + +*.config +*.log +*.cache +node_modules/* + diff --git a/Competition.png b/Competition.png new file mode 100644 index 000000000..a4ee16263 Binary files /dev/null and b/Competition.png differ diff --git a/Details.js b/Details.js new file mode 100644 index 000000000..fc3db73c1 --- /dev/null +++ b/Details.js @@ -0,0 +1,727 @@ +function newAAMastersPlottersTradingDetails() { + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + container: undefined, + getContainer: getContainer, + setTimePeriod: setTimePeriod, + setDatetime: setDatetime, + draw: draw + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let timeLineCoordinateSystem = newTimeLineCoordinateSystem(); // Needed to be able to plot on the timeline, otherwise not. + let timeLineCoordinateSystemFrame = newTimeLineCoordinateSystem(); // This chart uses this extra object. + + let timePeriod; // This will hold the current Time Period the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let fileCache; // This object will provide the different Market Files at different Time Periods. + let fileCursorCache; // This object will provide the different File Cursors at different Time Periods. + + /* these are module specific variables: */ + + let volumes = []; // Here we keep the volumes to be ploted every time the Draw() function is called by the AAWebPlatform. + + return thisObject; + + function initialize(pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) { + + let cursorCacheInProgress = false; + let finaleStepsInProgress = false; + + datetime = pDatetime; + timePeriod = pTimePeriod; + + fileCache = newFileCache(); + fileCache.initialize("AAMasters", "AAOlivia", "Market Volumes", "Market Volumes", pExchange, pMarket, onFileReady); + + function onFileReady() { + + let newMarketFile = fileCache.getFile(ONE_DAY_IN_MILISECONDS); + + if (newMarketFile !== undefined) { + + marketFile = newMarketFile; + + initializeFileCursorCache(); + + } + } + + function initializeFileCursorCache() { + + if (cursorCacheInProgress === false) { + + cursorCacheInProgress = true; + + fileCursorCache = newFileCursorCache(); + fileCursorCache.initialize("AAMasters", "AAOlivia", "Daily Volumes", "Daily Volumes", pExchange, pMarket, pDatetime, pTimePeriod, onFileCursorReady); + + } + } + + function onFileCursorReady() { + + recalculate(); + + let newFileCursor = fileCursorCache.getFileCursor(pTimePeriod); + + if (newFileCursor !== undefined) { // if the file ready is the one we need then it and we dont have it yet, then we will continue here. + + let stringDate = datetime.getUTCFullYear() + '-' + pad(datetime.getUTCMonth() + 1, 2) + '-' + pad(datetime.getUTCDate(), 2); + + let dailyFile = newFileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + if (finaleStepsInProgress === false) { + + finaleStepsInProgress = true; + + fileCursor = newFileCursor; + + recalculateScaleX(); + recalculate(); + recalculateScaleY(); + + viewPort.eventHandler.listenToEvent("Zoom Changed", onZoomChanged); + canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + + callBackFunction(); + + } + } + } + + } + + } + + function getContainer(point) { + + let container; + + /* First we check if this point is inside this space. */ + + if (this.container.frame.isThisPointHere(point) === true) { + + return this.container; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } + + function setTimePeriod(pTimePeriod) { + + timePeriod = pTimePeriod; + + if (timePeriod >= _1_HOUR_IN_MILISECONDS) { + + let newMarketFile = fileCache.getFile(pTimePeriod); + + if (newMarketFile !== undefined) { + + marketFile = newMarketFile; + + recalculate(); + + } + + } else { + + fileCursorCache.setTimePeriod(pTimePeriod); + + fileCursorCache.setDatetime(datetime); + + let newFileCursor = fileCursorCache.getFileCursor(pTimePeriod); + + if (newFileCursor !== undefined) { + + fileCursor = newFileCursor; + + recalculate(); + + } + } + + if (timePeriod === _1_HOUR_IN_MILISECONDS) { + + fileCursorCache.setTimePeriod(pTimePeriod); + + fileCursorCache.setDatetime(datetime); + + } + + } + + function setDatetime(pDatetime) { + + datetime = pDatetime; + + } + + function draw() { + + this.container.frame.draw(); + + if (timePeriod < _1_HOUR_IN_MILISECONDS) { + + if (Math.random() * 1000 > 995) { + + recalculateScaleX(); + recalculate(); + recalculateScaleY(); + + } + } + + plotChart(); + + } + + function recalculate() { + + if (timePeriod >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Volumes Changed", volumes); + } + + function recalculateUsingDailyFiles() { + + if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timePeriod); + + let leftDate = getDateFromPoint(viewPort.visibleArea.topLeft, thisObject.container, timeLineCoordinateSystem); + let rightDate = getDateFromPoint(viewPort.visibleArea.topRight, thisObject.container, timeLineCoordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + volumes = []; + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let volume = { + amountBuy: 0, + amountSell: 0, + begin: undefined, + end: undefined + }; + + volume.amountBuy = dailyFile[i][0]; + volume.amountSell = dailyFile[i][1]; + + volume.begin = dailyFile[i][2]; + volume.end = dailyFile[i][3]; + + if (volume.begin >= farLeftDate.valueOf() && volume.end <= farRightDate.valueOf()) { + + volumes.push(volume); + + if (datetime.valueOf() >= volume.begin && datetime.valueOf() <= volume.end) { + + thisObject.container.eventHandler.raiseEvent("Current Candle Changed", thisObject.currentCandle); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by volumes. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + if (volumes.length > 0) { + + if (volumes[0].begin > lowerEnd || volumes[volumes.length - 1].end < upperEnd) { + + setTimeout(recalculate, 2000); + + //console.log("File missing while calculating volumes, scheduling a recalculation in 2 seconds."); + + } + } + + //console.log("Olivia > recalculateUsingDailyFiles > total volumes generated : " + volumes.length); + + } + + function recalculateUsingMarketFiles() { + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timePeriod); + + let leftDate = getDateFromPoint(viewPort.visibleArea.topLeft, thisObject.container, timeLineCoordinateSystem); + let rightDate = getDateFromPoint(viewPort.visibleArea.topRight, thisObject.container, timeLineCoordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + volumes = []; + + for (let i = 0; i < marketFile.length; i++) { + + let volume = { + amountBuy: 0, + amountSell: 0, + begin: undefined, + end: undefined + }; + + volume.amountBuy = marketFile[i][0]; + volume.amountSell = marketFile[i][1]; + + volume.begin = marketFile[i][2]; + volume.end = marketFile[i][3]; + + if (volume.begin >= leftDate.valueOf() && volume.end <= rightDate.valueOf()) { + + volumes.push(volume); + + if (datetime.valueOf() >= volume.begin && datetime.valueOf() <= volume.end) { + + thisObject.container.eventHandler.raiseEvent("Current Volume Changed", thisObject.currentCandle); + + } + } + } + + //console.log("Olivia > recalculateUsingMarketFiles > total volumes generated : " + volumes.length); + } + + function recalculateScaleX() { + + + var minValue = { + x: MIN_PLOTABLE_DATE.valueOf() + }; + + var maxValue = { + x: MAX_PLOTABLE_DATE.valueOf() + }; + + timeLineCoordinateSystem.initializeX( + minValue, + maxValue, + thisObject.container.frame.width + ); + + timeLineCoordinateSystemFrame.initializeX( + minValue, + maxValue, + thisObject.container.frame.width + ); + + } + + function recalculateScaleY() { + + var minValue = { + y: 0 + }; + + var maxValue = { + y: 0 + }; + + let timePeriodRatio = ONE_DAY_IN_MILISECONDS / timePeriod; + + maxValue.y = getMaxVolume() / (timePeriodRatio / 10); + + timeLineCoordinateSystem.initializeY( + minValue, + maxValue, + viewPort.visibleArea.bottomRight.y - viewPort.visibleArea.topLeft.y + ); + + timeLineCoordinateSystemFrame.initializeY( + minValue, + maxValue, + thisObject.container.frame.height + ); + + function getMaxVolume() { + + let maxValue = 0; + + for (var i = 0; i < marketFile.length; i++) { + + let currentMax = (marketFile[i][0] + marketFile[i][1]) * 4; + + if (maxValue < currentMax) { + maxValue = currentMax; + } + } + + return maxValue; + + } + + } + + function plotChart() { + + if (volumes.length > 0) { + + /* Now we calculate and plot the volumes */ + + let visibleHeight = viewPort.visibleArea.bottomRight.y - viewPort.visibleArea.topLeft.y; + + let frameCorner1 = { + x: 0, + y: 0 + }; + + let frameCorner2 = { + x: thisObject.container.frame.width, + y: thisObject.container.frame.height + }; + + + /* Now the transformations. */ + + frameCorner1 = transformThisPoint(frameCorner1, thisObject.container.frame.container); + frameCorner2 = transformThisPoint(frameCorner2, thisObject.container.frame.container); + + let frameHeightInViewPort = frameCorner2.y - frameCorner1.y; + + if (volumes.length > 0) { + + for (var i = 0; i < volumes.length; i++) { + + volume = volumes[i]; + + let volumePointA1; + let volumePointA2; + let volumePointA3; + let volumePointA4; + + function calculateBuys(plot, height) { + + volumePointA1 = { + x: volume.begin + timePeriod / 7 * 2, + y: 0 + }; + + volumePointA2 = { + x: volume.begin + timePeriod / 7 * 2, + y: volume.amountBuy + }; + + volumePointA3 = { + x: volume.begin + timePeriod / 7 * 5, + y: volume.amountBuy + }; + + volumePointA4 = { + x: volume.begin + timePeriod / 7 * 5, + y: 0 + }; + + volumePointA1 = plot.transformThisPoint(volumePointA1); + volumePointA2 = plot.transformThisPoint(volumePointA2); + volumePointA3 = plot.transformThisPoint(volumePointA3); + volumePointA4 = plot.transformThisPoint(volumePointA4); + + volumePointA1 = transformThisPoint(volumePointA1, thisObject.container); + volumePointA2 = transformThisPoint(volumePointA2, thisObject.container); + volumePointA3 = transformThisPoint(volumePointA3, thisObject.container); + volumePointA4 = transformThisPoint(volumePointA4, thisObject.container); + + let baseIncrement = (volumePointA3.x - volumePointA1.x) * WIDHTER_VOLUME_BAR_BASE_FACTOR; + + volumePointA1.x = volumePointA1.x - baseIncrement; + volumePointA4.x = volumePointA4.x + baseIncrement; + + if (volumePointA4.x < viewPort.visibleArea.bottomLeft.x || volumePointA1.x > viewPort.visibleArea.bottomRight.x) { + return false; + } + + return true; + + } + + if (calculateBuys(timeLineCoordinateSystemFrame, thisObject.container.frame.height) === false) { continue; } // We try to see if it fits in the visible area. + + if (volumePointA1.y > viewPort.visibleArea.bottomLeft.y && frameHeightInViewPort > visibleHeight * 2 / 3) { + + if (calculateBuys(timeLineCoordinateSystem, visibleHeight) === false) { continue; } // We snap t to the view port. + + /* Now we set the real value of y. */ + + volumePointA1.y = viewPort.visibleArea.bottomRight.y; + volumePointA2.y = viewPort.visibleArea.bottomRight.y - volume.amountBuy * timeLineCoordinateSystem.scale.y; + volumePointA3.y = viewPort.visibleArea.bottomRight.y - volume.amountBuy * timeLineCoordinateSystem.scale.y; + volumePointA4.y = viewPort.visibleArea.bottomRight.y; + + } + + let volumePointB1; + let volumePointB2; + let volumePointB3; + let volumePointB4; + + function calculateSells(plot, height) { + + volumePointB1 = { + x: volume.begin + timePeriod / 7 * 2, + y: height + }; + + volumePointB2 = { + x: volume.begin + timePeriod / 7 * 2, + y: height - volume.amountSell + }; + + volumePointB3 = { + x: volume.begin + timePeriod / 7 * 5, + y: height - volume.amountSell + }; + + volumePointB4 = { + x: volume.begin + timePeriod / 7 * 5, + y: height + }; + + volumePointB1 = plot.transformThisPoint2(volumePointB1); + volumePointB2 = plot.transformThisPoint2(volumePointB2); + volumePointB3 = plot.transformThisPoint2(volumePointB3); + volumePointB4 = plot.transformThisPoint2(volumePointB4); + + volumePointB1 = transformThisPoint(volumePointB1, thisObject.container); + volumePointB2 = transformThisPoint(volumePointB2, thisObject.container); + volumePointB3 = transformThisPoint(volumePointB3, thisObject.container); + volumePointB4 = transformThisPoint(volumePointB4, thisObject.container); + + } + + calculateSells(timeLineCoordinateSystemFrame, thisObject.container.frame.height); // We try to see if it fits in the visible area. + + if (volumePointB1.y < viewPort.visibleArea.topLeft.y && frameHeightInViewPort > visibleHeight * 2 / 3) { + + calculateSells(timeLineCoordinateSystem, visibleHeight); // We snap it to the view port. + + /* Now we set the real value of y. */ + + volumePointB1.y = viewPort.visibleArea.topLeft.y; + volumePointB2.y = viewPort.visibleArea.topLeft.y + volume.amountSell * timeLineCoordinateSystem.scale.y; + volumePointB3.y = viewPort.visibleArea.topLeft.y + volume.amountSell * timeLineCoordinateSystem.scale.y; + volumePointB4.y = viewPort.visibleArea.topLeft.y; + + } + + /* We put a wider base */ + + let baseIncrement = (volumePointB3.x - volumePointB1.x) * WIDHTER_VOLUME_BAR_BASE_FACTOR; + + volumePointB1.x = volumePointB1.x - baseIncrement; + volumePointB4.x = volumePointB4.x + baseIncrement; + + /* We put a less wider top */ + + let baseDencrement = (volumePointA3.x - volumePointA2.x) * LESS_WIDHTER_VOLUME_BAR_TOP_FACTOR; + + volumePointA2.x = volumePointA2.x + baseDencrement; + volumePointA3.x = volumePointA3.x - baseDencrement; + + volumePointB2.x = volumePointB2.x + baseDencrement; + volumePointB3.x = volumePointB3.x - baseDencrement; + + + /* Everything must fit within the visible area */ + + volumePointA1 = viewPort.fitIntoVisibleArea(volumePointA1); + volumePointA2 = viewPort.fitIntoVisibleArea(volumePointA2); + volumePointA3 = viewPort.fitIntoVisibleArea(volumePointA3); + volumePointA4 = viewPort.fitIntoVisibleArea(volumePointA4); + + volumePointB1 = viewPort.fitIntoVisibleArea(volumePointB1); + volumePointB2 = viewPort.fitIntoVisibleArea(volumePointB2); + volumePointB3 = viewPort.fitIntoVisibleArea(volumePointB3); + volumePointB4 = viewPort.fitIntoVisibleArea(volumePointB4); + + /* Everything must fit within the Frame. We know that that is equivalent to say that each bar con not be higher than the base of the opposite . */ + + if (volumePointA2.y < volumePointB1.y) { + + volumePointA2.y = volumePointB1.y; + volumePointA3.y = volumePointB1.y; + + } + + if (volumePointB2.y > volumePointA1.y) { + + volumePointB2.y = volumePointA1.y; + volumePointB3.y = volumePointA1.y; + + } + + /* Now the drawing of the volume bars*/ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(volumePointA1.x, volumePointA1.y); + browserCanvasContext.lineTo(volumePointA2.x, volumePointA2.y); + browserCanvasContext.lineTo(volumePointA3.x, volumePointA3.y); + browserCanvasContext.lineTo(volumePointA4.x, volumePointA4.y); + + browserCanvasContext.closePath(); + + + if (datetime !== undefined) { + + let dateValue = datetime.valueOf(); + + if (dateValue >= volume.begin && dateValue <= volume.end) { + + browserCanvasContext.fillStyle = 'rgba(255, 233, 31, 0.40)'; // Current bar accroding to time + + } else { + + browserCanvasContext.fillStyle = 'rgba(64, 217, 26, 0.40)'; + } + + } else { + + browserCanvasContext.fillStyle = 'rgba(64, 217, 26, 0.40)'; + + } + + browserCanvasContext.fill(); + browserCanvasContext.strokeStyle = 'rgba(27, 105, 7, 0.40)'; + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(volumePointB1.x, volumePointB1.y); + browserCanvasContext.lineTo(volumePointB2.x, volumePointB2.y); + browserCanvasContext.lineTo(volumePointB3.x, volumePointB3.y); + browserCanvasContext.lineTo(volumePointB4.x, volumePointB4.y); + + browserCanvasContext.closePath(); + + if (datetime !== undefined) { + + let dateValue = datetime.valueOf(); + + if (dateValue >= volume.begin && dateValue <= volume.end) { + + browserCanvasContext.fillStyle = 'rgba(255, 233, 31, 0.40)'; // Current candle accroding to time + + } else { + + browserCanvasContext.fillStyle = 'rgba(219, 18, 18, 0.40)'; + } + + } else { + + browserCanvasContext.fillStyle = 'rgba(219, 18, 18, 0.40)'; + + } + + browserCanvasContext.strokeStyle = 'rgba(130, 9, 9, 0.40)'; + + browserCanvasContext.fill(); + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + + + + if (datetime !== undefined) { + + let dateValue = datetime.valueOf(); + + if (dateValue >= volume.begin && dateValue <= volume.end) { + + let buyInfo = { + baseWidth: volumePointA4.x - volumePointA1.x, + topWidth: volumePointA3.x - volumePointA2.x, + height: volumePointA2.y - volumePointA1.y + }; + + let sellInfo = { + baseWidth: volumePointB4.x - volumePointB1.x, + topWidth: volumePointB3.x - volumePointB2.x, + height: volumePointB2.y - volumePointB1.y + }; + + let currentVolume = { + buyInfo: buyInfo, + sellInfo: sellInfo, + period: timePeriod, + innerVolumeBar: volume + }; + + thisObject.container.eventHandler.raiseEvent("Current Volume Info Changed", currentVolume); + + } + } + } + } + } + } + + function onZoomChanged(event) { + + recalculateScaleX(); + recalculate(); + recalculateScaleY(); + + } + + function onDragFinished() { + + recalculateScaleX(); + recalculate(); + recalculateScaleY(); + + } +} + diff --git a/History.js b/History.js new file mode 100644 index 000000000..aaf61d035 --- /dev/null +++ b/History.js @@ -0,0 +1,926 @@ +function newAAMastersPlottersTradingHistory() { + + const MODULE_NAME = "AAMasters Plotters Trading History"; + const INTENSIVE_LOG = false; + const ERROR_LOG = true; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + getContainer: getContainer, + setTimePeriod: setTimePeriod, + setDatetime: setDatetime, + recalculateScale: recalculateScale, + draw: draw, + payload: { + profile: { + position: { + x: 0, + y: 0 + }, + visible: false + }, + notes: [] + } + }; + + /* this is part of the module template */ + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + let timeLineCoordinateSystem = newTimeLineCoordinateSystem(); // Needed to be able to plot on the timeline, otherwise not. + + let timePeriod; // This will hold the current Time Period the user is at. + let datetime; // This will hold the current Datetime the user is at. + + /* these are module specific variables: */ + + let fileSequence; + let plotElements = []; // This is where the elements to be plotted are stored before plotting. + let plotLines = []; // Here we store the lines of open positions. + let notes = []; // Here we store the notes with messages from the bot. + + let notesChangedEventRaised = true; // This controls when to raise the event that notes changed. + + let previousNotesSetKey; + let currentNotesSetKey; + + let offsetChangedEventSubscriptionId + let filesUpdatedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + + viewPort.eventHandler.stopListening(offsetChangedEventSubscriptionId); + fileSequence.eventHandler.stopListening(filesUpdatedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + + /* Destroyd References */ + + datetime = undefined; + timePeriod = undefined; + + fileSequence = undefined; + plotElements = undefined; + plotLines = undefined; + notes = undefined; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err.message = " + err.message); } + } + } + + function initialize(pStorage, pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) { + + try { + + datetime = pDatetime; + timePeriod = pTimePeriod; + + fileSequence = pStorage.fileSequences[0]; + + recalculate(callBackFunction); + recalculateScale(callBackFunction); + + filesUpdatedEventSubscriptionId = fileSequence.eventHandler.listenToEvent("Files Updated", onFilesUpdated);// Only the first sequence is supported right now. + offsetChangedEventSubscriptionId = viewPort.eventHandler.listenToEvent("Offset Changed", onOffsetChanged); + + immensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(callBackFunction); + recalculateScale(callBackFunction); + }) + + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE); + } + } + + function getContainer(point) { + + try { + + let container; + + /* First we check if this point is inside this space. */ + + if (thisObject.container.frame.isThisPointHere(point) === true) { + + return thisObject.container; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function onFilesUpdated() { + + recalculate(); + + } + + function setTimePeriod(pTimePeriod) { + + try { + + timePeriod = pTimePeriod; + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> err = " + err.stack); } + } + } + + function setDatetime(newDatetime) { + + try { + + datetime = newDatetime; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> err = " + err.stack); } + } + } + + function onOffsetChanged() { + + if (Math.random() * 100 > 95) { + + recalculate() + }; + + } + + function recalculate(callBackFunction) { + + try { + + if (fileSequence === undefined) { return; } + + /* + + We are going to filter the records depending on the Time Period. We want that for a 1 min time peroid all the records appears on screen, + but for higher periods, we will filter out some records, so that they do not overlap ever. + + */ + + plotElements = []; + plotLines = []; + notes = []; + + let lastSellRate; + let lastSellDate; + let sellExecRate; + + let lastBuyRate; + let lastBuyDate; + let buyExecRate; + + let maxSequence = fileSequence.getFilesLoaded(); + + for (let j = 0; j < maxSequence; j++) { + + let file = fileSequence.getFile(j); + + let history = []; + let lines = []; + + /* First the small balls */ + + const ONE_MIN_IN_MILISECONDS = 60 * 1000; + let step = timePeriod / ONE_MIN_IN_MILISECONDS; + + let i = 0; + let lastRecordPushed = 0; + + for (i = 0; i < file.length; i = i + step) { + + let newHistoryRecord = { + + date: Math.trunc(file[i][0] / 60000) * 60000 + 30000, + buyAvgRate: file[i][1], + sellAvgRate: file[i][2], + + lastSellRate: file[i][3], + sellExecRate: file[i][4], + lastBuyRate: file[i][5], + buyExecRate: file[i][6], + + marketRate: file[i][7], + newPositions: file[i][8], + newTrades: file[i][9], + movedPositions: file[i][10], + profitsAssetA: file[i][11], + profitsAssetB: file[i][12], + combinedProfitsA: file[i][13], + combinedProfitsB: file[i][14], + + messageRelevance: file[i][15], + messageTitle: file[i][16], + messageBody: file[i][17] + }; + + history.push(newHistoryRecord); + lastRecordPushed = i; + + /* Here we build the lines. */ + + if (timePeriod <= 1 * 60 * 1000) { + + if (newHistoryRecord.lastSellRate > 0) { + + lastSellRate = newHistoryRecord.lastSellRate; + lastSellDate = newHistoryRecord.date; + + } + + if (newHistoryRecord.sellExecRate > 0) { + + let newLine = { + type: "sell", + x1: lastSellDate, + y1: lastSellRate, + x2: newHistoryRecord.date, + y2: newHistoryRecord.sellExecRate + }; + + lines.push(newLine); + + } + + if (newHistoryRecord.lastBuyRate > 0) { + + lastBuyRate = newHistoryRecord.lastBuyRate; + lastBuyDate = newHistoryRecord.date; + + } + + if (newHistoryRecord.buyExecRate > 0) { + + let newLine = { + type: "buy", + x1: lastBuyDate, + y1: lastBuyRate, + x2: newHistoryRecord.date, + y2: newHistoryRecord.buyExecRate + }; + + lines.push(newLine); + + } + } + + } + + /* We allways want to put the last record of the file on the filterd dataset, so as to allways show the latest advance of the bot. */ + + i = file.length - 1; + + if (lastRecordPushed !== i) { + + let newHistoryRecord = { + + date: Math.trunc(file[i][0] / 60000) * 60000 + 30000, + buyAvgRate: file[i][1], + sellAvgRate: file[i][2], + + lastSellRate: file[i][3], + sellExecRate: file[i][4], + lastBuyRate: file[i][5], + buyExecRate: file[i][6], + + marketRate: file[i][7], + newPositions: file[i][8], + newTrades: file[i][9], + movedPositions: file[i][10], + profitsAssetA: file[i][11], + profitsAssetB: file[i][12], + combinedProfitsA: file[i][13], + combinedProfitsB: file[i][14], + + messageRelevance: file[i][15], + messageTitle: file[i][16], + messageBody: file[i][17] + }; + + history.push(newHistoryRecord); + + } + + + /* Second we process the text */ + + for (let i = 0; i < file.length; i++) { + + let newHistoryRecord = { + + date: Math.trunc(file[i][0] / 60000) * 60000 + 30000, + buyAvgRate: file[i][1], + sellAvgRate: file[i][2], + + lastSellRate: file[i][3], + sellExecRate: file[i][4], + lastBuyRate: file[i][5], + buyExecRate: file[i][6], + + marketRate: file[i][7], + newPositions: file[i][8], + newTrades: file[i][9], + movedPositions: file[i][10], + profitsAssetA: file[i][11], + profitsAssetB: file[i][12], + combinedProfitsA: file[i][13], + combinedProfitsB: file[i][14], + + messageRelevance: file[i][15], + messageTitle: file[i][16], + messageBody: file[i][17] + }; + + if (newHistoryRecord.messageTitle !== "" && newHistoryRecord.messageBody !== "") { + + if (newHistoryRecord.messageRelevance >= 0 && newHistoryRecord.messageRelevance <= 10) { + + let relevanceTimePeriod = (dailyFilePeriods[10 - newHistoryRecord.messageRelevance][0]); + + if (timePeriod <= relevanceTimePeriod) { + + let note = { + title: newHistoryRecord.messageTitle, + body: newHistoryRecord.messageBody, + date: newHistoryRecord.date, + rate: newHistoryRecord.marketRate, + position: { + x: 0, + y: 0 + }, + visible: false + }; + + notes.push(note); + } + } + } + } + + plotElements.push(history); + plotLines.push(lines); + + } + + notesChangedEventRaised = false; + + thisObject.container.eventHandler.raiseEvent("History Changed", history); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE); + } + } + + function recalculateScale(callBackFunction) { + + try { + + if (fileSequence === undefined) { return; } // We need the market file to be loaded to make the calculation. + + if (timeLineCoordinateSystem.maxValue > 0) { return; } // Already calculated. + + let minValue = { + x: MIN_PLOTABLE_DATE.valueOf(), + y: 0 + }; + + let maxValue = { + x: MAX_PLOTABLE_DATE.valueOf(), + y: nextPorwerOf10(USDT_BTC_HTH) / 4 // TODO: This 4 is temporary + }; + + + timeLineCoordinateSystem.initialize( + minValue, + maxValue, + thisObject.container.frame.width, + thisObject.container.frame.height + ); + + function getMaxRate() { + + try { + + let maxValue = 0; + + let maxSequence = fileSequence.getFilesLoaded(); + + for (let j = 0; j < maxSequence; j++) { + + let file = fileSequence.getFile(j); + + for (let i = 0; i < file.length; i++) { + + let currentMax = file[i][1] + file[i][2]; // 1 = rates. + + if (maxValue < currentMax) { + maxValue = currentMax; + } + } + } + + return maxValue; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScale -> getMaxRate -> err = " + err.stack); } + callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE); + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScale -> err = " + err.stack); } + callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE); + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function plotChart() { + + try { + + let userPosition = getUserPosition() + let userPositionDate = userPosition.point.x + + let point = { + x: 0, + y: 0 + }; + + let history; + + for (let j = 0; j < plotElements.length; j++) { + + let history = plotElements[j]; + + for (let i = 0; i < history.length; i++) { + + record = history[i]; + + let timestamp = Math.trunc(record.date / timePeriod) * timePeriod + timePeriod / 2; + + point = { + x: timestamp, + y: record.marketRate + }; + + point = timeLineCoordinateSystem.transformThisPoint(point); + point = transformThisPoint(point, thisObject.container); + + if (point.x < viewPort.visibleArea.bottomLeft.x || point.x > viewPort.visibleArea.bottomRight.x) { continue; } + + point = viewPort.fitIntoVisibleArea(point); + + let isCurrentRecord = false; + + if (userPositionDate >= record.date - timePeriod / 2 && userPositionDate <= record.date + timePeriod / 2 - 1) { + isCurrentRecord = true; + } + + let radius = 3; + + let opacity = '0.2'; + + browserCanvasContext.lineWidth = 1; + + /* Outer Circle */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')'; + + if (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.LIGHT + ', ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.arc(point.x, point.y, radius, 0, Math.PI * 2, true); + browserCanvasContext.closePath(); + + if (point.x < viewPort.visibleArea.topLeft.x + 50 || point.x > viewPort.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else { + browserCanvasContext.fill(); + } + + browserCanvasContext.stroke(); + + /* Draw a red inverted triangle on exec sell */ + + if (record.sellExecRate > 0) { + + opacity = '0.5'; + + let point1 = { + x: record.date, + y: record.sellExecRate + }; + + let point2 = { + x: record.date + timePeriod / 7 * 2, + y: record.sellExecRate + }; + + let point3 = { + x: record.date - timePeriod / 7 * 2, + y: record.sellExecRate + }; + + point1 = timeLineCoordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = viewPort.fitIntoVisibleArea(point1); + + point2 = timeLineCoordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = viewPort.fitIntoVisibleArea(point2); + + point3 = timeLineCoordinateSystem.transformThisPoint(point3); + point3 = transformThisPoint(point3, thisObject.container); + point3 = viewPort.fitIntoVisibleArea(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y - diff; + point3.y = point3.y - diff; + + point2 = viewPort.fitIntoVisibleArea(point2); + point3 = viewPort.fitIntoVisibleArea(point3); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(point1.x, point1.y); + browserCanvasContext.lineTo(point2.x, point2.y); + browserCanvasContext.lineTo(point3.x, point3.y); + browserCanvasContext.lineTo(point1.x, point1.y); + + browserCanvasContext.closePath(); + + if (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(219, 18, 18, ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(130, 9, 9, ' + opacity + ')'; + browserCanvasContext.stroke(); + + } + + /* Draw a green triangle on exec buy */ + + if (record.buyExecRate > 0) { + + opacity = '0.5'; + + let point1 = { + x: record.date, + y: record.buyExecRate + }; + + let point2 = { + x: record.date + timePeriod / 7 * 2, + y: record.buyExecRate + }; + + let point3 = { + x: record.date - timePeriod / 7 * 2, + y: record.buyExecRate + }; + + point1 = timeLineCoordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = viewPort.fitIntoVisibleArea(point1); + + point2 = timeLineCoordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = viewPort.fitIntoVisibleArea(point2); + + point3 = timeLineCoordinateSystem.transformThisPoint(point3); + point3 = transformThisPoint(point3, thisObject.container); + point3 = viewPort.fitIntoVisibleArea(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y + diff; + point3.y = point3.y + diff; + + point2 = viewPort.fitIntoVisibleArea(point2); + point3 = viewPort.fitIntoVisibleArea(point3); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(point1.x, point1.y); + browserCanvasContext.lineTo(point2.x, point2.y); + browserCanvasContext.lineTo(point3.x, point3.y); + browserCanvasContext.lineTo(point1.x, point1.y); + + browserCanvasContext.closePath(); + + if (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(64, 217, 26, ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(27, 105, 7, ' + opacity + ')'; + browserCanvasContext.stroke(); + + } + + /* Draw a red inverted triangle on sell */ + + if (record.lastSellRate > 0) { + + opacity = '0.5'; + + let point1 = { + x: record.date, + y: record.lastSellRate + }; + + let point2 = { + x: record.date + timePeriod / 7 * 2, + y: record.lastSellRate + }; + + let point3 = { + x: record.date - timePeriod / 7 * 2, + y: record.lastSellRate + }; + + point1 = timeLineCoordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = viewPort.fitIntoVisibleArea(point1); + + point2 = timeLineCoordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = viewPort.fitIntoVisibleArea(point2); + + point3 = timeLineCoordinateSystem.transformThisPoint(point3); + point3 = transformThisPoint(point3, thisObject.container); + point3 = viewPort.fitIntoVisibleArea(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y - diff; + point3.y = point3.y - diff; + + point2 = viewPort.fitIntoVisibleArea(point2); + point3 = viewPort.fitIntoVisibleArea(point3); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(point1.x, point1.y); + browserCanvasContext.lineTo(point2.x, point2.y); + browserCanvasContext.lineTo(point3.x, point3.y); + browserCanvasContext.lineTo(point1.x, point1.y); + + browserCanvasContext.closePath(); + + if (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(255, 255, 255, ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(130, 9, 9, ' + opacity + ')'; + browserCanvasContext.stroke(); + + } + + /* Draw a green triangle on buy */ + + if (record.lastBuyRate > 0) { + + opacity = '0.5'; + + let point1 = { + x: record.date, + y: record.lastBuyRate + }; + + let point2 = { + x: record.date + timePeriod / 7 * 2, + y: record.lastBuyRate + }; + + let point3 = { + x: record.date - timePeriod / 7 * 2, + y: record.lastBuyRate + }; + + point1 = timeLineCoordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = viewPort.fitIntoVisibleArea(point1); + + point2 = timeLineCoordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = viewPort.fitIntoVisibleArea(point2); + + point3 = timeLineCoordinateSystem.transformThisPoint(point3); + point3 = transformThisPoint(point3, thisObject.container); + point3 = viewPort.fitIntoVisibleArea(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y + diff; + point3.y = point3.y + diff; + + point2 = viewPort.fitIntoVisibleArea(point2); + point3 = viewPort.fitIntoVisibleArea(point3); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(point1.x, point1.y); + browserCanvasContext.lineTo(point2.x, point2.y); + browserCanvasContext.lineTo(point3.x, point3.y); + browserCanvasContext.lineTo(point1.x, point1.y); + + browserCanvasContext.closePath(); + + if (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(255, 255, 255, ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(27, 105, 7, ' + opacity + ')'; + browserCanvasContext.stroke(); + + } + + + /* Since there is at least some point plotted, then the profile should be visible. */ + + thisObject.payload.profile.visible = true; + } + + /* Draw the lines connecting plot elements. */ + + let lines = plotLines[j]; + + for (let i = 0; i < lines.length; i++) { + + let line = lines[i]; + + opacity = '0.2'; + + let point1 = { + x: line.x1, + y: line.y1 + }; + + let point2 = { + x: line.x2, + y: line.y2 + }; + + point1 = timeLineCoordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = viewPort.fitIntoVisibleArea(point1); + + point2 = timeLineCoordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = viewPort.fitIntoVisibleArea(point2); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(point1.x, point1.y); + browserCanvasContext.lineTo(point2.x, point2.y); + + browserCanvasContext.closePath(); + + if (line.type === "sell") { + + browserCanvasContext.strokeStyle = 'rgba(130, 9, 9, ' + opacity + ')'; + + } else { + + browserCanvasContext.strokeStyle = 'rgba(27, 105, 7, ' + opacity + ')'; + + } + + browserCanvasContext.stroke(); + + } + + /* Now we calculate the anchor position of notes. */ + + currentNotesSetKey = ""; + + for (let i = 0; i < notes.length; i++) { + + let note = notes[i]; + + opacity = '0.2'; + + note.position = { + x: note.date, + y: note.rate + }; + + note.position = timeLineCoordinateSystem.transformThisPoint(note.position); + note.position = transformThisPoint(note.position, thisObject.container); + + if (note.position.x < (viewPort.visibleArea.bottomRight.x / 2) * (-1) || note.position.x > (viewPort.visibleArea.bottomRight.x) * (1.5)) { + note.visible = false; + currentNotesSetKey = currentNotesSetKey + "0"; + } else { + note.visible = true; + currentNotesSetKey = currentNotesSetKey + "1"; + } + } + + if (notesChangedEventRaised === false) { + + /* In this case the event is raised because we might have a different set of notes. */ + + thisObject.container.eventHandler.raiseEvent("Notes Changed", notes); + thisObject.payload.notes = notes; + + notesChangedEventRaised = true; + + } else { + + if (currentNotesSetKey !== previousNotesSetKey) { + + /* In this case the event is raised because the visibility of some of the notes changed. */ + + thisObject.container.eventHandler.raiseEvent("Notes Changed", notes); + thisObject.payload.notes = notes; + + previousNotesSetKey = currentNotesSetKey; + } + } + + } + + /* + + We replace the coordinate of the profile point so that whoever has a reference to it, gets the new position. + We will use the last point plotted on screen as the profilePoint. + + */ + + thisObject.payload.profile.position.x = point.x; + thisObject.payload.profile.position.y = point.y; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } +} + diff --git a/History.png b/History.png new file mode 100644 index 000000000..a6809adfd Binary files /dev/null and b/History.png differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..10e295746 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/Live.png b/Live.png new file mode 100644 index 000000000..71f565286 Binary files /dev/null and b/Live.png differ diff --git a/Plotters-Trading.sln b/Plotters-Trading.sln new file mode 100644 index 000000000..661fcc076 --- /dev/null +++ b/Plotters-Trading.sln @@ -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}") = "Project", "Project.njsproj", "{096B35AD-F86F-47B9-A99F-04CA93397DA8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {096B35AD-F86F-47B9-A99F-04CA93397DA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {096B35AD-F86F-47B9-A99F-04CA93397DA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {096B35AD-F86F-47B9-A99F-04CA93397DA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {096B35AD-F86F-47B9-A99F-04CA93397DA8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E75EDC9D-66A7-4C54-8EA1-5ADC148CACA8} + EndGlobalSection +EndGlobal diff --git a/Project.njsproj b/Project.njsproj new file mode 100644 index 000000000..d12ab345e --- /dev/null +++ b/Project.njsproj @@ -0,0 +1,70 @@ + + + + Debug + 2.0 + {096b35ad-f86f-47b9-a99f-04ca93397da8} + + ShowAllFiles + History.js + . + . + {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + Project + + + + + + + Code + + + + + + + + + + + + + + False + True + 0 + / + http://localhost:48022/ + False + True + http://localhost:1337 + False + + + + + + + CurrentPage + True + False + False + False + + + + + + + + + False + False + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..1fec2f25c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# AAMariam-Plotter diff --git a/this.plotter.config.json b/this.plotter.config.json new file mode 100644 index 000000000..73cd0f511 --- /dev/null +++ b/this.plotter.config.json @@ -0,0 +1,20 @@ +{ + "displayName": "Plotters Trading", + "codeName": "PlottersTrading", + "devTeam": "AAMasters", + "modules": [ + { + "codeName": "Details", + "moduleName": "Details", + "description": "Plots trading decitions in an easy to understand way.", + "panels": [] + }, + { + "codeName": "History", + "moduleName": "History", + "description": "Plots trading history.", + "profilePicture": "History.png", + "panels": [] + } + ] +} \ No newline at end of file