Forex Tester Forum (Professional Training Software for Traders) Forum Index Forex Tester Forum (Professional Training Software for Traders)

Back to main site   Risk disclosure
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

One Cancels Other (OCO) orders?

 
Post new topic   Reply to topic    Forex Tester Forum (Professional Training Software for Traders) Forum Index -> FT API
View previous topic :: View next topic  
Author Message
Matt_mm



Joined: 07 Jun 2009
Posts: 17

PostPosted: Thu Jul 09, 2009 10:59 am    Post subject: One Cancels Other (OCO) orders? Reply with quote

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
Back to top
View user's profile Send private message
Terranin
Site Admin


Joined: 21 Oct 2006
Posts: 831

PostPosted: Thu Jul 09, 2009 3:01 pm    Post subject: Re: One Cancels Other (OCO) orders? Reply with quote

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:
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
Back to top
View user's profile Send private message
Matt_mm



Joined: 07 Jun 2009
Posts: 17

PostPosted: Thu Jul 09, 2009 10:18 pm    Post subject: Reply with quote

Thanks Mike,

Perhaps it was my incorrect for loop. I'll give this a go.

Kind regards,

Matt
Back to top
View user's profile Send private message
Matt_mm



Joined: 07 Jun 2009
Posts: 17

PostPosted: Fri Jul 10, 2009 11:02 am    Post subject: Reply with quote

No luck for some reason here's my code:

Code:
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;
Back to top
View user's profile Send private message
Terranin
Site Admin


Joined: 21 Oct 2006
Posts: 831

PostPosted: Fri Jul 10, 2009 7:00 pm    Post subject: Reply with quote

Matt_mm wrote:
No luck for some reason here's my code:

Code:
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:
OrderHandle := SendInstantOrder(....)

....

if OrderClosed(OrderHandle) then <do something>


when you found situation that order was closed delete all other orders with same MagicNumber

Code:
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
Back to top
View user's profile Send private message
Matt_mm



Joined: 07 Jun 2009
Posts: 17

PostPosted: Sun Jul 12, 2009 11:35 am    Post subject: Reply with quote

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:

Code:
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:
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:
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]
Back to top
View user's profile Send private message
dackjaniels



Joined: 24 Feb 2009
Posts: 151

PostPosted: Mon Jul 13, 2009 10:50 am    Post subject: Reply with quote

Hi Matt_mm,
Maybe this will help a little until Mike is able to reply...

Quote:
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:
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:
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:
//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 8:36 am; edited 1 time in total
Back to top
View user's profile Send private message
Matt_mm



Joined: 07 Jun 2009
Posts: 17

PostPosted: Tue Jul 14, 2009 5:56 am    Post subject: Reply with quote

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

Code:
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?
Back to top
View user's profile Send private message
dackjaniels



Joined: 24 Feb 2009
Posts: 151

PostPosted: Tue Jul 14, 2009 8:33 am    Post subject: Reply with quote

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. Smile

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:
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 Embarassed

Regards,
Steve
Back to top
View user's profile Send private message
Matt_mm



Joined: 07 Jun 2009
Posts: 17

PostPosted: Tue Jul 14, 2009 10:37 pm    Post subject: Reply with quote

Thanks Dackjaniels!

That should solve my last problems.

Thanks Mike too!
Back to top
View user's profile Send private message
dackjaniels



Joined: 24 Feb 2009
Posts: 151

PostPosted: Wed Jul 15, 2009 11:27 am    Post subject: Reply with quote

Glad I could help Matt Smile
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Forex Tester Forum (Professional Training Software for Traders) Forum Index -> FT API All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You can attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group