API Support Forum
OEC API > API Support > OSO orders bug in COM API
Author Topic: OSO orders bug in COM API
(13 messages, Page 1 of 1)
Moderators: VPfau
WWatson2582
Posts: 38
Joined: May 03, 2018


Posted: Feb 05, 2021 @ 01:46 PM             Msg. 1 of 13
I have encountered the following bug in the COM API (latest released version).

After submitting a OSO order using GFAPI()->Orders->SendOSOOrders() method, the two linked orders are submitted twice, or in other words, are each duplicated twice. After the bracket order is completed and the position would normally be again flat, the duplicate order causes an extra closing transaction to be executed, often leaving a non-flat position.

I have created some sample code to demonstrate this using as a base your COM API sample application. All modifications are done to the file CppCOMSampleDlg.cpp. To demonstrate this bug:

1) Add the following global variable declaration to the top of the file:

double lastPrice;

2) Change the CCppCOMSampleDlg::OnPriceChanged method as follows:

void __stdcall CCppCOMSampleDlg::OnPriceChanged(GF_Api_COM::IGFComClientPtr client, GF_Api_COM::IContractPtr contract, GF_Api_COM::IPricePtr price)
{
lastPrice = price->LastPrice;

if(m_ShowPriceUpdates)
{
log Symbol PriceToString(price->LastPrice) LastVol << std::endl;
}
}

3) Change the CCppCOMSampleDlg::OnBnClickedSendorder() as follows:

void CCppCOMSampleDlg::OnBnClickedSendorder()
{
CheckConnectedGFAPI();
COrderDlg dlg(GFAPI());
bool orderError = false;

if(dlg.DoModal() == IDOK)
{

GF_Api_COM::IOrderDraftBuilderPtr builder;
builder.CreateInstance(__uuidof(GF_Api_COM::OrderDraftBuilder));
GF_Api_COM::IAccountListPtr accounts = GFAPI()->Accounts->Get();
GF_Api_COM::IAccountPtr account = accounts->GetAt(0);
GF_Api_COM::IAccountIDPtr accountID = account->id;

builder = builder
->WithAccountID(accountID)
->WithSide(dlg.m_Buy ? GF_Api_COM::OrderSide_Buy : GF_Api_COM::OrderSide_Sell)
->WithQuantity(dlg.m_Qty)
->WithContractID(GFAPI()->Contracts->Get_2((LPCSTR)dlg.m_SelectedContract)->id)
->WithOrderType((GF_Api_COM::OrderType)dlg.m_SelectedType)
->WithFlags(dlg.m_GTC ? GF_Api_COM::OrderFlags_GTC : GF_Api_COM::OrderFlags_None);

double tickSize = GFAPI()->Contracts->Get_2((LPCSTR)dlg.m_SelectedContract)->tickSize; // To set up bracket order distances later

GF_Api_COM::IOrderDraftPtr mainOrder = builder->Build();

if (GFAPI()->Orders->Drafts->Validate_2(mainOrder)->Count > 0) {
log << "Main Order has invalid parts and will not be sent" << std::endl;
orderError = true;
}

GF_Api_COM::IOrderDraftBuilderPtr target;
target.CreateInstance(__uuidof(GF_Api_COM::OrderDraftBuilder));

target = target
->WithAccountID(accountID)
->WithSide(GF_Api_COM::OrderSide_Sell)
->WithQuantity(dlg.m_Qty)
->WithContractID(GFAPI()->Contracts->Get_2((LPCSTR)dlg.m_SelectedContract)->id)
->WithOrderType(GF_Api_COM::OrderType_Limit)
->WithFlags(dlg.m_GTC ? GF_Api_COM::OrderFlags_GTC : GF_Api_COM::OrderFlags_None)
->WithPrice(lastPrice + 15 * tickSize);

GF_Api_COM::IOrderDraftPtr profitTarget = target->Build();

if (GFAPI()->Orders->Drafts->Validate_2(profitTarget)->Count > 0) {
log << "Profit Target Order has invalid parts and will not be sent" << std::endl;
orderError = true;
}

GF_Api_COM::IOrderDraftBuilderPtr loss;
loss.CreateInstance(__uuidof(GF_Api_COM::OrderDraftBuilder));
loss = loss
->WithAccountID(accountID)
->WithSide(GF_Api_COM::OrderSide_Sell)
->WithQuantity(dlg.m_Qty)
->WithContractID(GFAPI()->Contracts->Get_2((LPCSTR)dlg.m_SelectedContract)->id)
->WithOrderType(GF_Api_COM::OrderType_Stop)
->WithFlags(dlg.m_GTC ? GF_Api_COM::OrderFlags_GTC : GF_Api_COM::OrderFlags_None)
->WithPrice(lastPrice - 15 * tickSize);

GF_Api_COM::IOrderDraftPtr stopLoss = loss->Build();

if (GFAPI()->Orders->Drafts->Validate_2(stopLoss)->Count > 0) {
log << "Stop Loss Order has invalid parts and will not be sent" << std::endl;
orderError = true;
}

if (!orderError) {
GF_Api_COM::IOrderListPtr orders = GFAPI()->Orders->SendOSOOrders(mainOrder, profitTarget, stopLoss, GF_Api_COM::OSOGroupingMethod_ByFirstPrice);
if (!orders)
log << "Cannot send OSO orders" << std::endl;
else
log << "OSO Orders have been sent. " << std::endl
GetAt(0)->id->Value GetAt(0)->ToString << std::endl
GetAt(1)->id->Value GetAt(1)->ToString << std::endl
GetAt(2)->id->Value GetAt(2)->ToString << std::endl;
}
}
}

