1.    Easy Language Extension ( ELX ) DLL

ELX is a Microsoft Windows DLL written and maintained by Easy Language staff for the TradeStation community at large.  It is a vehicle for delivering functionality that is not otherwise provided by the EasyLanguage workspace library. 

Currently, ELX contains the following modules:

1.  Buffers -  a simple facility for sending and receiving global time stamped data between cooperating indicators, strategies, and other analytic procedures. 

2.  Records – a simple and efficient facility for maintaining persistent data between sessions.  Each record is stored as a text file using a random access, key and value format.

1.1        ELX Initialization

Most ELX function groups require an application to initialize an internal context before calling any other function in the group.  A single function named UseELX performs initialization for all groups.  UseELX should be called once, usually on the first bar, and usually by the main routine.  The current version is returned.

1.2        Bridge Functions

Every DLL C function has a corresponding EasyLanguage “bridge” function.  Bridge functions encapsulate the somewhat foreign syntax associated with calling DLL functions, and provide a natural EL interface for an application.  Bridge functions are documented in the reference section that follows.

2.    Buffers

ELX buffers provide a simple facility for sharing global data in one or more EasyLanguage programs.  Programs may be in the same chart or in different charts.

Buffers are typed and ordered.  Every item in a particular buffer has the same data type, and each item is time stamped with date and time in the same way that price bars are time stamped.  Items are retrieved in the same order that they are written.  Thus, a buffer conforms to the concept of a first-in-first-out ( “fifo” ) queue.


A typical buffer application involves two programs.  One is designated to be a “sender” while the other is a designated to be a “receiver”.  A sender writes items to a buffer and a receiver reads items from the same buffer.  Items written to a buffer are accumulated until the receiver reads them.  Reading an item removes it from the buffer.

A buffer must be opened before it can be used.  To open a buffer, both sender and receiver must specify a unique name, a data type, and a scale factor.  Scale factor determines how many identical and contiguous buffers to create ( see
Opening A Buffer ).  Both programs receive an integer “handle” from ELX that is used to identify the buffer in subsequent calls.

To effectively use buffers in an application, the EasyLanguage programmer must be aware of the asynchronous relationship between sender and receiver.  TradeStation controls the execution order of studies and strategies and does not formally define that order.  Nor does EasyLanguage provide a mechanism to otherwise regulate ordering.  Thus, an application cannot invent a synchronous protocol that requires a sender to send before a receiver receives.  Consequently, when two programs cooperate to transfer data through a buffer, synchronization becomes an important issue.


As a practical alternative to real synchronization, EL programs can use time stamps to effect logical synchronization.  Buffers accommodate this kind of synchronization automatically.  As items are written to a buffer, ELX tags them with current date and time.  As items are read, the current date and time are used to delimit the read operation.  Current date and time is the date and time associated with the current bar. 

Items retrieved by a receiving program are never logically ahead of the receiver’s current bar.  However, it is possible, indeed likely, that items will “lag” behind the current bar.  That is, that the date and time of a retrieved item may precede the date and time of the current bar.

Lag is an important issue that every application must address individually.  There is not a generally suitable solution for all.  Tolerance for lag, and reaction to it, will depend on many factors.  However, the possibility for arbitrarily large lag will present insurmountable problems for any application.  Fortunately, there are practical ways to control and limit lag a priori, making cooperative applications feasible.  See
Synchronization Issues for more information.

2.1        Opening A Buffer

Before a buffer can be used, an application must call OpenBuffer to initialize it and gain access to it, usually on the first bar.  OpenBuffer requires a specification of name, type, and scale.  An integer valued handle is returned to identify the buffer in subsequent calls.  Zero is an invalid handle value, and indicates an error.

Buffers are initially identified by unique global names.  Since an application may employ many buffers, and since there may be many applications using ELX, a well designed application must give careful consideration to the generation of buffer names.  An application can call CheckBuffer to determine if a buffer name is already in use.   

There are five distinct buffer types corresponding to the five EasyLanguage types:  double, float, integer, string, and truefalse ( aka boolean ).  Float is provided for compatibilty, but its use is deprecated.

