Question - Answer

Notification System.NotImplementedException: It doesn't possible

Issued when using Fictitious Execution and all types of orders, except for "By Market" orders.

For example: System.NotImplementedException: It doesn't possible. at TSLab.ScriptEngine.PositionsList.OpenIfGreater

Fictitious execution and conditional order are used, Open if greater. Fictitious execution works only with "By Market" orders. Please use them.

Interpolating strings

TSLab recognizes string interpolation in code starting from version 2.1. Doesn't work in earlier versions, the script will throw an error, so use string formatting.

An example of string interpolation:

int n = 100;
string msg = $"Result: {n}";

A similar entry through formatting:

int n = 100;
string msg = string.Format("Result {0}", n);

How to work with events?

The ctx.Runtime.LastRecalcReasons property contains a list of the script recalculation reasons. There may be several of them.

An example of how to get the reason for the recalculation of the script (displayed in the log):

var listReasons = ctx.Runtime.LastRecalcReasons.Select(x => x.Name).Distinct().ToList();
var listReasonsSt = string.Join(", ", listReasons);
ctx.Log($"RecalcReasons: " + listReasonsSt, MessageType.Info, true);

An example of how to get the event of opening a position (displayed in the log):

if (ctx.Runtime.LastRecalcReasons.Any(x => x.Name == EventKind.PositionOpening.ToString()))
{
    ctx.Log($"PositionOpening!", MessageType.Info, true);
}

In the agent settings, the Events -> Position opened checkbox must be enabled, then the script will be recalculated when a position is opened. All possible events are listed in the EventKind enum.

Information in quotes is not received

Some brokers do not broadcast information about the price step when the quotes are updated (UpdateQuote Event)

Accordingly, in the API, for example:

tickPrice = (double) rtSymbol.FinInfo.StepPrice / rtSymbol.Tick; 

it is impossible to pull out the price step from ITInvest. This line will work for other brokers. FinInfo.StepPrice has the same properties as FinInfo.BuyDeposit, BuyDeposit will work, StepPrice is not, the broker simply does not send it.

It is easy to check what exactly the broker sends in quotes using the Quotes window. View - Quotes. Select the instruments of interest by right-clicking. If there is no data in the table, then there will be no data in the API.

Do I need to get Close, Open, High, Low through Contex.GetData

Close, Open, High, Low via Contex.GetData? or are the symbol.GetClosePrices (context) methods already cached?

===

Does not matter. Within the GetClosePrices, GetOpenPrices, ... methods there is a call to context.GetDat

Referring via COM

There is no communication via COM. You can write scripts in api c #, agents have access to their deals.

Order queue

The ISecurity API has methods:

  • IReadOnlyList GetBuyQueue (int barNum);

  • IReadOnlyList GetSellQueue (int barNum);

How UpdateQueueData () works

This function resets the cache and forces the order manager data to be re-requested. This is done - to reduce the load. It turns out that in order to get the latest information, you need to make a call to UpdateQueueData before each call to GetBuyQueue / GetSellQueue

  1. The order manager is “fixed” at the time of recount to ensure that the “mold” of the order manager (both parts of it) is taken at the moment the recount begins.

  2. In order to update the order manager during the recount and get a new impression, this method was made. Not related to performance.

Get server time

    public class ServerTime : IExternalScript
    {
        public void Execute(IContext ctx, ISecurity sec)
        {
            var time = GetServerTime(sec);
            ctx.Log($"Server time: {time?.ToString()}", MessageType.Info, true);
        }

        private DateTime? GetServerTime(ISecurity sec)
        {
            var con = sec.SecurityDescription.TradePlace.DataSource as IConnectable;
            return con?.ServerTime;
        }
    }

Forced script recalculation

Method for forced recalculation of the script:

IContext.Runtime.Recalc (string recalcReason, IDataSourceSecurity dataSourceSecurity, bool inAnyCase = fals)

  • recalcReason - recalculation reason, any string.

  • dataSourceSecurity - the source from which the recalculation is started. You can pass null.

  • inAnyCase - perform recalculation anyway. If false is specified, then recalculation will be performed only if the current recalculation is completed (used in metronome cubes). If true is specified, then recalculation will be performed immediately after the current recalculation.

Call example:

ctx.Runtime.Recalc ($ "MyRecalc", null, true); 

For example, the current reason for recalculating the script is read, if the script is not launched through the Recalc method, then Recalc is launched, waits for 5 seconds and ends. After completion, a new recalculation is started, it will indicate that the current recalculation reason is "MyRecalc" and will not be called Recalc again.