To demonstrate a bug with OSO orders after connecting to the GAIN server:
1) Subscribe to prices for a contract, e.g. ESH21
2) Click on the "Send Order" button in the main application dialog box.
3) Assure that the Order dialog box is completed with a Market Buy order for the same symbol subscribed to in step #1

An OSO bracket order will be constructed and submitted using a 15-tick Limit order and 15-tick Stop order. Rather than just the primary order and two linked orders submitted, you should see duplicate orders for each of the linked orders.
SPikalov
Posts: 24
Joined:


Posted: Feb 10, 2021 @ 04:46 AM             Msg. 2 of 13
Hello.

I repeated your test and found no trouble.
Please repeat the test and provide the account name and order IDs.

Thanks,
Sergey
WWatson2582
Posts: 38
Joined: May 03, 2018


Posted: Feb 10, 2021 @ 03:45 PM             Msg. 3 of 13
We've done this multiple times using our own software calling GFAPI()->Orders->SendOSOOrders() as well as the sample COM API code (modified as given in the first message) always with the same result.

The account is mine ("WWatson2582") using the test server api.gainfutures.com.

In my most recent test, the initial main order was 211862711 and the OCO bracket orders were 211862712 and 211862713. The additional incorrect orders were 211862714 and 211862715.

The net effect of these extra orders is that when the bracket order is supposed to flatten the position, it is instead left short or long depending on which side of the bracket is filled (both orders on that side are filled simultaneously.)
WWatson2582
Posts: 38
Joined: May 03, 2018


Posted: Feb 10, 2021 @ 04:21 PM             Msg. 4 of 13
I just heard that a customer of ours testing the COM API with our product had an bracket order go from LONG 3 to SHORT 3 because of this bug but fortunately caught it and were able to flatten their position manually. We are going to disable OSO orders until this is resolved, but this was a real money account so it appears the bug is not limited to the test server.

I would post screencaps of the entire transaction but don't see a way to do that here.
SPikalov
Posts: 24
Joined:


Posted: Feb 11, 2021 @ 02:39 AM             Msg. 5 of 13
it’s not a bug it’s a feature :)
211862712 - this is a "fake" order that we use to group real orders
211862714 - this is a "real" or "sub" order that we send to an exchange. See order comment - ‘Sub order for 211862712’

