Design a tempTable in class to hold a "matrix" of values

In another post I got to, by the great help of Martin, a solution with a TempTable to hold values.

In post https://dynamicsuser.net/ax/f/developers/96205/memory-not-cleaned-with-struct you can see the discussion

So I want to create a class of a TempTable and then GetSettermethods to get and set the values in the table.

If I’m not wrong I can do that by (assume I created a class of type TempTable named “Commodity”

public real parmOpen(int _id, real _open = 0)
{

//update value

if(_open != 0)

{

select forupdate Commodity where Commodity.id == _id;
commodity.open = _open;

commodity.update();

}

return commodity::find(_id).open;

}

Wrote it form the top of my head, but its about the concept.

What do you think?

Coding it I see it misses some points (like insert :wink:

Please use Insert > Insert Code to paste source it. It’ll preserve indentation, which will make your code much easier to read. If you want others’ help, you shouldn’t make it too complicated for them.

I’ve also updated a few other things in your code according the usual code style.

public real parmOpen(int _id, real _open = 0)
{
	Commodity commodity;

	//update value
	if (_open != 0)
	{
		select forupdate commodity where commodity.Id == _id;
		commodity.Open = _open;
		commodity.update();
	}

	return Commodity::find(_id).Open;
}

You said you created “a class of type TempTable named Commodity”, but that’s not true. Commodity is a table, not a class.

Note that I don’t know what you’re asking about. Also, you showed us a method but didn’t say in which class or table it’s defined. Such context is crucial.

I get a bit lost I have to confess.

I will try to explain the need in a very basic way, maybe we can find the right approach.

The need: I have an API which sends data of commodities to a form.

The form process this values and if some trigger is met it sends an order back to the API.

Hereunder a drawing of the concept.

I have a working version with 1 commodity. API is asked for one commodity and sends only back the data of this one commodity.

An example: API sends bidprice and askprice. Form puts these prices in a variable _bid and _ask.

So form is constantly putting these received prices in the variables. Note that this can happen many times per second and I’m only interested in the last value. Values don’t need to be saved for later use.

After receiving data form processes the logic and when f.e. _ask - _bid > 10 the form processes Send Order.

This in a nutshell. In real there are more values received and the process is more complicated, but the idea is the same.

As said this is working for 1 commodity.

Now I want to process more commodities with the same logic and form, so I need to track the values per commodity.

Every commodity has its own id.

So I thought, instead of the current variables, put them in a sort matrix, like an Excel sheet as you wish.

id bid ask

1 150.04 150.10

2 74.36 76.40

3 9.99 10.01

Now looking for the approach to get there. I hope the concept is clear, if not pls ask.

A programming interface communicating with a form is a very strange thing. I can’t imagine how it should work. Form are for humans, not for other applications. Can you please explain the role of the form in your design?

Also, can you tell us more about your API? An API is a very abstract term.

Can you confirm that you have no problem with losing all data received from the API in case of a failure (or when somebody simply closes the form)?

Aha, its clear that its not so logical as I thought and need to explain more, which i will gladly do:)

The form is used because it holds an AcitveX object which communicates with the API.

This is the only way I knew how to get it worked when I started with it a long time ago.

Besides that I can easily use some buttons and more to interact with the process.

I can imagine that there maybe are much better ways to process it through classes and not with a from, but the main thing it works like a charm for a long time. For 1 commodity that is :wink:

The API establish the connection between the Market data/Broker and the form through the ActiveX object. Through that object I can initialize, getdata, sendorder etc. All functions to work with the API are in there. And in the form I make use of methods to call and process all these functions.

And you’re right. When the values are lost it’s no problem at all. Strange maybe, but I don’t have to make it more complicated. And for the process it’s not neccesary to hold he historical values.

Pls take note that this is a single Axapta 3.0 installation, so there’s no need for multiuser, concurrency etc etc

So as you see my concern at this moment is to extend the whole thing from 1 commodity to n commodities, leaving the process and logic untouched.

Hope it’s more clear now, other wise pls ask.

I wouldn’t ever put such business logic to a form, but all right, let’s ignore it this time.
I would put the data to a temporary table, especially if you want to show it in the form and/or do some calculations. Tables are designed for this purpose.

Thanks :wink:

So TmpTable it is. That means that I have to shift from just populating and read variables, the methods change to “Select forupdate tmptable” and “table.insert()” etc.

Now I worked with TmpTables before (in the other business unit we use Axapta) and in that case I also used the TmpTable in a form (sorry :wink:

So knowledge is there how to handle it.

Now I’m questioning.

Should I use in this case the TmpTable as a Global object in the form, or is the better way to create a class which will create TmpTable and get/set all the data? I prefer a Class so doing it on “higher level” and on a smart way.

Pls advise

If you want to display the data in the form, use the table as the form data source.

If not, I would wrap the logic in a class, because it allows you to reuse the logic from other places, test the class etc.

Ok, its not necessary to display the perse. If I want to check a value I can do infolog.

I tried a bit via a Class but don’t get the right resuts yet.

Can I upload an xpo project here with a same form and class to check?

Or you want me to put the codelines here in the sourcecode?

I have no Axapta 3 environment to import the .xpo into, so it’s not very useful for me and .xpo is harder to read than the actual code.

Rather gave us your code as text, or even just pseudo-code demonstrating what you’re doing. Most importantly, don’t forget to explain what you’re trying to achieve by your code and what problem you have with it. Code alone isn’t very useful.

Ok, here a sample of the TmpTable, the Class and the Form.

This is just to concentrate on the question.

In real there’s alot other different stuff going on of course

Created a TempTable cdtyTable with fields

id (int), bid(real), ask(real)

Table has a find method:

static CdtyTable find(int  _Id,
                        boolean     update = false)
{
    CdtyTable  CdtyTable;
    ;

    CdtyTable.selectForUpdate(update);

    if (_Id)
    {
        select firstonly CdtyTable
           // index hint RecIdIdx
            where CdtyTable.Id == _Id;
    }

    return CdtyTable;
}

Created a Class cdtyClass with:

class CdtyClass
{

    CdtyTable   CdtyTable;

}
public real parmBid(int _id, real _bid = 0)
{

    ;
    if(_bid != 0)
        {
        select forupdate CdtyTable where CdtyTable.Id == _id;
        CdtyTable.Id = _id;
        CdtyTable.Bid = _bid;
            if(CdtyTable.RecId)  CdtyTable.update();
            else CdtyTable.insert();
        }

    return CdtyTable::find(_id).Bid;
}
public real parmAsk(int _id, real _ask = 0)
{

    ;
    if(_ask != 0)
        {
        select forupdate CdtyTable where CdtyTable.Id == _id;
        CdtyTable.Id = _id;
        CdtyTable.ask = _ask;
            if(CdtyTable.RecId)  CdtyTable.update();
            else CdtyTable.insert();
        }

    return CdtyTable::find(_id).ask;
}

Created a Form cdtyForm

public class FormRun extends ObjectRun
{
    CDTYClass   cdty;
}
public void init()
{
    super();
    cdty = new CDTYClass();

}

and a method:

void showValues()
{
    ;
        info(strfmt("%1", num2str(cdty.parmBid(1),1,4,1,0)));
        cdty.parmBid(1,1.2345);
        info(strfmt("%1", num2str(cdty.parmBid(1),1,4,1,0)));

        cdty.parmBid(2,2.3432);
        info(strfmt("%1", num2str(cdty.parmBid(1),1,4,1,0)));
        info(strfmt("%1", num2str(cdty.parmBid(2),1,4,1,0)));
        cdty.parmAsk(3,3.3333);
        info(strfmt("%1", num2str(cdty.parmAsk(3),1,4,1,0)));
        cdty.parmAsk(4,4.4444);
        info(strfmt("%1", num2str(cdty.parmAsk(3),1,4,1,0)));


}

Also a button on the form, when clicked runs method showValues.

This way I can emulate insert, update and read values from te cdtyTable but per ID.

I would expect when clicking the button on the form:

Info Bericht (12/1/2019 - 11:29:00) 0.0000
Info Bericht (12/1/2019 - 11:29:00) 1.2345
Info Bericht (12/1/2019 - 11:29:00) 1.2345
Info Bericht (12/1/2019 - 11:29:00) 2.3432
Info Bericht (12/1/2019 - 11:29:00) 3.3333
Info Bericht (12/1/2019 - 11:29:00) 4.4444

But I only get zeros…

Pls advise

I don’t have much time, so just a few quick notes:

You say performance is critical, but you run two select statements and one update for every field you want to change? The normal approach is selecting a record, changing fields and calling update() - just once. It also makes sure that all changes are applied at the same time.

Your implementation of find() won’t work with a temporary table.

Can you please show us how you receive data from the API?

Because you’re new to programming, you’re not in the best position to design the solution. But I can’t suggest one for you without any information about the API.

I will gladly do Martin.

As said there’s an ActiveX named, I named MDAPI, component on the form which communicates with the API.

Through the activeX I can use all methods.

In ClassDeclaration I set

COM IContract;
COM IOrder;

To start I call method MDAPI.connect"127.0.0.1",4002,0,false);

Then I call the IContract with some parameters of identify the commodity like symbol, exchange and currency

then I call

MDAPI.reqMktDataEx(_id, IContract, "", 0 , iTagValuelist);

With this I request the marketdata(stream) of the commodity described in IContract

After this the stream starts and there’s a method in the ActiveX what givesback the marketdata.

I can get this data in the method: onevent_tickprice

void onEvent_TickPrice(int _id, int tickType, real _price)
{
    ;
    if(_tickType == 1 && _price != 0)//bid
        {
        //write this _price in TmpTable record with Id _id and field Bid
        }
        
    if(_tickType == 2 && _price != 0)//ask
        {
        //write this _price in TmpTable record with Id _id and field Ask
        }
        
    element.checkPrices(_id);
}

Basically thats all to need it working.

Of course when checkPrices() creates a trigger, then I can call MDAPI.placeOrder(_orderId, Icontract, IOrder);

Where IOrder is an Object which is filled with orderparameters.

This system works completely for 1 contract.

Now I want to use the same system, for n contracts, so with every new pricetick it does all the pricechecks for that _id

Hope it’s clear? Otherwise pls ask

I have removed the Find method in the parmBid and parmAsk and changed the select

So parmbid is now

public real parmBid(int _id, real _bid = 0)
{

    ;
    if(_bid != 0)
        {
        select forupdate CdtyTable where CdtyTable.Id == _id;
        CdtyTable.Id = _id;
        CdtyTable.Bid = _bid;
            if(CdtyTable.RecId)  CdtyTable.update();
            else CdtyTable.insert();
        }
    else
        {
        select CdtyTable where CdtyTable.Id == _id;
        }
    return CdtyTable.Bid;
}

and is I call the method for showing values:

void showValues()
{
    ;
        info(strfmt("%1", num2str(cdty.parmBid(1),1,4,1,0)));
        cdty.parmBid(1,1.2345);
        info(strfmt("%1", num2str(cdty.parmBid(1),1,4,1,0)));

        cdty.parmBid(2,2.3432);
        info(strfmt("%1", num2str(cdty.parmBid(1),1,4,1,0)));
        info(strfmt("%1", num2str(cdty.parmBid(2),1,4,1,0)));
        cdty.parmAsk(3,3.3333);
        info(strfmt("%1", num2str(cdty.parmAsk(3),1,4,1,0)));
        cdty.parmAsk(3,4.4444);
        info(strfmt("%1", num2str(cdty.parmAsk(3),1,4,1,0)));


}

I get:

Info Bericht (12/1/2019 - 17:32:30) 0.0000
Info Bericht (12/1/2019 - 17:32:30) 1.2345
Info Bericht (12/1/2019 - 17:32:30) 1.2345
Info Bericht (12/1/2019 - 17:32:30) 2.3432
Info Bericht (12/1/2019 - 17:32:30) 3.3333
Info Bericht (12/1/2019 - 17:32:30) 4.4444

As expected.

So it looks all god.

Do you agree this is the right way to get and set the variables in memory per _id as asked in the OP??

(Only when it’s under 128kb, but I have read 220 CustTable records is plm 128kb, so I with about 5 commodities and 23 values will not exceed that it seems)

I changed the parmbid and parmask a bit further with only 1 select statement.

This works also.

Don’t know if this is good practice to select forupdate, but don’t do an update when only getting a value (when _bid parameter is 0)

public real parmBid(int _id, real _bid = 0)
{

    ;
    select forupdate CdtyTable where CdtyTable.Id == _id;

    if(_bid != 0)
        {
        CdtyTable.Id = _id;
        CdtyTable.Bid = _bid;
            if(CdtyTable.RecId)  CdtyTable.update();
            else CdtyTable.insert();
        }
    return CdtyTable.Bid;
}

Decided to sepsarate the insert in another method, which will be executed at initialization.

So the records are always there and the “GetSetmethod” will not lock any record becuase a forupdate() is not committed and creates a lock.

So GetSetMethod is like:

public real parmBid(int _id, real _bid = 0)
{
    ;
    if(_bid != 0)
        {
        select forupdate CdtyTable where CdtyTable.Id == _id;

        CdtyTable.bid = _bid;
        CdtyTable.update();
        }
    else
        {
        select CdtyTable where CdtyTable.Id == _id;
        }
    return CdtyTable.bid;
}

Let’s talk about the data model before writing code.

If I understand it correctly, you get the data as a combination of three values: ID, tick type and price. But you want to save it in a different format - you want to have a single record record for all tick types for a single ID, with a field for every tick type.

Wouldn’t it be much easier to store data in the format as what the API uses? You could throw away all your complicated method such as parmBid() and implement saving with just a few lines of code:

void onEvent_TickPrice(int _id, int _tickType, real _price)
{
	select forUpdate tmpTable
		where tmpTable.Id == _id
		   && tmpTable.TickType == _tickType;
	
	tmpTable.Price = _price;
	tmpTable.write();
}

Good idea [emoticon:c4563cd7d5574777a71c318021cbbcc8]

Yes I receive it that way. Not sure if I understand you correctly about the different format.

In a simplistic conceptual way: There are 3 interesting ticktypes: 1(bid), 2(ask) and 7 (open) those come in random order.

So I want 1 record per _id, with a field named Bid, a field named Ask and a field named Open. (and more fields, see later)

So those 3 fields are written in the tmpTable in the method onEventTickprice.

If the field Bid != 0 and field Ask != 0, I fill a 4th field of that same record called Spread.

Spread is calculated by ask -/- bid.

In the checkprices method I will compare bid with open, so at that moment I have to read the bid and open from the table.

In the init I fill more fields per ID with values like deviationtrigger, Qty etc. all used at checkPrices method
That’s why I thought of 1 method per field which can write and read, depending on the params I feed.

Hope it’s clear