A contiguous array of identical buffers may be created by specifying a scale factor greater than one.  Each individual buffer in an array is referenced by adding an offset to the handle value returned by OpenBuffer.  For example, if BH is a buffer handle, then (BH + 0) identifies the first buffer, while (BH + N – 1) identifies the Nth buffer.

It is common for two programs to share a buffer.  Both programs should call OpenBuffer using the same specification.  The program that first calls OpenBuffer will create the buffer as specified.  The second program locates the buffer by name and confirms type and scale.

In exchange for access flexibility, an application must specify how to handle queued items in an existing buffer.  Logically, queued items may be pending or obsolete.  A true-false value named Flush is used to specify the proper action.  If Flush is true, the items are obsolete, and they will be discarded.  Otherwise, Flush is false, and they will be treated as pending. 

There are numerous scenarios to consider in general, but for specific cases the choice is usually clear.  When two programs are involved, one specifies Flush true and the other specifies Flush false.  The choice of true or false depends on whether the buffer is unidirectional or bidirectional.  If the buffer is unidirectional, sender specifies true, while receiver specifies false.  In the buffer is bidirectional, responsibility is arbitrary.  When one program is involved, Flush is false.  

Buffers are designed to support well behaved and cooperative applications.  They are not “secure” or protected from malicious or careless practices.  The application should employ suitable naming conventions and correctly use and compute handle values.

2.2        Reading and Writing a Buffer

A sending program can add items to a buffer by calling one function from a class of functions named WriteBuffer.  A receiving program can retrieve items by calling one function from a class of functions named ReadBufferNext or ReadBufferSync.  Each function class is comprised of five related functions, one for each data type.  Individual functions are distinguished from one another by a single letter suffix that identifies the type of data it reads or writes.

The calling convention for read and write functions is similar.  Each takes a buffer handle and a value argument.  Each returns an integer to indicate the number of items transferred ( zero or one ).  The value argument has a type that matches the type of the function.  Write values are inputs, while Read values are outputs.  Read functions also have an additional output for specification of lag.

Sending data to another cooperating program is pretty straightforward.  Simply call WriteBuffer, specifying the value to be sent.  The value is automatically time stamped using the current date and time for a specified or default data series.  WriteBuffer returns zero if there was an error, otherwise it returns one.

Receiving data is a little trickier due to synchronization and lag issues.  ReadBufferNext reads at most one item from a buffer ( the next one ), while ReadBufferSync may read over several items as it compares the date and time of buffered items to the current date and time.  Both functions return one if an item is in fact copied.  In this case, the value argument is set to the retrieved value, and the lag argument defines the difference in seconds between the buffered date/time and the current date/time.  If no item is copied ( because the buffer is logically empty or there is an error ), both functions return zero and output arguments are not modified.

Lag times in seconds may be converted to a number of bar intervals using the function BarLag.  Bar lags are not meaningful for tick data.  Also, because time lag is an elapsed time, bar lag is only definite for minute, day, and week bars.  The application must provide its own conversion for longer intervals if required.  Bar lag is rounded up to the next bar when there is a fractional time lag. If there is an error, BarLag returns zero.

2.3        Synchronization Issues

Although there is no precise control over execution order in a program group, there are certain things an application can and should do to facilitate communication with buffers. 

First, determine the “natural” execution order in a set of cooperating programs.  This order is determined by analyzing the send and receive  relationship among individual programs.  A sending program will precede its receiving program.  Programs should be inserted into their charts in natural order.  Command macros may be useful for automating insertion.  An alternative, and perhaps preferable way, is to create a workspace with all programs inserted but disabled ( status set to “off” ).  Then, each program can be enabled in proper order. 

Most receiving programs should call ReadBufferSync immediately after OpenBuffer.  This “stages” the program for regular bar processing where lag times are assumed to be nominal.

Provided individual programs are enabled and staged in proper order, lag times will be nominal for both historical and real-time data.  This means that items read synchronously will be close enough to the current bar that they can be considered “timely”.  A particular application however, must decide what a normal lag is, based on the relative time frames of sender and receiver, and adjust accordingly.

2.4        Suggested Uses

Buffers are especially useful for coupling studies and strategies.  A study can perform calculations using analytically meaningful intervals, while updating and transmiting signals to a strategy tick-by-tick.  Conversely, a strategy can transmit information to a study for plotting.