Sub orders creation algorithm depends on the grouping method you choose. See GF.Servers.Msgs.Values.Orders. OSOGroupingMethod
In your case, it was OSOGroupingMethod. ByFirstPrice

211862713 -> 211862715
The same situation.

You can open the Trader app and submit OSO orders. I think after that you will understand the OSO algorithm more clearly
SPikalov
Posts: 24
Joined:


Posted: Feb 11, 2021 @ 03:28 AM             Msg. 6 of 13
Sorry,
GF.Servers.Msgs.Values.Orders. OSOGroupingMethod
=>
GF.Api.Values.Orders.OSOGroupingMethod
WWatson2582
Posts: 38
Joined: May 03, 2018


Posted: Feb 11, 2021 @ 09:52 AM             Msg. 7 of 13
I will investigate this further. How does one specify the server to use with the Gain Trader app? My credentials don't seem to work with anything other than api.gainfutures.com
WWatson2582
Posts: 38
Joined: May 03, 2018


Posted: Feb 11, 2021 @ 09:52 AM             Msg. 8 of 13
Do I understand that you are saying that the "fake" orders are never being filled? When are they cancelled and why do we even see them on the client side of the API if they are not "real"?
Edited by WWatson2582 on Feb 11, 2021 09:53 AM
CMicciche902
Posts: 348
Joined:


Posted: Feb 11, 2021 @ 10:27 AM             Msg. 9 of 13
Details on the OSO grouping can be found here: https://gainfutures.com/gain-trader/additional-features/

Latest GAIN Trader Developer can be found at: https://api.gainfutures.com/WebAPI/api/Files/DownloadClientUpdateLast?brandId=0&clientTypeId=0&branchId=2

We don't have a server selector for public versions.

GAIN Trader Developer points to api.gainfutures.com
GAIN Trader Demo points to sim.gainfutures.com
GAIN Trader (LIVE) points to prod.gainfutures.com

If credentials need reset, I'll send an email with new password.
Chris M
WWatson2582
Posts: 38
Joined: May 03, 2018


Posted: Feb 11, 2021 @ 11:40 AM             Msg. 10 of 13
It wouldn't let me log in, Chris, so I guess I need a credential reset.
WWatson2582
Posts: 38
Joined: May 03, 2018


Posted: Feb 19, 2021 @ 09:50 AM             Msg. 11 of 13
I'm not sure I understand why reporting non-existent orders to the client is a "feature", but I have worked around this by filtering out all orders whose "comments" field starts with "Sub order for" and things seem to be working fine on our end now. I know that according to your description I am technically filtering out the real order and holding on to the fake order, but for our purposes it works out and I see no way to otherwise distinguish real from fake.

So I guess my remaining question is: why are these fake orders coming through the API at all? What possible use could I on the client end have for them?
Edited by WWatson2582 on Feb 19, 2021 09:51 AM
SPikalov
Posts: 24
Joined:


Posted: Feb 22, 2021 @ 12:11 AM             Msg. 12 of 13
“I see no way to otherwise distinguish real from fake.”
See
GF.Api.Orders.IOrder
IOrder ParentOrder { get; }
IReadOnlyList SubOrders { get; }

or
GF.Api.COM.Orders.IOrder
IOrder ParentOrder { get; }
IOrderList SubOrders { get; }

“ So I guess my remaining question is: why are these fake orders coming through the API at all? What possible use could I on the client end have for them?”
You can work with both parent orders and sub-orders.
I.e. If you modify\cancel the parent order => sub orders will be modified\canceled corresponding way
If you modify\cancel sub orders => the parent order will be modified\canceled corresponding way
WWatson2582
Posts: 38
Joined: May 03, 2018


Posted: Feb 24, 2021 @ 09:10 AM             Msg. 13 of 13
We're working now fine, so you can consider this issue closed. Thanks!