One Cancels Other (OCO) orders?
-
- Posts: 17
- Joined: Sat Jun 06, 2009 8:40 pm
One Cancels Other (OCO) orders?
Hi all,
I'm trying to open multiple positions when the first position closes then the others close also (even if they're still pending).
I've tried using MagicNumber to specify related trades to close at once but this doesn't seem to work. How would I do this?
Kind regards,
Matt
I'm trying to open multiple positions when the first position closes then the others close also (even if they're still pending).
I've tried using MagicNumber to specify related trades to close at once but this doesn't seem to work. How would I do this?
Kind regards,
Matt
- Terranin
- Site Admin
- Posts: 833
- Joined: Sat Oct 21, 2006 4:39 pm
Re: One Cancels Other (OCO) orders?
Matt_mm wrote:Hi all,
I'm trying to open multiple positions when the first position closes then the others close also (even if they're still pending).
I've tried using MagicNumber to specify related trades to close at once but this doesn't seem to work. How would I do this?
Kind regards,
Matt
Magic number works if you use it correctly. For example if your orders were marked with some magic number:
Code: Select all
if OrderClosed(handle) then
begin
for i:=OrdersTotal - 1 downto 0 do
if OrderSelect(i, SELECT_BY_POS, MODE_TRADES) then
if OrderMagicNumber = <some number> then
CloseOrder(OrderHandle);
end;
Note: you should select orders in opposite direction, because when you close them your order will be broken if you select them from 0 to ...
Hasta la vista
Mike
Mike
-
- Posts: 17
- Joined: Sat Jun 06, 2009 8:40 pm
No luck for some reason here's my code:
Code: Select all
for i:=0 to OrdersTotal do
begin
if OrderSelect(i, SELECT_BY_TICKET, MODE_HISTORY) then
begin
if OrderClosed(i) then
begin
TempMagicNum:= OrderMagicNumber;
for ii:=0 to OrdersTotal do
begin
if OrderSelect(i, SELECT_BY_POS, MODE_TRADES) then
begin
if (OrderMagicNumber = TempMagicNum) then
begin
if (OrderType = tp_BuyStop) or (OrderType = tp_SellStop) then
begin
DeleteOrder(OrderTicket);
end;
if (OrderType = tp_Buy) or (OrderType = tp_Sell) then
begin
CloseOrder(OrderTicket);
end;
end;
end;
end;
end;
end;
end;
- Terranin
- Site Admin
- Posts: 833
- Joined: Sat Oct 21, 2006 4:39 pm
Matt_mm wrote:No luck for some reason here's my code:Code: Select all
for i:=0 to OrdersTotal do
begin
if OrderSelect(i, SELECT_BY_TICKET, MODE_HISTORY) then
begin
if OrderClosed(i) then
begin
TempMagicNum:= OrderMagicNumber;
for ii:=0 to OrdersTotal do
begin
if OrderSelect(i, SELECT_BY_POS, MODE_TRADES) then
begin
if (OrderMagicNumber = TempMagicNum) then
begin
if (OrderType = tp_BuyStop) or (OrderType = tp_SellStop) then
begin
DeleteOrder(OrderTicket);
end;
if (OrderType = tp_Buy) or (OrderType = tp_Sell) then
begin
CloseOrder(OrderTicket);
end;
end;
end;
end;
end;
end;
end;
Lots of mistakes. Even difficult to understand what you want to do with this code.
OrdersTotal is only for opened positions. You can not search with it in history records. For history you should use HistoryTotal.
You can not use OrdersTotal and SELECT_BY_TICKET because it means you need to pass ticket but not a position number.
If you search in history then every order there is closed already.
If you close order - it changes HistoryTotal, you can not do this in the loop using HistoryTotal, at the same time it changes OrdersTotal and also affects other loop.
etc...
The correct algorithm would be to track new order by saving its handle -
Code: Select all
OrderHandle := SendInstantOrder(....)
....
if OrderClosed(OrderHandle) then <do something>
when you found situation that order was closed delete all other orders with same MagicNumber
Code: Select all
OrderSelect(OrderHande, SELECT_BY_TICKET, MODE_HISTORY);
MagicNumber := OrderMagicNumber;
for i:=OrdersTotal - 1 downto 0 do
if OrderSelect(i, SELECT_BY_POS, MODE_TRADES) then
if OrderMagicNumber = MagicNumber then
if (OrderType = tp_Buy) or (OrderType = tp_Sell) then
CloseOrder(OrderHandle)
else
DeleteOrder(OrderHandle);
Hasta la vista
Mike
Mike
-
- Posts: 17
- Joined: Sat Jun 06, 2009 8:40 pm
Thanks Terranin, that puts a lot in perspective.
I've got a related issue. I got the code to work but going through all the history continuously uses too much CPU. So I used your example to store any open positions in an array and remove them from the array once they're closed. Problem is I still need a big array so I added a variable to count the positions (since I couldn't dynamically change the array size in Delphi once it's been set once).
If I use the array length - length(array) - it works but is still processor intensive, when I use a variable to keep track of how many positions are in the array (PosNum) and use that in the loop it stops working and doesn't close the positions. Here's the code:
And the one cancels other code:
If instead of using "for i:=0 to ArrayLen do" I tried using the PosNum variable but as soon as I do this the code stops closing the positions.
If I instead leave the original code it still doesn't work properly since it's taking so much load on the CPU that there is a delay and some of the positions don't open at the right time, probably because it's still trying to do other calculations. So I need a more efficient way of doing this?
Any help would be greatly appreciated.
Kind regards,
Matt[/code]
I've got a related issue. I got the code to work but going through all the history continuously uses too much CPU. So I used your example to store any open positions in an array and remove them from the array once they're closed. Problem is I still need a big array so I added a variable to count the positions (since I couldn't dynamically change the array size in Delphi once it's been set once).
If I use the array length - length(array) - it works but is still processor intensive, when I use a variable to keep track of how many positions are in the array (PosNum) and use that in the loop it stops working and doesn't close the positions. Here's the code:
Code: Select all
MagicNum: integer = 0;
OpenPositions: array[0..50] of integer;
PosNum: integer = 0;
begin
if (OpenShort) then
begin
OpenPositions[PosNum]:= OrderNumber;
PosNum:= PosNum+1;
SendInstantOrder(Symbol, op_Sell, PositionSize, StopPrice, ProfitPrice, '', MagicNum, OrderNumber);
if (Stack) then
begin
SendPendingOrder(Symbol, op_SellStop, PositionSize, StopPrice, ProfitPrice, (EntryPrice-(Round(TakeProfit*0.7)*Point)), '', MagicNum, OrderNumber);
SendPendingOrder(Symbol, op_SellStop, PositionSize, StopPrice, ProfitPrice, (EntryPrice-(Round(TakeProfit*0.8)*Point)), '', MagicNum, OrderNumber);
SendPendingOrder(Symbol, op_SellStop, PositionSize, StopPrice, ProfitPrice, (EntryPrice-(Round(TakeProfit*0.9)*Point)), '', MagicNum, OrderNumber);
MagicNum:= MagicNum +1;
end;
end;
And the one cancels other code:
Code: Select all
ArrayLen:= length(OpenPositions);
for i:=0 to ArrayLen do
begin
if OrderClosed(OpenPositions[i]) then
begin
OrderSelect(OpenPositions[i], SELECT_BY_TICKET, MODE_HISTORY);
TempMagicNum:= OrderMagicNumber;
for ii:=0 to OrdersTotal do
begin
if OrderSelect(ii, SELECT_BY_POS, MODE_TRADES) then
begin
if (OrderMagicNumber = TempMagicNum) then
begin
if (OrderType = tp_BuyStop) or (OrderType = tp_SellStop) then
begin
DeleteOrder(OrderTicket);
end;
if (OrderType = tp_Buy) or (OrderType = tp_Sell) then
begin
CloseOrder(OrderTicket);
end;
end;
end;
end;
for delIndex:= i to PosNum do
begin
OpenPositions[delIndex] := OpenPositions[delIndex+1];
OpenPositions[ArrayLen] := 0;
end;
PosNum:= PosNum - 1;
end;
end;
end;
If instead of using "for i:=0 to ArrayLen do" I tried using the PosNum variable but as soon as I do this the code stops closing the positions.
Code: Select all
for i:=0 to (PosNum) do
If I instead leave the original code it still doesn't work properly since it's taking so much load on the CPU that there is a delay and some of the positions don't open at the right time, probably because it's still trying to do other calculations. So I need a more efficient way of doing this?
Any help would be greatly appreciated.
Kind regards,
Matt[/code]
-
- Posts: 151
- Joined: Tue Feb 24, 2009 1:03 pm
Hi Matt_mm,
Maybe this will help a little until Mike is able to reply...
You can create dynamic arrays in delphi using something like this:
(Global Vars)
OpenPositions: array of integer;
(Init Proc)
SetLength(OpenPositions, 0); //Initialises array, setting its length to 0
(your proc to open orders)
Also in your original code...
The third line populates the [posnum] index of the array with the contents of the OrderNumber variable. At this point in time the OrderNumber variable contains the orderhandle of the previous order (or the initial value of the variable if no previous order was created). You would need to place this line after the SendInstantOrder line so it gets populated with the orderhandle of the order just sent or preferably, use the method I used in my first bit of code above, assigning the result of the SendInstantOrder function directly to the OpenPositions array (OpenPositions[Length(OpenPositions)-1] := SendInstantOrder(....)).
The OCO part could be something like this...
NOTE: I have not tested any of the above code, just trying to show you the principles involved.
If you have any questions please ask.
Steve
Maybe this will help a little until Mike is able to reply...
Problem is I still need a big array so I added a variable to count the positions (since I couldn't dynamically change the array size in Delphi once it's been set once).
You can create dynamic arrays in delphi using something like this:
(Global Vars)
OpenPositions: array of integer;
(Init Proc)
SetLength(OpenPositions, 0); //Initialises array, setting its length to 0
(your proc to open orders)
Code: Select all
if (OpenShort) then
begin
SetLength(OpenPositions, Length(OpenPositions) + 1); //increase length of array by 1 element ready to accept next order number
//THE LINE BELOW IS INCORRECT, PLEASE SEE SECOND POST BELOW FOR CORRECT USAGE
OpenPositions[Length(OpenPositions)-1] := SendInstantOrder(Symbol, op_Sell, PositionSize, StopPrice, ProfitPrice, '', MagicNum, OrderNumber); //Saves orderhandle to newest array element
if (Stack) then
begin
SendPendingOrder(Symbol, op_SellStop, PositionSize, StopPrice, ProfitPrice, (EntryPrice-(Round(TakeProfit*0.7)*Point)), '', MagicNum, OrderNumber);
SendPendingOrder(Symbol, op_SellStop, PositionSize, StopPrice, ProfitPrice, (EntryPrice-(Round(TakeProfit*0.8)*Point)), '', MagicNum, OrderNumber);
SendPendingOrder(Symbol, op_SellStop, PositionSize, StopPrice, ProfitPrice, (EntryPrice-(Round(TakeProfit*0.9)*Point)), '', MagicNum, OrderNumber);
MagicNum:= MagicNum +1;
end;
end;
Also in your original code...
Code: Select all
begin
if (OpenShort) then
begin
OpenPositions[PosNum]:= OrderNumber;
PosNum:= PosNum+1;
SendInstantOrder(Symbol, op_Sell, PositionSize, StopPrice, ProfitPrice, '', MagicNum, OrderNumber);
...
The third line populates the [posnum] index of the array with the contents of the OrderNumber variable. At this point in time the OrderNumber variable contains the orderhandle of the previous order (or the initial value of the variable if no previous order was created). You would need to place this line after the SendInstantOrder line so it gets populated with the orderhandle of the order just sent or preferably, use the method I used in my first bit of code above, assigning the result of the SendInstantOrder function directly to the OpenPositions array (OpenPositions[Length(OpenPositions)-1] := SendInstantOrder(....)).
The OCO part could be something like this...
Code: Select all
//Length returns number of elements in array so array 0 to 9 has 10 elements and length function will return 10.
ArrayLen:= length(OpenPositions)-1; Upper boundary of array is now correctly set to Length(OpenPositions)-1
for i:=ArrayLen downto 0 do //start loop at end of array and work backwards, this way resizing the array (setlength below) should not affect loop
begin
if OrderClosed(OpenPositions[i]) then
begin
OrderSelect(OpenPositions[i], SELECT_BY_TICKET, MODE_HISTORY);
TempMagicNum:= OrderMagicNumber;
for ii:=OrdersTotal-1 downto 0 do //select orders in reverse as Mike suggested, also note OrdersTotal-1 is correct starting point in the loop
begin
if OrderSelect(ii, SELECT_BY_POS, MODE_TRADES) then
if (OrderMagicNumber = TempMagicNum) then
if (OrderType = tp_BuyStop) or (OrderType = tp_SellStop) then
DeleteOrder(OrderTicket)
else
CloseOrder(OrderTicket);
end;
for delIndex:= i to ArrayLen-1 do
OpenPositions[delIndex] := OpenPositions[delIndex+1]; //shifts all but last array element down by 1
SetLength(OpenPositions, Length(OpenPositions)-1); //resize array removing last element
end;
end;
NOTE: I have not tested any of the above code, just trying to show you the principles involved.
If you have any questions please ask.
Steve
Last edited by dackjaniels on Tue Jul 14, 2009 3:36 am, edited 1 time in total.
-
- Posts: 17
- Joined: Sat Jun 06, 2009 8:40 pm
Thanks Dackjaniels!
It really helped explaining some of your logic too, I was wondering why Mike had used the downto loop but when you explained it I realised why.
The changes to the code work great! (I'd tried using a dynamic array first but maybe because of my loop or incorrect understanding of length it wasn't working).
The only thing that still doesn't work is I can't assign an integer var to an order
An error comes back saying I'm trying to assign a boolean value to an integer.
I also couldn't use Mike's new function for time updated in 2.1. I installed 2.1 over the top of 2.0 and copied the library files from the Forex Tester folder to replace my existing but maybe I need to do a clean install for the libraries to install properly?
It really helped explaining some of your logic too, I was wondering why Mike had used the downto loop but when you explained it I realised why.
The changes to the code work great! (I'd tried using a dynamic array first but maybe because of my loop or incorrect understanding of length it wasn't working).
The only thing that still doesn't work is I can't assign an integer var to an order
Code: Select all
OpenPositions[ArrayLen] := SendInstantOrder(etc);
An error comes back saying I'm trying to assign a boolean value to an integer.
I also couldn't use Mike's new function for time updated in 2.1. I installed 2.1 over the top of 2.0 and copied the library files from the Forex Tester folder to replace my existing but maybe I need to do a clean install for the libraries to install properly?
-
- Posts: 151
- Joined: Tue Feb 24, 2009 1:03 pm
Hey Matt_mm,
My bad, I made a mistake that's why you are getting the boolean/integer error. I've done this plenty of times, guess I just had a [premature] 'senior' moment.
The SendInstantOrder function returns a boolean, if it's successful it returns TRUE othewise it returns FALSE. It also returns the OrderHandle which is an integer.
Here's a better example of use:
Note the destination variable for the orderhandle is the last parameter passed to the function.
Regarding the time function, I am assuming you are referring to the new TimeCurrent function included in FT that returns the time of the most recent tick (emulating getting the current time from the server in live trading).
I have had this working in the past so would guess it's an issue regarding the StrategyInterfaceUnit.pas file. Just be sure that you are compiling against the correct (latest) version. Delphi will usually look for this file in the folder where you save your delphi projects so just double-check it has been copied there correctly.
For example, if you have two folders where you save your indicator and strategy delphi projects...
Documents\Borland Delphi Projects\Indicators
Documents\BorlandDelphi Projects\Strategies
ensure you have the following files also (copied from the corresponding ForexTester2\Examples\Indicators\Delphi and C:\ForexTester2\Examples\Strategies\Delphi sub folders)
Documents\Borland Delphi Projects\Indicators\TechnicalFunctions.pas
Documents\BorlandDelphi Projects\Strategies\TechnicalFunctions.pas
Documents\Borland Delphi Projects\Indicators\IndicatorInterfaceUnit.pas
Documents\BorlandDelphi Projects\Strategies\StrategyInterfaceUnit.pas
NOTE: The \Indicators\TechnicalFuntions.pas and \Strategies\TechnicalFuntions.pas files are different, do not copy the same file to both locations as I once did
Regards,
Steve
My bad, I made a mistake that's why you are getting the boolean/integer error. I've done this plenty of times, guess I just had a [premature] 'senior' moment.
The SendInstantOrder function returns a boolean, if it's successful it returns TRUE othewise it returns FALSE. It also returns the OrderHandle which is an integer.
Here's a better example of use:
Code: Select all
If SendInstantOrder(Symbol, op_Sell, PositionSize, StopPrice, ProfitPrice, '', MagicNum, OpenPositions[Length(OpenPositions)-1]); then
Print('Order successfully placed') //Printed if order is successful
else
Print('Error placing Order'); //Printed if an error occurs placing order
Note the destination variable for the orderhandle is the last parameter passed to the function.
Regarding the time function, I am assuming you are referring to the new TimeCurrent function included in FT that returns the time of the most recent tick (emulating getting the current time from the server in live trading).
I have had this working in the past so would guess it's an issue regarding the StrategyInterfaceUnit.pas file. Just be sure that you are compiling against the correct (latest) version. Delphi will usually look for this file in the folder where you save your delphi projects so just double-check it has been copied there correctly.
For example, if you have two folders where you save your indicator and strategy delphi projects...
Documents\Borland Delphi Projects\Indicators
Documents\BorlandDelphi Projects\Strategies
ensure you have the following files also (copied from the corresponding ForexTester2\Examples\Indicators\Delphi and C:\ForexTester2\Examples\Strategies\Delphi sub folders)
Documents\Borland Delphi Projects\Indicators\TechnicalFunctions.pas
Documents\BorlandDelphi Projects\Strategies\TechnicalFunctions.pas
Documents\Borland Delphi Projects\Indicators\IndicatorInterfaceUnit.pas
Documents\BorlandDelphi Projects\Strategies\StrategyInterfaceUnit.pas
NOTE: The \Indicators\TechnicalFuntions.pas and \Strategies\TechnicalFuntions.pas files are different, do not copy the same file to both locations as I once did
Regards,
Steve
Who is online
Users browsing this forum: No registered users and 15 guests