One example of this technique is in the application of “pairs” trading.  In this intraday scenario, an indicator compares the price of two related securities in a bar chart, then sends buy and sell signals to separate strategies for execution.  A generic receiving strategy is inserted into two different tick charts to trade one security each.  If the securities are fairly active with respect to the bar interval, lag times in the receivers will be nominal because the signals are received and processed on the next tick.  Pairs trading by this method allows back-testing of the strategy and thus has an advantage over alternate methods ( e.g. order bar macros ).

Buffers are also useful for maintaining intrabar calculations.  When a program is running with “Update Every Tick” enabled, iterative calculations can be performed by passing values in a buffer from one tick cycle to the next.


3.  Records

ELX records provide a simple and effective way to manage inter-session data.  Records are best suited for storing moderate amounts of unstructured or loosely structured data. 

 

Logically, a record is a randomly ordered set of entries.  Each entry is stored in a text file using a “key and value” format, one entry per line ( see Record File Format ).  To access an entry for input or output, an application specifies its unique key.  Values are automatically converted from EasyLanguage data types to text during output, and from text back to EasyLanguage types during input.

 

By using indexed or regular key schemes, an application can represent fairly sophisiticated data structures in a record.  For example, it would not be too difficult to store a multidimensional array structure.  However, records are perhaps best suited for simpler applications, like storing individual security or analysis profiles.

 

To create or use a record, an application must first open it ( see Opening a Record ).  A unique file name identifies the record.  A full path may be specified, but when no path is specified, the default path designates a TradeStation subfolder named \MyWork\Records.  A program that opens a record receives an integer valued “handle” to identify the record in subsequent function calls.  More than one program may open the same record.

After a record has been opened, individual entries may be created, queried, and updated ( see Reading and Writing a Record ).  Typically, values are read into an EL program on the first bar to initialize local variables, then, as the program cycles on the last bar, record values that change are written back to the record, for automatic saving when the program is paused or removed.  

 

Although changes to a record are saved automatically when an EasyLanguage program terminates, changes can also be saved explicitly while the program runs ( see Saving a Record ).  Explicit saving allows an application to guarantee the contents of a record at important milestones.  Also, explicit saving allows an application to refresh file contents periodically to support a rudimentary form of interprocess communication.

 

3.1        Record File Format

Records are stored in text files using a simple key and value format.  Each record entry occupies a single line in the file.  In general, an entry has the form

Key = Value

where Key and Value are arbitrary text.  The first ( leftmost ) occurance of an equal ( = ) character separates Key from Value.

Not every line constitutes a valid or complete record entry.  Blank lines are not translated, and if a line doesn’t have an equal character, it is treated as an entry without a value.

In order to facilitate comparing and sorting, entry keys are subparsed into distinct words.  Two keys are equal if they are comprised of the same words.  Thus, the key “My dog has fleas” is equivalent to “My   dog   has    fleas”.  Also, keys are not case sensitive.  Thus, “My DOG has FLEAS” is the same as “my dog has fleas”.

Even though blank lines and key spaces are not logically significant, they are maintained for output.  Original order and spacing of lines is preserved.  New entries are appended to the end.  If a value is modified, the new value appears in the same location as the value it replaced. 

There are built-in size limits for a record file.  Currently, a line may have up to 1000 characters, and a file may have up to 5000 lines.                  

 

3.2        Opening a Record

Before a record can be used, an application must call OpenRecord to configure the record and make it available for reading and writing.  If the record file already exists, it is opened and parsed into individual entries as described above.

OpenRecord returns an integer valued access handle which is used to identify the record in subsequent calls. 

An open record resides in memory.  Changes, if any, are applied to the  memory image and do not become permanent until the record is explicitly or automatically saved.  

More than one EL program may open the same record.  Each program shares access to the same memory image.  Changes made by one are visible to others.  However, there are no special synchronization mechanisms for records, so an application must manage this issue separately.


3.3        Reading and Writing Records

To create a record entry, or to update the value of an entry, an application calls one function from a class of functions named WriteRecord.  To read values from an existing entry, an application calls one function from a class of functions named ReadRecord.  Each function class is comprised of five related functions, one for each EasyLanguage data type.  Individual functions are distinguished from one another by a single letter suffix that identifies the type of data it reads or writes.

