Unfortunately nothing ..
Something is going, the previous versions are now not giving out any data out either ...
Sorry, had the debug node disconnected, here is what we're getting now ... no "final message" and still all variables
Here is the object - you can see all of the original fields, and ACTUALLY (just noticed) none of the new fields defined in the function, "pair" is not getting truncated, etc. None of the function transformations are taking place.
Ok. Try this one. I eliminated our first two messages since we know the original message and the filter are working. Instead of sending each object prior to processing, now the tempObj will be sent instead after processing. Not sure where the break would be at this point, but this will help narrow it down.
//var check = context.get('recordKeeper');
check = {};
const input = msg.payload.filter(s => s.status.type == "stop_loss_finished" || s.status.type == "finished");
var tempObj;
for(let obj of input){
if(!(check.hasOwnProperty(obj.id))){
tempObj = {};
tempObj.pair = (String(obj.pair)).replace("USDT_", "");
tempObj.trade_id = obj.id;
tempObj.profit_usd = Number(obj.profit.usd).toFixed(2);
tempObj.profit_percent = obj.profit.percent;
tempObj.position = Number(obj.position.total.value).toFixed(0);
tempObj.volume = obj.data.current_price.quote_volume;
tempObj.close_price = obj.data.average_close_price;
tempObj.closed = obj.data.closed_at;
if (obj.status.type === "stop_loss_finished"){tempObj.status = "SL";}
else if (obj.status.type === "finished"){tempObj.status = "TP";}
else if (obj.status.type === "panic_sold"){tempObj.status = "➦";}
else {tempObj.status = obj.status.type}
if (obj.stop_loss.enabled) {
tempObj.to_sl = ((1 - obj.stop_loss.conditional.price.value / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "stop_loss_finished") {
tempObj.S_L = (((obj.data.average_enter_price - obj.stop_loss.conditional.price.value) / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "finished") {
tempObj.T_P = (((obj.take_profit.steps[0].price.value - obj.data.average_enter_price) / obj.data.average_enter_price) * 100).toFixed(2);
}
tempObj.in = obj.note.split("_");
tempObj.frames = obj.in[0];
tempObj.gain = obj.in[1];
tempObj.gain_st = obj.in[2];
tempObj.freq = obj.in[3];
tempObj.analysis = obj.in[4];
tempObj.tp_a = obj.in[5];
tempObj.tp_trl_a = obj.in[6];
tempObj.sl_a = obj.in[7];
tempObj.sl_trl_a = obj.in[8];
tempObj.tm_a = obj.in[9];
node.send({bundle:"Object", tempObj});
check[obj.id] = tempObj;
}
}
//context.set('recordKeeper', check);
msg = {};
msg.payload = check;
msg.bundle = "Final Message";
return msg;
Sorry, back to nothing I checked some the earlier "fully working" tests, and none of them had the function variable definitions
Ok. Let's step back a little and make a basic function again. Let's see if this works.
//var check = context.get('recordKeeper');
check = {};
const input = msg.payload.filter(s => s.status.type == "stop_loss_finished" || s.status.type == "finished");
var tempObj;
for(let obj of input){
if(!(check.hasOwnProperty(obj.id))){
tempObj = {};
tempObj.pair = (String(obj.pair)).replace("USDT_", "");
/*tempObj.trade_id = obj.id;
tempObj.profit_usd = Number(obj.profit.usd).toFixed(2);
tempObj.profit_percent = obj.profit.percent;
tempObj.position = Number(obj.position.total.value).toFixed(0);
tempObj.volume = obj.data.current_price.quote_volume;
tempObj.close_price = obj.data.average_close_price;
tempObj.closed = obj.data.closed_at;
if (obj.status.type === "stop_loss_finished"){tempObj.status = "SL";}
else if (obj.status.type === "finished"){tempObj.status = "TP";}
else if (obj.status.type === "panic_sold"){tempObj.status = "➦";}
else {tempObj.status = obj.status.type}
if (obj.stop_loss.enabled) {
tempObj.to_sl = ((1 - obj.stop_loss.conditional.price.value / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "stop_loss_finished") {
tempObj.S_L = (((obj.data.average_enter_price - obj.stop_loss.conditional.price.value) / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "finished") {
tempObj.T_P = (((obj.take_profit.steps[0].price.value - obj.data.average_enter_price) / obj.data.average_enter_price) * 100).toFixed(2);
}
tempObj.in = obj.note.split("_");
tempObj.frames = obj.in[0];
tempObj.gain = obj.in[1];
tempObj.gain_st = obj.in[2];
tempObj.freq = obj.in[3];
tempObj.analysis = obj.in[4];
tempObj.tp_a = obj.in[5];
tempObj.tp_trl_a = obj.in[6];
tempObj.sl_a = obj.in[7];
tempObj.sl_trl_a = obj.in[8];
tempObj.tm_a = obj.in[9];*/
node.send({bundle:"Object", tempObj});
check[obj.id] = tempObj;
}
}
//context.set('recordKeeper', check);
msg = {};
msg.payload = check;
msg.bundle = "Final Message";
return msg;
Yes - pair properly truncated
How about now?
//var check = context.get('recordKeeper');
check = {};
const input = msg.payload.filter(s => s.status.type == "stop_loss_finished" || s.status.type == "finished");
var tempObj;
for(let obj of input){
if(!(check.hasOwnProperty(obj.id))){
tempObj = {};
tempObj.pair = (String(obj.pair)).replace("USDT_", "");
tempObj.trade_id = obj.id;
tempObj.profit_usd = Number(obj.profit.usd).toFixed(2);
tempObj.profit_percent = obj.profit.percent;
tempObj.position = Number(obj.position.total.value).toFixed(0);
tempObj.volume = obj.data.current_price.quote_volume;
tempObj.close_price = obj.data.average_close_price;
tempObj.closed = obj.data.closed_at;
/*if (obj.status.type === "stop_loss_finished"){tempObj.status = "SL";}
else if (obj.status.type === "finished"){tempObj.status = "TP";}
else if (obj.status.type === "panic_sold"){tempObj.status = "➦";}
else {tempObj.status = obj.status.type}
if (obj.stop_loss.enabled) {
tempObj.to_sl = ((1 - obj.stop_loss.conditional.price.value / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "stop_loss_finished") {
tempObj.S_L = (((obj.data.average_enter_price - obj.stop_loss.conditional.price.value) / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "finished") {
tempObj.T_P = (((obj.take_profit.steps[0].price.value - obj.data.average_enter_price) / obj.data.average_enter_price) * 100).toFixed(2);
}
tempObj.in = obj.note.split("_");
tempObj.frames = obj.in[0];
tempObj.gain = obj.in[1];
tempObj.gain_st = obj.in[2];
tempObj.freq = obj.in[3];
tempObj.analysis = obj.in[4];
tempObj.tp_a = obj.in[5];
tempObj.tp_trl_a = obj.in[6];
tempObj.sl_a = obj.in[7];
tempObj.sl_trl_a = obj.in[8];
tempObj.tm_a = obj.in[9];*/
node.send({bundle:"Object", tempObj});
check[obj.id] = tempObj;
}
}
//context.set('recordKeeper', check);
msg = {};
msg.payload = check;
msg.bundle = "Final Message";
return msg;
YUP - with more variables
Good. That's progress. Let's move the comment marker and give ourselves our first if block.
//var check = context.get('recordKeeper');
check = {};
const input = msg.payload.filter(s => s.status.type == "stop_loss_finished" || s.status.type == "finished");
var tempObj;
for(let obj of input){
if(!(check.hasOwnProperty(obj.id))){
tempObj = {};
tempObj.pair = (String(obj.pair)).replace("USDT_", "");
tempObj.trade_id = obj.id;
tempObj.profit_usd = Number(obj.profit.usd).toFixed(2);
tempObj.profit_percent = obj.profit.percent;
tempObj.position = Number(obj.position.total.value).toFixed(0);
tempObj.volume = obj.data.current_price.quote_volume;
tempObj.close_price = obj.data.average_close_price;
tempObj.closed = obj.data.closed_at;
if (obj.status.type === "stop_loss_finished"){tempObj.status = "SL";}
else if (obj.status.type === "finished"){tempObj.status = "TP";}
else if (obj.status.type === "panic_sold"){tempObj.status = "➦";}
else {tempObj.status = obj.status.type}
/*if (obj.stop_loss.enabled) {
tempObj.to_sl = ((1 - obj.stop_loss.conditional.price.value / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "stop_loss_finished") {
tempObj.S_L = (((obj.data.average_enter_price - obj.stop_loss.conditional.price.value) / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "finished") {
tempObj.T_P = (((obj.take_profit.steps[0].price.value - obj.data.average_enter_price) / obj.data.average_enter_price) * 100).toFixed(2);
}
tempObj.in = obj.note.split("_");
tempObj.frames = obj.in[0];
tempObj.gain = obj.in[1];
tempObj.gain_st = obj.in[2];
tempObj.freq = obj.in[3];
tempObj.analysis = obj.in[4];
tempObj.tp_a = obj.in[5];
tempObj.tp_trl_a = obj.in[6];
tempObj.sl_a = obj.in[7];
tempObj.sl_trl_a = obj.in[8];
tempObj.tm_a = obj.in[9];*/
node.send({bundle:"Object", tempObj});
check[obj.id] = tempObj;
}
}
//context.set('recordKeeper', check);
msg = {};
msg.payload = check;
msg.bundle = "Final Message";
return msg;
Ok. Now let's take a chance and enable everything but the obj.in assignments.
//var check = context.get('recordKeeper');
check = {};
const input = msg.payload.filter(s => s.status.type == "stop_loss_finished" || s.status.type == "finished");
var tempObj;
for(let obj of input){
if(!(check.hasOwnProperty(obj.id))){
tempObj = {};
tempObj.pair = (String(obj.pair)).replace("USDT_", "");
tempObj.trade_id = obj.id;
tempObj.profit_usd = Number(obj.profit.usd).toFixed(2);
tempObj.profit_percent = obj.profit.percent;
tempObj.position = Number(obj.position.total.value).toFixed(0);
tempObj.volume = obj.data.current_price.quote_volume;
tempObj.close_price = obj.data.average_close_price;
tempObj.closed = obj.data.closed_at;
if (obj.status.type === "stop_loss_finished"){tempObj.status = "SL";}
else if (obj.status.type === "finished"){tempObj.status = "TP";}
else if (obj.status.type === "panic_sold"){tempObj.status = "➦";}
else {tempObj.status = obj.status.type}
if (obj.stop_loss.enabled) {
tempObj.to_sl = ((1 - obj.stop_loss.conditional.price.value / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "stop_loss_finished") {
tempObj.S_L = (((obj.data.average_enter_price - obj.stop_loss.conditional.price.value) / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "finished") {
tempObj.T_P = (((obj.take_profit.steps[0].price.value - obj.data.average_enter_price) / obj.data.average_enter_price) * 100).toFixed(2);
}
tempObj.in = obj.note.split("_");
/*tempObj.frames = obj.in[0];
tempObj.gain = obj.in[1];
tempObj.gain_st = obj.in[2];
tempObj.freq = obj.in[3];
tempObj.analysis = obj.in[4];
tempObj.tp_a = obj.in[5];
tempObj.tp_trl_a = obj.in[6];
tempObj.sl_a = obj.in[7];
tempObj.sl_trl_a = obj.in[8];
tempObj.tm_a = obj.in[9];*/
node.send({bundle:"Object", tempObj});
check[obj.id] = tempObj;
}
}
//context.set('recordKeeper', check);
msg = {};
msg.payload = check;
msg.bundle = "Final Message";
return msg;
Looking good, although a couple of variables seem to be missing, for instance: obj.frames = obj.in[0]
Yes. You'll notice the comment marker currently excludes that block of code from being run.
Let's enable that block now and see what happens.
//var check = context.get('recordKeeper');
check = {};
const input = msg.payload.filter(s => s.status.type == "stop_loss_finished" || s.status.type == "finished");
var tempObj;
for(let obj of input){
if(!(check.hasOwnProperty(obj.id))){
tempObj = {};
tempObj.pair = (String(obj.pair)).replace("USDT_", "");
tempObj.trade_id = obj.id;
tempObj.profit_usd = Number(obj.profit.usd).toFixed(2);
tempObj.profit_percent = obj.profit.percent;
tempObj.position = Number(obj.position.total.value).toFixed(0);
tempObj.volume = obj.data.current_price.quote_volume;
tempObj.close_price = obj.data.average_close_price;
tempObj.closed = obj.data.closed_at;
if (obj.status.type === "stop_loss_finished"){tempObj.status = "SL";}
else if (obj.status.type === "finished"){tempObj.status = "TP";}
else if (obj.status.type === "panic_sold"){tempObj.status = "➦";}
else {tempObj.status = obj.status.type}
if (obj.stop_loss.enabled) {
tempObj.to_sl = ((1 - obj.stop_loss.conditional.price.value / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "stop_loss_finished") {
tempObj.S_L = (((obj.data.average_enter_price - obj.stop_loss.conditional.price.value) / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "finished") {
tempObj.T_P = (((obj.take_profit.steps[0].price.value - obj.data.average_enter_price) / obj.data.average_enter_price) * 100).toFixed(2);
}
tempObj.in = obj.note.split("_");
tempObj.frames = obj.in[0];
tempObj.gain = obj.in[1];
tempObj.gain_st = obj.in[2];
tempObj.freq = obj.in[3];
tempObj.analysis = obj.in[4];
tempObj.tp_a = obj.in[5];
tempObj.tp_trl_a = obj.in[6];
tempObj.sl_a = obj.in[7];
tempObj.sl_trl_a = obj.in[8];
tempObj.tm_a = obj.in[9];
node.send({bundle:"Object", tempObj});
check[obj.id] = tempObj;
}
}
//context.set('recordKeeper', check);
msg = {};
msg.payload = check;
msg.bundle = "Final Message";
return msg;
VERY STANGE .. uncommenting /*tempObj.frames = obj.in[0]; breaks the whole thing, BUT I missed the fact that frames was there (with it commented) ... you can see the [0] value: "--30m" ... how can that be!! ?? However, let's not spend your time on this detail (I'll try to track it down later). Lets try to save it!
I can see the excitement coming through...
Ok. Saving this will be a really quick change since we know everything but the obj.in section works. First, we'll need to make sure these two lines are in the Startup tab. They're a little different than normal. It's just an experiment in simplicity.
context.set('recordKeeper', {});
context.set('recordKeys', []);
Don't mind the 'recordKeys' variable yet. It will make turning this into a table style array much easier later. I'm putting it in now since we're messing with the Startup tab. If it has any issues with those two lines, just use these:
var recordKeeper = {};
var recordKeys = [];
context.set('recordKeeper', recordKeeper);
context.set('recordKeys', recordKeys);
Now with that setup, let's open up the functionality of recordKeeper and do something that may unbreak your array assignments that just broke the code.
var check = context.get('recordKeeper');
const input = msg.payload.filter(s => s.status.type == "stop_loss_finished" || s.status.type == "finished");
var tempObj;
var tempArr;
for(let obj of input){
if(!(check.hasOwnProperty(obj.id))){
tempObj = {};
tempObj.pair = (String(obj.pair)).replace("USDT_", "");
tempObj.trade_id = obj.id;
tempObj.profit_usd = Number(obj.profit.usd).toFixed(2);
tempObj.profit_percent = obj.profit.percent;
tempObj.position = Number(obj.position.total.value).toFixed(0);
tempObj.volume = obj.data.current_price.quote_volume;
tempObj.close_price = obj.data.average_close_price;
tempObj.closed = obj.data.closed_at;
if (obj.status.type === "stop_loss_finished"){tempObj.status = "SL";}
else if (obj.status.type === "finished"){tempObj.status = "TP";}
else if (obj.status.type === "panic_sold"){tempObj.status = "➦";}
else {tempObj.status = obj.status.type}
if (obj.stop_loss.enabled) {
tempObj.to_sl = ((1 - obj.stop_loss.conditional.price.value / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "stop_loss_finished") {
tempObj.S_L = (((obj.data.average_enter_price - obj.stop_loss.conditional.price.value) / obj.data.average_enter_price) * 100).toFixed(2);
}
if (obj.status.type == "finished") {
tempObj.T_P = (((obj.take_profit.steps[0].price.value - obj.data.average_enter_price) / obj.data.average_enter_price) * 100).toFixed(2);
}
tempObj.in = obj.note.split("_");
tempArr = obj["in"];
tempObj.frames = tempArr[0];
tempObj.gain = tempArr[1];
tempObj.gain_st = tempArr[2];
tempObj.freq = tempArr[3];
tempObj.analysis = tempArr[4];
tempObj.tp_a = tempArr[5];
tempObj.tp_trl_a = tempArr[6];
tempObj.sl_a = tempArr[7];
tempObj.sl_trl_a = tempArr[8];
tempObj.tm_a = tempArr[9];
node.send({bundle:"Object", tempObj});
check[obj.id] = tempObj;
}
}
context.set('recordKeeper', check);
msg = {};
msg.payload = check;
msg.bundle = "Final Message";
return msg;
I have a theory that .in might actually be a keyword in Javascript, though I can't find any evidence. So I'm assigning that group to a temporary variable using the label method to avoid that. It may not work, but it might also. We'll see.
I am afraid this didn't solve the frames issue (still broke) - if "in" was an issue I would have experienced some symptoms prior to this (?). So I took the version that worked and I am seeing it in context! Is context going to get changed to local file system save? Should it be the common file or a dedicated one? As you saw, there can be up to 50 transactions in 24 hours so it's going to add up
How is it going to get served to the table?
If you were trying to reference things in the same way, then yes. It was a precaution in case this was a unique situation. But yes, it can be skipped for now.
Yay! More progress!
So the simple part of this now is you can use file read and write nodes to save the context. At ~50 transactions in 24 hours, it will add up, but not very fast. Memory will serve you well for a few months with the trimming you're doing on what's coming in. What you'll do at this point for saving something to a file is decide what you want to trigger a file save with. Is it every time the context variable changes? Is it once a day? Any time the function is run? What you'll do is just create another output node on your function (function Setup tab) and connect that to a file write node. Then just send the context variable to that node and have the file node save it. That's the quickest way. Make sure overwrite is selected in the file node setup instead of append.
Reading the file in (for now) means sending a trigger to a file read node. Same filename as the write node. Send a message to the read node, probably with a .topic that specifies a file read and then do a context.set('recordKeeper', msg.payload) in your function to save it to memory. You can trigger it with a button or something else to make it read the file into the function. Just so long as it sends something you can mark the message with as a file read, you're good. Once I work out how Node-Red works with file saves in code, we can change to that instead, which will allow your function to read and write the file instead of other nodes.
This is where recordKeys comes in. Unfortunately I've run out of time right now to program that part (must begin my travels which take a long time). Once I can get back on again, I'll give you that part and you'll see how it gets sent out to the table.
Play with the file thing for right now and see if you can get it to save. The file itself will be saved in the .node-red folder under your username on your computer. You can open it up and see what's being saved. Which reminds me, you'll want to put a JSON node between the file read and your function. The file read will send a string out, which you don't want. You want a JSON object, which the JSON node creates by default. So file read trigger -> file read node -> JSON node -> function. Let me know if there are any issues.
Thank you! Safe travels.