public void Execute(IContext ctx, ISecurity sec)
{
	var listReasons = ctx.Runtime.LastRecalcReasons.Select(x => x.Name).Distinct().ToList();
	var listReasonsSt = string.Join(", ", listReasons);
	ctx.Log($"RecalcReasons: " + listReasonsSt, MessageType.Info, true);

	if (!listReasonsSt.Contains("MyRecalc"))
	{
		ctx.Log($"Start recalc", MessageType.Info, true);
		ctx.Runtime.Recalc($"MyRecalc {DateTime.Now}", null, true);

		for (int i = 1; i <= 5; i++)
		{
			Thread.Sleep(1000);
			ctx.Log($"Step {i}", MessageType.Info, true);
		}
	}
}

Working with the cache

"I save the variable to the cache:

int qty = 1; ctx.StoreObject ( key, qty); 

the next time the script is recalculated, I load:

object fv = ctx.LoadObject ( key); 
if (fv! = null) volume = (int) fv; 
else ctx.Log ("Volume not in cache!", MessageType.Warning);

And I always get a message stating that there is nothing in the cache for this key. "

===

You need to use the NotClearableContainer class and store the objects in the cache, not the value types, because the boxing and unboxing operations are obtained.

class ScriptUserData
{
 public int Qty { get; set; }
}
...
Context.StoreObject(cashKey, new NotClearableContainer(new ScriptUserData{ Qty = qty }));
var cacheObj = m_context.LoadObject(cashKey);
if ((cacheObj is NotClearableContainer container) && (container.Content is ScriptUserData data))
{
 var qty = data.Qty;
}

Slippage handling

In the code, you can specify slippage when working with a position:

Set stop with slippage of 100:

pos.CloseAtStop (i + 1, price, 100, "LX");

Place a take with a slippage of 100:

pos.CloseAtProfit (i + 1, price, 100, "LX"); 

Important: One slippage value must be used in the script. For example, set a stop (CloseAtStop) and a take (CloseAtProfit). In both cases, you must pass one slip value to these methods. If they are different, then the latter will be applied for both applications. If slippage is not specified in the code, it will be applied from the agent settings.

Script from the program code generator

In order to view the .cs file from the program code generator, you need to:

Open in the program: File -> Program Settings -> Script Optimization -> Script debugging

Create a script and compile it. The .cs file will be available in the temp folder. It is not difficult to get into it from the program. Tools -> Log Folder -> Go up one level higher, on the same level with the logs folder, temp folder. An example of creating a script in the visual program editor and viewing the C # (CS file) of this script from the code generator.

Create a container with your dll

An encrypted container can contain a dll library inside itself and the script will work with it. But one condition, the script should not call statics of the field or methods from the library.

Cannot compare two variables "double"

Two double variables cannot be compared. You can see about this on the Internet.

==================================

Hello. Faced a problem in the API. The algorithm should check the relevance of the order price and cancel it if the price is not up to date. Here's a real-world example.

The price of an active order in real time is 1332.8

1️⃣ If this code is executed, everything works correctly - the application is not canceled, a new one is not placed

double BuyPrice = 1332.8;
foreach (IOrder MyOrder in SymbolRt.Orders) 
{ 
    if (MyOrder.IsActive) 
    { 
        if (MyOrder.Price == BuyPrice) return; // Output 
        else SymbolRt.CancelOrder (MyOrder); // Cancel order 
    }
} 
// Then the conditions for placing new orders are checked. 
if (,,,,,,,,,,,,,,,,) 
{ 
    SymbolRt.NewOrder (OrderType.Growth, true, BuyPrice, 1, "example"); 
}

2️⃣ If this code is executed, the order is ALWAYS canceled, and then EXACTLY the same with the same price is placed

double BuyPrice = 1332.9 - 0.1;
foreach (IOrder MyOrder in SymbolRt.Orders) 
{ 
    if (MyOrder.IsActive) 
    { 
        if (MyOrder.Price == BuyPrice) return; // Output 
        else SymbolRt.CancelOrder (MyOrder); // Cancel order 
    } 
} 
// Then the conditions for placing new orders are checked. 
if (,,,,,,,,,,,,,,,,) 
{ 
    SymbolRt.NewOrder (OrderType.Growth, true, BuyPrice, 1, "example"); 
}

3️⃣ At the same time, if the variable is made in this way, then everything works correctly.

double BuyPrice = (1332.9 * 10 - 1) / 10 

That is, for some reason a problem arises when the subtraction and addition of decimal fractions occurs - after that the two variables are not compared correctly

Last updated