The calling convention for read and write functions is similar.  Each takes a record handle, a key, and a value argument.  Each returns an integer to indicate the number of values transferred ( zero or one ).  The value argument has a type that matches the type of the function.  Write values are inputs, while Read values are outputs. 


 

3.4        Saving a Record

Changes made to a record are automatically saved when the EL program is turned off or removed from its chart.  However, in some applications it may be necessary or desirable to explicitly save a record before it would otherwise be saved automatically.  This can be accomplished by calling SaveRecord.  Saving a record guarantees that logical changes made to a record will become permanent.


Function:

 

      UseELX – create an ELX context for an EL program

 

Usage:           

     

      Version = UseELX( EnableTraps );

 

Interface:

 

      input truefalse EnableTraps

     

            { false, true }

 

return int Version

 

            { encoded value }

 

Protocol:

 

      random ( simple )

 

Description:

 

UseELX creates an internal context, associates it with a calling EL program, and returns the current version number.

 

Most ELX functions require that UseELX be called as a prerequisite.  Typically, UseELX is called from an indicator, strategy, or some other top level routine, on the first bar.

 

EnableTraps specifies a mode for runtime error handling.  If EnableTraps is true, runtime error traps are enabled, otherwise they are not.  When error traps are enabled, ELX functions will raise error events that cause the EasyLanguage program to halt and display an event message.  When error traps are not enabled, processing continues. 

 

NOTE:  In either case, ELX functions validate their inputs and return specific values to indicate an error.

 

NOTE:  Normally, enabling error traps is a good idea.  However, runtime error handling is not yet fully implemented in the TradeStation platform, and its use sometimes leads to unexpected or misleading results.  

 

Version is an encoded three level version identifier of form XXYYZZ, where XX identifies major version, YY identifies minor version, and ZZ is a minor version serial number.     

 

Errors:

 

      none

 

Association:

 

      all


Function:

 

      CheckBuffer – check ELX buffer name

 

Usage:           

     

      InUse = CheckBuffer( Name );

 

Interface:

 

      input string Name

 

            A name chosen by the application

 

return truefalse InUse

 

            { false, true }

     

Protocol:

 

      random ( simple )

 

Description:

 

CheckBuffer returns true if a specified buffer is open.

 

ELX must be initialized prior to calling CheckBuffer.  See UseELX.

 

If there are errors, InUse will be false.  If ELX error traps are are enabled, the program will be disabled on the current bar.

 

Errors:

 

      Indication:  InUse = false

 

      Void Context ( ELX not initialized )

 

Association:

 

      UseELX, OpenBuffer


Function:

 

      OpenBuffer - open an ELX buffer

 

Usage:           

     

      BH = OpenBuffer( Name, Type, Scale, Flush );

 

Interface:

 

      input string Name

 

            A unique name chosen by the application

 

input string Type

 

            {

              { "numeric", "double" },

  { "int", "integer" },

  { "float },

  { "string", "text" },

  { "truefalse", "boolean", "bool" }

            }

 

      input int Scale

 

            { 1, ..., 256 }

 

      input truefalse Flush

 

            { false, true }

 

      return int BH

 

            { 0, ... }

 

Protocol:

 

      random ( simple )

 

Description:

 

OpenBuffer opens an ELX buffer according to given specifications and returns an integer valued access handle which identifies the buffer in subsequent calls to related functions.

     

Name uniquely identifies the buffer.  Buffer names are not case sensitive.  The application should employ a naming convention that reduces the likelihood of unintentional match.   

 

NOTE!  Due consideration should be given to this issue.  Accidental matching of buffers can lead to errors that are difficult to resolve.

     

Type specifies the type of data that will be buffered.  All items in a buffer have the same type.  There are five distinct types.  Some types have more than one expression and expressions are case insensitive. 

 

Scale specifies the number of related parallel buffers to allocate.  Each parallel buffer may be referenced in subsequent calls using the returned buffer handle plus an offset.  The first buffer is referenced by (BH + 0), the second by (BH + 1), and so forth up to the last buffer which is referenced by (BH + Scale - 1). 

 

NOTE!  Do not depend on any arithmetic relationship between handles returned by separate calls to OpenBuffer.

 

Flush specifies a disposition for items already in the buffer.  If Flush is true, buffered items, if any, are discarded.

 

NOTE:  When a buffer conveys strictly in one direction, i.e. from sender to receiver, the sender should specify true, and the receiver should specify false.  In bi-directional applications of two programs, one should specify true and the other false.  Otherwise, in single program  applications, Flush should be false.

 

ELX must be initialized prior to calling OpenBuffer.  See UseELX.

 

OpenBuffer may be called more than once for a particular buffer.  The first time OpenBuffer is called with a given Name, it creates a buffer with specified attributes.  In subsequent calls using the same Name, attributes are confirmed.  If the buffer already exists, and if Flush is true, OpenBuffer will discard any previously queued items.    

 

If there are errors, BH is zero.  Otherwise, BH is greater than zero.

 

Errors:

 

Indication: BH = 0

 

      Void Context ( ELX not initialized )

Invalid Specification ( invalid Type or Scale value )

Incompatible Type ( buffer exists but Type did not match )

 

 

Association:

 

      UseELX, WriteBuffer, ReadBufferNext, ReadBufferSync


Function:

 

      WriteBuffer – write data to an ELX buffer

 

Usage:           

     

      Out = WriteBuffer.d( BH, ValueD ) dataN;

Out = WriteBuffer.f( BH, ValueF ) dataN;

      Out = WriteBuffer.i( BH, ValueI ) dataN;

      Out = WriteBuffer.s( BH, ValueS ) dataN;

      Out = WriteBuffer.b( BH, ValueB ) dataN;

 

Interface:

 

      input int BH

 

            { 1, ... }

 

      input double ValueD

 

            { any double precision float }

 

      input float ValueF

 

            { any single precision float }

 

      input int ValueI

 

            { any integer }

 

      input string ValueS

 

            { any string }

 

      input truefalse ValueB

 

            { false, true }

 

      literal dataN ( optional )

 

            { data1, ..., data50 }

 

return int Out

 

            { 0, 1 }

     

Protocol:

 

      random ( simple )

 

Description:

 

WriteBuffer copies a specified value to the end of a specified ELX buffer and returns the number of items written. 

 

An application must call a version of WriteBuffer that corresponds to the data type specified when the buffer was created.  Data type is indicated by the last letter of the function name. 

 

NOTE!  An application must call the exact function that corresponds to buffer type.  There are no implicit type conversions.

 

BH must be a valid buffer access handle equal to, or calculated from, the value returned by OpenBuffer. 

 

NOTE!  ELX can only validate the value of BH in a superficial way.  If BH references an existing type compatible buffer it will be accepted, even if the buffer was not created by the same application.  This implies that ELX buffers are not “secure.”  A malicious or careless application can create problems that are difficult to detect.

 

The value written is stamped with the current date and time.  Current date and time is the date and time of the current bar in the data stream specified by dataN, or the default data stream, if none is specified.

 

If there are errors, Out is zero.  Otherwise, Out is one.

 

Errors:

 

      Indication:  Out = 0

 

      Void Context ( ELX not initialized )

      Invalid Handle ( buffer does not exist )

      Incompatible Type ( Value type does not match buffer type )

 

Association:

 

      UseELX, OpenBuffer, ReadBufferSync, ReadBufferNext

 


Function:

 

      ReadBufferSync – read from an ELX buffer

 

Usage:           

     

      In = ReadBufferSync.d( BH, ValueD, Lag ) dataN;

In = ReadBufferSync.f( BH, ValueF, Lag ) dataN;

      In = ReadBufferSync.i( BH, ValueI, Lag ) dataN;

      In = ReadBufferSync.s( BH, ValueS, Lag ) dataN;

      In = ReadBufferSync.b( BH, ValueB, Lag ) dataN;

 

Interface:

 

      input int BH

 

            { 1, ... }

 

      output double ValueD

 

            { any double precision float }

 

      output float ValueF

 

            { any single precision float }

 

      output int ValueI

 

            { any integer }

 

      output string ValueS

 

            { any string }

 

      output truefalse ValueB

 

            { false, true }

 

      output double Lag

 

            { 0, ... }

 

      literal dataN ( optional )

 

            { data1, ..., data50 }

 

return int In

 

            { 0, 1 }

     

Protocol:

 

      random ( simple )


Description:

 

ReadBufferSync copies the next logically coincident item from a specified ELX buffer, assigns a “lag” value, and returns the number of items read.

 

An application must call a version of ReadBufferSync that corresponds to the data type specified when the buffer was created.  Data type is indicated by the last letter of the function name. 

 

NOTE!  An application must call the exact function that corresponds to buffer type.  There are no implicit type conversions.

 

BH must be a valid buffer access handle equal to, or calculated from, the value returned by OpenBuffer. 

 

NOTE!  ELX can only validate the value of BH in a superficial way.  If BH references an existing type compatible buffer it will be accepted, even if the buffer was not created by the same application.  This implies that ELX buffers are not “secure.”  A malicious or careless application can create problems that are difficult to detect.

 

ReadBufferSync reads items in order until it encounters an item that has date and time greater than or equal to the current date and time.  If there are no coincident or lagging items, ReadBufferSync returns zero and output variables are not modified.  Otherwise, ReadBufferSync copies an item value to Value, sets Lag to the time difference, and returns one. 

 

NOTE:  Current date and time is the date and time of the current bar in the data stream specified by dataN, or the default data stream, if none is specified.

 

NOTE:  Reading is a “destructive” operation.  Items are removed from the buffer as they are read.

 

Conceptually, ReadBufferSync scans through a buffer looking for the latest item in time that does not occur after the current date and time.  If the lastest item has a date and time that precedes the current date and time, Lag will be greater than zero.  Otherwise, the item is coincident, and Lag will be zero.  If multiple items have the same coincident date and time, the first one synchronizes the read operation.  Lag is expressed in seconds. 

 

NOTE:  Use BarLag to express a given time lag as a number of bar intervals. 

 

If there are no items to read or if there is an error, In will be zero. Otherwise, In will be one.  The two cases cannot be distinguished when In is zero.  However, in the error case, if ELX error traps are enabled, the program will be disabled on the current bar.

 

Errors:

 

      Indication:  In = 0

 

      Void Context ( ELX not initialized )

      Invalid Handle ( BH does not identify a buffer )

Incompatible Type ( Value type does not match buffer type )

     

Association:

 

      UseELX, OpenBuffer, ReadBufferNext, BarLag

 


Function:

 

      ReadBufferNext – read from an ELX buffer

 

Usage:           

     

      In = ReadBufferNext.d( BH, ValueD, Lag ) dataN;

In = ReadBufferNext.f( BH, ValueF, Lag ) dataN;

      In = ReadBufferNext.i( BH, ValueI, Lag ) dataN;

      In = ReadBufferNext.s( BH, ValueS, Lag ) dataN;

      In = ReadBufferNext.b( BH, ValueB, Lag ) dataN;

 

Interface:

 

      input int BH

 

            { 1, ... }

 

      output double ValueD

 

            { any double precision float }

 

      output float ValueF

 

            { any single precision float }

 

      output int ValueI

 

            { any integer }

 

      output string ValueS

 

            { any string }

 

      output truefalse ValueB

 

            { false, true }

 

      output double Lag

 

            { 0, ... }

 

      literal dataN ( optional )

 

            { data1, ..., data50 }

 

return int In

 

            { 0, 1 }

     

Protocol:

 

      random ( simple )


Description:

 

ReadBufferNext copies the next item from a specified ELX buffer, assigns a “lag” value, and returns the number of items read.

 

An application must call a version of ReadBufferNext that corresponds to the data type specified when the buffer was created.  Data type is indicated by the last letter of the function name. 

 

NOTE!  An application must call the exact function that corresponds to buffer type.  There are no implicit type conversions.

 

BH must be a valid buffer access handle equal to, or calculated from, the value returned by OpenBuffer. 

 

NOTE!  ELX can only validate the value of BH in a superficial way.  If BH references an existing type compatible buffer it will be accepted, even if the buffer was not created by the same application.  This implies that ELX buffers are not at all “secure.”  A malicious or careless application can create problems that are difficult to detect.

 

ReadBufferNext reads the next item that does not occur after the current date and time. If there are no items to read, ReadBufferNext returns zero and output variables are not modified.  Otherwise, ReadBufferNext copies the item value to Value, sets Lag to the time difference, and returns one. 

 

NOTE:  Current date and time is the date and time of the current bar in the data stream specified by dataN, or the default data stream, if none is specified.

 

NOTE:  Reading is a “destructive” operation.  Items are removed from the buffer as they are read.

 

If the next item has a date and time that precedes the current date and time, Lag will be greater than zero.  Otherwise, the item is coincident, and Lag will be zero.  Lag is expressed in seconds. 

 

NOTE:  Use BarLag to express a given time lag as a number of bar intervals. 

 

If there are no items to read or if there is an error, In will be zero. Otherwise, In will be one.  The two cases cannot be distinguished when In is zero.  However, in the error case, if ELX error traps are enabled, the program will be disabled on the current bar.

 

Errors:

 

Indication:  In = 0

 

      Void Context ( ELX not initialized )

Invalid Handle ( BH does not identify a buffer )

Incompatible Type ( Value type does not match buffer type )

     

Association:

 

      UseELX, OpenBuffer, ReadBufferSync, BarLag


Function:

 

      BarLag – compute lag in bars given lag in seconds

 

Usage:           

     

      Bars = BarLag( TimeLag, BarType, BarInterval );

 

Interface:

 

      input double TimeLag

 

            { 0, ... }

 

      input int BarType

 

            { 2 = minute, 3 = daily, 4 = weekly }

 

      input int BarInterval

 

            { 1, ..., 1440 }

 

return int Bars

 

            { 0, ... }

     

Protocol:

 

      random ( simple )

 

Description:

 

BarLag returns a number of bars given a time lag in seconds, bar type, and bar interval.

 

TimeLag is a value in seconds determined by ReadBufferNext or ReadBufferSync.

 

If BarType is 1, BarInterval specifies the number of minutes in the bar.  Otherwise, BarInterval is ignored.

 

BarLag always returns a whole bar number.  If the actual bar calculation is fractional, the result is rounded up.  Thus, BarLag bounds the given time lag.

 

If there are errors, Bars is zero.  Otherwise, Bars is greater than zero. 

 

Errors:

 

      Indication:  Bars = 0

 

      Invalid Specification ( invalid TimeLag, BarType, or BarInterval )

 

Association:

 

      ReadBufferSync, ReadBufferNext


Function:

 

      OpenRecord - open an ELX record

 

Usage:           

     

      RH = OpenRecord( Name );

 

Interface:

 

      input string Name

 

            A unique name chosen by the application

 

      return int RH

 

            { 0, ... }

 

Protocol:

 

      random ( simple )

 

Description:

 

OpenRecord opens an ELX record and returns an integer valued access handle which identifies the record in subsequent calls to related functions.

     

Name uniquely identifies the record.  Record names must conform to file naming rules.  If Name includes a proper path, the record file will be placed in the designated folder.  Otherwise, a default path is used to place the file.

 

NOTE:  The default path designates a TradeStation subfolder named \MyWork\Records. 

If the specified record is already open, an access handle to the open record is returned.


If the specified record is not already open, and if the specified record file exists, OpenRecord reads and parses the file into individual entries, one entry per line.  In general, each entry has one key and one value.  Key is separated from value by the leftmost equal ( = ) character.  A line with no separator and at least one nonblank character is treated as an entry without a value.

 

Empty lines, and other explicit formatting features are maintained for output. 

 

After the record has been parsed into memory, the source file is closed.

 

ELX must be initialized prior to calling OpenRecord.  See UseELX.

 

Size limits restrict line length to 1000 characters, and lines per file to 5000.

 

If there are errors, RH is zero.  Otherwise, RH is greater than zero.


Errors:

 

Indication: RH = 0

 

      Void Context ( ELX not initialized )

Invalid Specification ( invalid Name )

LimitViolation ( truncated line or file )

Association:

 

      UseELX, WriteRecord, ReadRecord, SaveRecord


Function:

 

      WriteRecord – write a value to an ELX record

 

Usage:           

     

      Out = WriteRecord.d( RH, Key, ValueD );

Out = WriteRecord.f( RH, Key, ValueF );

      Out = WriteRecord.i( RH, Key, ValueI );

      Out = WriteRecord.s( RH, Key, ValueS );

      Out = WriteRecord.b( RH, Key, ValueB );

 

Interface:

 

      input int RH

 

            { 1, ... }

 

      input string Key

 

            a unique key chosen by the application

 

      input double ValueD

 

            { any double precision float }

 

      input float ValueF

 

            { any single precision float }

 

      input int ValueI

 

            { any integer }

 

      input string ValueS

 

            { any string }

 

      input truefalse ValueB

 

            { false, true }

 

      return int Out

 

            { 0, 1 }

     

Protocol:

 

      random ( simple )

 

Description:

 

WriteRecord formats an entry for a specified record and returns the number of values formatted. 

 

RH must be a valid record access handle returned by OpenRecord.

 

Key identifies a specific record entry.  If an entry with the same Key does not already exist, a new entry is created.  Otherwise, the entry’s current value is replaced.

 

NOTE:  Key is parsed into individual words for comparison.  More than one contiguous space or tab character is not significant.

 

Value is the new entry value.  If Value is not a string type, it will be converted to equivalent text in the entry. 

 

An application must call a version of WriteRecord that is compatible with the data type of Value.  Data type is indicated by the last letter in the function name.  If Value is a numeric type, EasyLanguage will automatically convert it to match the function type.

 

If there are errors, Out is zero.  Otherwise, Out is one.

 

Errors:

 

      Indication:  Out = 0

 

      Void Context ( ELX not initialized )

      Invalid Handle ( record does not exist )

     

Association:

 

      UseELX, OpenRecord, ReadRecord


Function:

 

      ReadRecord – read a value from an ELX record

 

Usage:           

     

      In = ReadRecord.d( RH, Key, ValueD );

In = ReadRecord.f( RH, Key, ValueF );

      In = ReadRecord.i( RH, Key, ValueI );

      In = ReadRecord.s( RH, Key, ValueS );

      In = ReadRecord.b( RH, Key, ValueB );

 

Interface:

 

      input int RH

 

            { 1, ... }

 

input string Key

 

            a unique key chosen by the application

 

      output double ValueD

 

            { any double precision float }

 

      output float ValueF

 

            { any single precision float }

 

      output int ValueI

 

            { any integer }

 

      output string ValueS

 

            { any string }

 

      output truefalse ValueB

 

            { false, true }

 

      return int Out

 

            { 0, 1 }

     

Protocol:

 

      random ( simple )

 

Description:

 

ReadRecord reads an entry for a specified record and returns the number of values read. 

 

RH must be a valid record access handle returned by OpenRecord.

 

Key identifies a specific record entry.  An entry with the same Key must already exist.

 

NOTE:  Key is parsed into individual words for comparison.  More than one contiguous space or tab character is not significant.

 

ReadRecord attempts to read the entry value as a proper value of the function data type.  Function data type is indicated by the last letter in its name.  If the entry value cannot be properly interpreted, In will be zero and Value will not be modified.  Otherwise, In will be one, and Value will be significant.  If Value is a numeric type, EasyLanguage will automatically convert the value as read to the actual type of Value.   

 

In will be zero if there is an error, or if a proper value cannot be extracted from the entry.  The two cases cannot be distinguished.  However, in the error case, if ELX error traps are enabled, the program will be disabled on the current bar.

 

Errors:

 

      Indication:  In = 0

 

      Void Context ( ELX not initialized )

      Invalid Handle ( record does not exist )

     

Association:

 

      UseELX, OpenRecord, WriteRecord


Function:

 

      SaveRecord - save an ELX record

 

Usage:           

     

      Lines = SaveRecord( RH );

 

Interface:

 

input int RH

 

            { 1, ... }

 

      return int Lines

 

            { 0, ... }

 

Protocol:

 

      random ( simple )

 

Description:

 

SaveRecord updates an ELX record file and returns the number of lines output.

     

RH must be a valid record access handle returned by OpenRecord.

 

Record file contents are completely replaced. 

 

Lines will be zero if the record hasn’t been modified or if there is an error accessing the record file.  The two cases cannot be distinguished.  However, in the error case, if ELX error traps are enabled, the program will be disabled on the current bar

 

Errors:

 

Indication: Lines = 0

 

      Void Context ( ELX not initialized )

Invalid Handle ( record does not exist )

ReservedFile ( could not open file )

Association:

 

      UseELX, OpenRecord