developer guide: aef python

72
Front Arena Your Intelligent Engine Developer Guide: AEF Python PRIME 2019.4 Refer to the Front Arena Knowledgebase for a complete list of valid releases. FCA2831-17 December 2019

Upload: others

Post on 01-Mar-2022

37 views

Category:

Documents


0 download

TRANSCRIPT

Front Arena Your Intelligent Engine

Developer Guide: AEF Python PRIME 2019.4 Refer to the Front Arena Knowledgebase for a complete list of valid releases.

FCA2831-17 December 2019

Notices Copyright Copyright © 2019. By Fidelity National Information Services (FIS).

FIS, the FIS logo, and Front Arena are trademarks or registered trademarks of FIS, or its subsidiaries in the U.S. and other countries. All other trade names are trademarks or registered trademarks of their respective holders.

This document and the software described within are copyrighted with all rights reserved. No part of this document may be reproduced, transcribed, transmitted, stored in an electronic retrieval system, or translated into any language in any form by any means without prior written permission of FIS. FIS makes no warranties, express or implied, in this document. In no event shall FIS be liable for damages of any kind, direct or indirect, arising out of the use of this document or the information contained within.

Confidentiality statement This document contains information that is confidential or proprietary to FIS (or its direct or indirect subsidiaries). By accepting this document, you agree that: (1) if there is any pre-existing contract containing disclosure and use restrictions between your company and FIS, you and your company will use this information in reliance on and submit to the terms of any such pre-existing context; or (2) if there is no contractual relationship between you or your company and FIS, you and your Company agree to protect this information and not reproduce, disclose, or use the information in any way, except as may be required by law.

Disclaimer The screens and illustrations are representative of those created by the software, and are not always exact copies of what appears on the computer monitor. Companies, names, and data used in examples herein are fictitious unless otherwise noted. The material in this document is for information only, and is subject to change without notice. FIS reserves the right to make changes in the product design and installation software without reservation and without notice to users.

Supplementary legal notice for product documentation Any information expressed in this document regarding standard practice or conventions in financial markets or in the administration or functioning of banks is included to provide context to information provided about our products and services and thereby clarify how these products and services function. Such information is expressed in good faith but FIS accepts no liability for its accuracy or validity. Users are responsible for verifying the validity and accuracy of such information to their own satisfaction.

Products belonging to third party suppliers other than FIS or its subsidiaries are mentioned in this document only as information to the reader and are not to be regarded as specific recommendations. FIS or its subsidiaries does not guarantee the quality of these products, their performance, or their usage for any special purpose.

Contents 1 ....... Introduction................................................................................................................................... 7

1.1..... The possibilities of Python in Front Arena ........................................................................... 7

1.2..... Python modules supplied with Front Arena ......................................................................... 7

1.3..... Terminology ......................................................................................................................... 7 2 ....... Working with Python in PRIME ................................................................................................... 9

2.1..... Storing Python modules ....................................................................................................... 9

2.2..... Python Editor ........................................................................................................................ 9

2.2.1 .... "Hello World" in Python Editor ................................................................................ 9 2.2.2 .... Installing modules on menus ................................................................................10

2.3..... Extension Editor .................................................................................................................10 2.3.1 .... "Hello World" in Extension Editor .........................................................................11

2.4..... Python log window .............................................................................................................11

2.5..... AEF Browser ......................................................................................................................12

3 ....... Working with the ael module .....................................................................................................13

3.1..... AEL classes .......................................................................................................................13

3.2..... The ael_table class ............................................................................................................13 3.3..... The ael_entity class ...........................................................................................................14

3.3.1 .... Committing cloned child entities ...........................................................................14 3.4..... ael_table[ ] ..........................................................................................................................14

3.5..... The read( ) method ............................................................................................................14

3.5.1 .... Linking to referenced ael_entity objects between tables ......................................15 3.6..... AEL object types ................................................................................................................16

3.7..... ARENA functions in AEL ....................................................................................................16

3.8..... Working with dates .............................................................................................................16

3.9..... Accessing text and binary objects......................................................................................16

3.10... Handling enumerated values .............................................................................................17

3.11... Accessing ASQL query results from AEL ..........................................................................17 3.11.1 .. Syntax ...................................................................................................................17 3.11.2 .. Parameters ...........................................................................................................17 3.11.3 .. Returns .................................................................................................................17

3.12... Extended translation ..........................................................................................................18

3.13... User input variables ...........................................................................................................19

3.13.1 .. Performance guidelines ........................................................................................19 3.13.2 .. Recommendations ................................................................................................20

4 ....... Working with the acm module ..................................................................................................21

4.1..... ACM classes and abstraction ............................................................................................21

4.2..... ACM mapping of the ADM .................................................................................................21

4.3..... Other business classes ......................................................................................................22

4.4..... Core classes ......................................................................................................................23 4.5..... Server methods ..................................................................................................................23

4.6..... FClass objects....................................................................................................................23

4.7..... Handling ACM classes in Python .......................................................................................24 4.8..... FObject methods ................................................................................................................25

4.8.1 .... Attributes ...............................................................................................................25 4.8.2 .... String representation ............................................................................................25 4.8.3 .... Testing for type .....................................................................................................25 4.8.4 .... Others ...................................................................................................................26

4.9..... FRow ..................................................................................................................................26 4.10... FMethod .............................................................................................................................26

4.11... Accessing ADFL calculated values from Python ...............................................................26

4.12... Calculating ADFL values using Python ..............................................................................27

4.13... Exception handling .............................................................................................................27

4.14... Collections ..........................................................................................................................28

4.15... Dictionaries ........................................................................................................................29 4.16... FFunction ...........................................................................................................................29

4.17... Datatype mappings and implicit conversions .....................................................................30

4.17.1 .. Mapping of ACM datatypes to Python ..................................................................30 4.17.2 .. Mapping of Python datatypes to ACM ..................................................................31 4.17.3 .. Implicit conversion of Python strings to ACM datatypes ......................................31

5 ....... Updating and subscribing to the ADM database ....................................................................32 5.1..... Updating and subscribing with ael .....................................................................................32

5.2..... Updating single and related entities ...................................................................................32

5.3..... Updating multiple entities ...................................................................................................34

5.4..... Synchronising with ADS .....................................................................................................34

5.4.1 .... Callback handlers .................................................................................................34 5.4.2 .... Syntax ...................................................................................................................35

5.5..... Updating data with acm .....................................................................................................36

5.5.1 .... Using standard clones ..........................................................................................36 5.5.2 .... Using storage images ...........................................................................................37 5.5.3 .... Reverting changes ................................................................................................37 5.5.4 .... Transactions .........................................................................................................38 5.5.5 .... Best practices .......................................................................................................38

5.6..... Subscribing to data ............................................................................................................38

5.7..... Deleting child entities in AEL .............................................................................................39

5.8..... Deleting child entities in ACM ............................................................................................39

6 ....... Entering Python variables at run time .....................................................................................41

6.1..... Using Run Script ................................................................................................................41 6.2..... ael_variables format ...........................................................................................................41

6.2.1 .... Using InputHook ...................................................................................................42 6.3..... ael_main( ) .........................................................................................................................42

6.3.1 .... Syntax ...................................................................................................................42 6.4..... Starting the Run Script parameter GUI from a script .........................................................43 6.5..... Performance when using candidate values .......................................................................44

7 ....... User-Written ARENA Functions ................................................................................................45

7.1..... Method 1 (ASQL only) .......................................................................................................45 7.2..... Method 2 ............................................................................................................................46

7.2.1 .... Syntax ...................................................................................................................46 8 ....... Exception handling ....................................................................................................................48

8.1..... Exception handling in AEL .................................................................................................48

8.1.1 .... TypeError ..............................................................................................................48 8.1.2 .... RuntimeError ........................................................................................................48 8.1.3 .... AEL IndexError .....................................................................................................49

8.2..... Catching exceptions in ACM ..............................................................................................49

9 ....... The run time environment .........................................................................................................50

9.1..... Logging ..............................................................................................................................50

9.2..... The site-startup module .....................................................................................................50

9.3..... Configuration variables ......................................................................................................50 9.4..... The standard library ...........................................................................................................50

10 ..... The sybdb and calldb modules .................................................................................................51

10.1... Setting up sybdb ................................................................................................................51

10.2... Sybdb and calldb functions and methods ..........................................................................54

10.2.1 .. The connect( ) function .........................................................................................54 10.2.2 .. The execute( ) method .........................................................................................55 10.2.3 .. The close( ) method ..............................................................................................55

11 ..... Using third-party Python modules ...........................................................................................56

11.1... Adding module to ADS .......................................................................................................56

11.2... Network file install ..............................................................................................................56

11.3... Adding to PythonExtensionLib ...........................................................................................56 11.4... Full Python install ...............................................................................................................56

12 ..... Sample Python programs ..........................................................................................................57

12.1... Subscriptions ......................................................................................................................57

12.2... Custom broker fee calculation ...........................................................................................58

12.3... Adding an additional info field ............................................................................................58

12.4... Populating and reading data fields in PriceDefinition ........................................................59 13 ..... Python coding best practices ...................................................................................................60

13.1... General ..............................................................................................................................60

13.1.1 .. Avoid direct SQL access ......................................................................................60 13.1.2 .. When to use acm and when to use ael ................................................................60 13.1.3 .. Code conventions .................................................................................................60

13.2... Error handling .....................................................................................................................60 13.2.1 .. Catching errors early and indicating location clearly ............................................60 13.2.2 .. Being specific in exception handler ......................................................................61

13.3... Updates and subscriptions .................................................................................................61

13.3.1 .. Avoiding infinite recursion in update handlers ......................................................61 13.4... Performance .......................................................................................................................61

13.4.1 .. Using static ACM structures whenever possible ..................................................61 14 ..... Debugging Python in PRIME using Winpdb ............................................................................63

14.1... About Winpdb.....................................................................................................................63

14.1.1 .. Compatibility note .................................................................................................63 14.1.2 .. Limitations .............................................................................................................63

14.2... Getting started....................................................................................................................63

14.3... Quick start instructions .......................................................................................................63

14.4... The console ........................................................................................................................67 14.4.1 .. Execute code ........................................................................................................67 14.4.2 .. Conditional breakpoints ........................................................................................67

14.5... Opening source files during debugging .............................................................................67

14.6... Debugging alternatives ......................................................................................................68

14.7... rpdb2.py workaround .........................................................................................................68

15 ..... ARENA Python ...........................................................................................................................69 15.1... What is ARENA Python? ...................................................................................................69

15.2... Using ARENA Python ........................................................................................................69

15.2.1 .. On Windows .........................................................................................................69 15.2.2 .. On Linux ...............................................................................................................69

15.3... ARENA Python options ......................................................................................................70

15.3.1 .. ARENA Python specific options ...........................................................................70 15.4... Interactive mode.................................................................................................................70

15.5... Command option values ....................................................................................................70

Developer Guide: AEF Python (FCA2831-17)

1 Introduction 7 of 72

1 Introduction This guide describes how to use the Python programming language to customise and extend Front Arena within the ARENA Extensions Framework (AEF). This guide does not focus on the Python language itself but covers issues that are specific to the use of Python within AEF.

Note: Python is freeware, a multi-platform, interpreted, object-oriented scripting language supplied with Front Arena. For learning purposes, both code and documentation can be downloaded from http://www.python.org/. If you are new to Python, it is recommended that you study the tutorial that can be found in the documentation section of the Python web, http://www.python.org/doc.

1.1 The possibilities of Python in Front Arena Python is embedded in certain Front Arena components and can be used in several ways:

• Python scripts can be run to perform tasks, typically database interactions such as producing a report or adjusting data when 'marking to market'.

• New functions can be created with Python which can be called from ASQL or ADFL to provide custom functionality, for example non-standard calculations. See 7 User-Written ARENA Functions.

• Standalone applications can be developed using Python that run under an external Python interpreter and access Front Arena through the supplied extension modules. This is typically done for system integration solutions.

• Hook modules coded in Python can be used to extend or modify the functionality of Front Arena and PRIME. Each hook is described in detail in AEF Browser (FCA4033). Further information about the hooks, including examples of possible Python implementations of hook modules, are described in Developer Guide: AEF Basic Extensions (FCA3724). Hooks and hook module examples for BDP are described in Overview: BDP Business Data Processing (FCA2324) and for AMBA in Developer Guide: AMB SDK (FCA1132).

1.2 Python modules supplied with Front Arena The following Python modules are supplied with Front Arena:

• acm: Provides access to the classes, methods, and functions in the ARENA Class Model (ACM).

• ael: Provides access to the functions, classes, and methods in the ARENA Extension Library (AEL). This provides an ADM oriented way of working.

• sybdb: Provides direct connectivity to Sybase Adaptive Server Enterprise (ASE), bypassing the ARENA Data Server (ADS).

• calldb: Provides direct connectivity to Microsoft SQL Server, bypassing the ADS. • amb: Provides access to the Message Broker Format (MBF) functions in the ARENA

Message Broker (AMB) Software Development Kit (SDK).

1.3 Terminology We use the following terms in this guide:

• ACM: The set of classes, relationships, and methods available to Python, ARENA Data Flow Language (ADFL), and ACM .NET programmers. Almost all entities in Front Arena are represented by classes in the ACM.

Developer Guide: AEF Python (FCA2831-17)

1 Introduction 8 of 72

• acm: The Python library supplied with Front Arena that provides access to entities in the ACM.

• ADM: The tables, columns, and relationships of the database. • AEF (ARENA Extension Framework): The collection of tools, techniques, and supported

ways of extending Front Arena and PRIME; see Overview: Extending Front Arena (FCA2736).

• AEL: The tools and technology and methods around the use of Python with the ael library. • ael: The Python module supplied with Front Arena that provides access to entities in the

ADM. • ARENA Python: A standalone Python interpreter combined with acm and ael. Further terminology is explained in the Front Arena Glossary (FCA1249).

Developer Guide: AEF Python (FCA2831-17)

2 Working with Python in PRIME 9 of 72

2 Working with Python in PRIME This section describes the facilities for creating and managing Python modules within the Front Arena PRIME client.

2.1 Storing Python modules There are two basic ways of storing Python source code modules in the ADM database:

• As extension definitions of type FPythonCode. Python modules created and edited in the PRIME Extension Editor and stored in extension modules. Such modules are always associated with an ACM class (usually FObject). They may be overridden by other Python modules in the current context. PRIME Explorer can be used to test these modules.

• As text objects of type FAel. Python modules created and edited in the PRIME Python Editor and stored directly on the ADS. Unless they are "safe" modules, refer to Developer Guide: AEF Basic Extensions (FCA3724), they may be overridden by Python modules stored in the current context. Code created in Python Editor can be reloaded directly in that editor.

2.2 Python Editor The PRIME Python Editor is opened from the Session Manager's System menu. It is a fully-featured editor for Python code.

The user group to which the user belongs and the user profile settings for that group controls the Python Editor user access (All>Admin>Administration Console>User, Groups & Organisation and/or User Profile).

Detailed information about all the Python Editor's features can be found in PRIME Help (shortcut: F1, while you are in the editor).

2.2.1 "Hello World" in Python Editor The following procedure creates a simple Hello World program and highlights some basic features of the Python Editor.

1. From PRIME Session Manager, select System>Python Editor. 2. Type the following code into the editor:

print ("hello world")

3. The print statement sends output to stdout (the standard output device) which in the case of Front Arena is the Python log window, a part of the Front Arena log window.

4. Select File>Save (or press Ctrl+S). 5. Enter a name for the module without a filename extension, for example helloworld. Select

Save. (Note that some special characters, like / and \, are not allowed in the filename.)

6. Run the module by selecting Home>Module>Reload (or by pressing Ctrl+R). The Log window will show the Python Log window view, with the output from the program:

Developer Guide: AEF Python (FCA2831-17)

2 Working with Python in PRIME 10 of 72

The Reload Module command is the equivalent of the Python reload function. Note that the command does not save any changes you have made to the module.

7. Add the following flawed line of code in the editor then reload the module again:

prin "goodbye world"

8. A syntax error will be detected when you reload a module and will be shown in the Python Editor's error pane:

When the editor detects multiple errors, you can move between the errors in the error pane and the corresponding lines in the code by pressing F4 or SHIFT+F4 (of selecting View>Next Error / Previous Error).

In Python source code, indentation affects the program's logic:

1. Select Home>Alignment>Whitespace (or press Ctrl+Shift+W) to make spaces, tabs, and other whitespace characters visible in your code.

2. Select Home>Alignment>Indent Guides (or press Ctrl+Shift+I) to show vertical bars that indicate the indent level, this may make working with long deeply nested functions easier.

3. Press TAB to indent code lines in increments of four spaces. Press SHIFT+TAB or BACKSPACE to unindent. Source code modules saved from the Python Editor are stored as files directly on the ADS (as objects of class FAel).

4. Press Ctrl+G to bring the Session Manager to the foreground.

5. Select Data>Explorer>Business Objects>Python Modules to see a list of all the Python modules loaded in the current session.

6. Locate your module in the list. Note that the location is shown as ADS.

7. Right-click the module to see the context menu options:

− Run… to run the module. − Open With Run Script to run the module as a script with input parameters refer to

Developer Guide: AEF Basic Extensions (FCA3724). − Open With>Python Editor. − Properties to see a wide range of interesting details about your module.

2.2.2 Installing modules on menus A Python module can be added to the system as an application and can then be added to user profiles using the Administration Console. This is done using the same procedure as when installing ASQL queries as applications, see PRIME Help (FCA1260) and the topic Admin>Administration Console>User Profile>Installing a script for details. This also works for modules that have input parameters. So, in effect you can write mini applications that can be started from anywhere in the system.

2.3 Extension Editor The PRIME Extension Editor is another tool that can be used to create and edit Python modules.

With this tool, Python source code modules are associated with classes in the ACM and are stored in extension modules on the ADS (as objects of type FPythonCode). These extension modules may in turn be added to one or more extension contexts that can be loaded in the user's PRIME session to provide different users with different functionality and views of the data.

Extension Editor is opened from the Session Manager's System menu. Like the Python Editor, the user group to which the user belongs and the user profile settings for that group controls the

Developer Guide: AEF Python (FCA2831-17)

2 Working with Python in PRIME 11 of 72

Extension Editor user access (All>Admin>Administration Console>User, Groups & Organisation and/or User Profile).

Detailed information about all the Extension Editor's features can be found in PRIME Help (FCA1260) (shortcut F1, while you are in the Extension Editor). For information about working with contexts and extension modules and creating extensions (including Python modules) with Extension Editor, refer to Developer Guide: AEF Basic Extensions (FCA3724).

2.3.1 "Hello World" in Extension Editor The following procedure creates a simple Hello World program and highlights some basic features of the Extension Editor. Compare this procedure with the similar one for Python Editor given above.

1. From PRIME Session Manager, select System>Extension Editor. 2. Select Home>New.

3. Set the Type to FPythonCode and Module to the module where you want to save your extension.

4. Enter the following code into the editing pane:

FObject:helloworld print ("hello world")

5. Note that the first line of the module specifies both the extension's name (helloworld) and the ACM class with which the extension is associated - here, you are extending the class FObject, the parent class for all objects in the system. (Unlike Python Editor where you can create a Python module directly, in the Extension Editor you are defining a Python module that can be built at run time along with its source code. Effectively, the only difference is this first line in the module.)

6. Apply the changes. The first line of your module will now be prefixed by the name of your selected extension module, and the icon of this module will now show a large red dot indicating that the change has been applied to the module but is not yet saved.

7. Save the changes. Python modules saved from the Extension Editor are stored as text strings in extension modules (which themselves are stored on the ADS as objects of class FPythonModule).

8. Press Ctrl+G to bring the Session Manager to the foreground.

9. Select Data>Explorer>Business Objects>Python Modules to see a list of all the Python Modules loaded in the current session.

10. Locate your module in the list. Note that the location is shown as the name of your current context (for example, MyTestContext). This is because a Python module stored in the extension system always overwrites any module with the same name stored directly on the ADS - for example, one created with Python Editor, unless it is a "safe" module. Refer to AEF for Security and Validation in Developer Guide: AEF Basic Extensions (FCA3724).

11. Right-click the module and select Run to run the module. As with the Python Editor version of Hello World, the output is sent to output to stdout, the Python log window.

Unlike the Python Editor, there is no syntax error detection, whitespace display, or indentation guides. However, if these additional features are required, it is easy to develop modules in Python Editor and copy-and-paste code into Extension Editor for storage in extension modules.

2.4 Python log window Text sent to stdout (the default for the Python print statement) is displayed in the Python log window which is part of the PRIME log window. Text sent to stderr is displayed in the error pane of the Python Editor.

There are several uses for writing data to the Python log window:

Developer Guide: AEF Python (FCA2831-17)

2 Working with Python in PRIME 12 of 72

• To see the effects of Python code when learning. • To print out intermediate results when testing or debugging your own functions. • To display error messages, both when testing and when functions are put into production. Using the alternatives under the log window's View menu, output can be enabled and disabled.

Tip: For error messages, it may be more appropriate to use the ael.log( ) function, which writes to the ARENA LOG. This is both visible in the log window and it ends up in the log file for later analysis.

2.5 AEF Browser The AEF Browser window available in PRIME (System>AEF Browser), and as a standalone help file Reference: AEF Browser standalone version (FCA4033), provides a comprehensive description of the classes, methods, functions of the ACM that are accessible through the Python acm module.

Developer Guide: AEF Python (FCA2831-17)

3 Working with the ael module 13 of 72

3 Working with the ael module The Python module ael provides classes, objects, and functions for working with ADM database tables and data.

To use this module, including the following statement at the beginning of your own Python modules:

import ael

3.1 AEL classes The AEL classes are:

• ael_table: Provides access to the tables in the ADM database. • ael_entity: Once data has been retrieved, individual rows are represented as instances of

ael_entity. • ael_selection: This is a collection of ael_entity objects, typically as a result of using the

select method on an ael_table. • ael_yc: Class that encapsulates yield curves. • ael_vol: Class that encapsulates volatilities. • ael_calc: Some calculated values are returned as this type. It behaves exactly like a float

with the addition that you can subscribe to it. • ael_date: Dates are represented as instances of this type. The classes are introduced below, for more information refer to the AEF Browser (System>AEF Browser).

3.2 The ael_table class Main tables and relationships in the ADM:

To create an ael_table object, you append the table name to the Python module name. For example, to reference the Calendar table as a Python object, you write this code:

ael.Calendar

The various ael_table objects have several general methods associated with them. The principal ones are read( ) and select( ), which you can use to create ael_entity and ael_selection objects, respectively.

Use the dir( ) function to list the methods available on any object and the columns( ) method to list the columns in a table object.

Developer Guide: AEF Python (FCA2831-17)

3 Working with the ael module 14 of 72

Example Print(dir(MyObject)) Print(ael.Trade.columns( ))

Note: Object names in Python are case sensitive, so you must refer to tables with the correct capitalisation - for example, Instrument and TradeFilter. This is also necessary to distinguish between User, which is an ael_table object, and user( ), which is an ael function.

3.3 The ael_entity class An ael_entity object is one row in a database table, with associated columns and methods. To create a reference to the ael_entity object, you must identify a unique value in the table, such as ID or number.

To see which fields are unique, refer to the ADM section in the AEF Browser. There are two approaches:

• using the [ ] (square brackets) operator: ael.Instrument['BMW'] • use the read( ) method: ael.Instrument.read('insid="BMW"')

3.3.1 Committing cloned child entities Committing a cloned child will update neither the version_id nor the update_time of the parent, hence subscribing clients will miss the update. Instead clone the parent, change the child, and commit the parent.

For example, instead of cloning and committing a payment, clone the trade, find the child payment, change it, and then commit the parent trade.

3.4 ael_table[ ] The simplest way to create an ael_entity object is with the square brackets operator acting on an ael_table object. The syntax used is:

ael_table[int | string]

Here ael_table is an ael_table object, int is a value of the primary key of the table, and string is a value of the unique name key in the table. All tables have a primary key, but only some have a unique name key. Examples of primary and unique name keys are insaddr and insid in the Instrument table.

Note: Although the ChoiceList table does not have a unique name key, this construct also works with ael_table objects of type ChoiceList. In this case, the MASTER choice lists have unique names, and this construct returns the MASTER choice list of the specified name.

Examples ael.Calendar[2] ael.Calendar['Frankfurt'] ael.ChoiceList['ValGroup']

3.5 The read( ) method Using the read( ) method, you can identify explicitly which column contains the unique value you are identifying.

Example ael.Calendar.read('calid="Frankfurt"') ael.Instrument.read('insid="BMW"')

Developer Guide: AEF Python (FCA2831-17)

3 Working with the ael module 15 of 72

Note: The column names are not included in the name spaces of ael_entity objects, so they are not listed by the Python dir( ) function. To find the names of columns for ael_entity objects, use the columns( ) method on the ael_entity object.

For example: print (ael.Instrument.columns( ))

In general, when you access a column in this way, Python AEL returns a Python object whose type matches the data type of the column, according to this table:

ASQL data type AEL data type

int int

char str

string str

float float

double float

boolean Int (0 or 1)

time int in seconds

date ael_date

Note: Some column names in the CorrelationCell, Instrument, Leg, RiskFactorSpec, and VolatilityCell tables use a dot notation, which is not permitted in Python AEL. These are pairs of columns that give a count of periods of a type determined by the enumerated value DatePeriodUnit.

For example, the pair of columns pay_period.unit and pay_period.count in the Instrument table is accessed by the column name pay_period. When accessing this column, AEL returns a string of the form np, where n is an integer, and p is a letter denoting the period: d, w, m, or y for the enumerated values Days, Weeks, Months, and Years.

3.5.1 Linking to referenced ael_entity objects between tables If the column is a reference to another table, an ael_entity object is created for the referenced table, and can be accessed using dot notation. For example, to read a user's name:

ael.User['JOHN'].name

If the column is a reference column to another table, AEL creates an ael_entity that is pointed to by the reference column. For example, this code creates an ael_entity object of the user group that user JOHN belongs to:

ael.User['JOHN'].grpnbr

This is thus a third way to create ael_entity objects. Note that this is different from ASQL, where u.grpnbr returns the group number itself. To obtain the group number in Python, you would code this:

ael.User['JOHN'].grpnbr.grpnbr

Alternatively, if you want the id of the group, you can use the display_id( ) ARENA function, like this:

ael.User['JOHN'].display_id('grpnbr')

This is equivalent to:

ael.User['JOHN'].grpnbr.grpid

Developer Guide: AEF Python (FCA2831-17)

3 Working with the ael module 16 of 72

3.6 AEL object types To help you determine the type of an AEL object, AEL includes seven type objects in the ael module, one for each type of AEL object. These type objects are ael_table, ael_entity, ael_selection, ael_date, ael_yc, and ael_vol and ael_calc.

For example, you could use this code to print a list of ael_table names in the ael module:

for o in dir(ael): obj = eval('ael.' + o) if type(obj) == ael.ael_table: print (o)

3.7 ARENA functions in AEL In ASQL, all ARENA functions have a table name as the first argument, in AEL they are instead implemented as methods on objects, so the first argument is dropped.

Some ARENA functions, which work on a set of trades, rather than a single trade, are implemented as functions of the ael module, for example avg_price( ), rpl( ), and upl( ).

Example ael.Trade[1596383].nominal_amount(ael.date("1999-03-17"))

If an argument is optional, you enter the Python key word None.

For more information about ARENA Functions in Front Arena Track 4, refer to ARENA Functions in Front Arena 4 Help (FCA4131) and Reference: ARENA Functions in Front Arena 4 (FCA4406).

3.8 Working with dates Date handing is a central issue in a financial system such as Front Arena. For this purpose, there is a large set of date functions available in ael. Refer to AEF Browser for details.

If you do not create ael_date objects by reading date columns in the ADM database or through ARENA functions, you can create them using functions contained in the ael module.

There are functions for incrementing dates according to various calendars.

Sometimes you need to convert ael_date objects into different formats for various purposes. There are several functions for this such as to_string( ) and to_ymd( ).

When outputting to the Python output stream stdout, for example, the ael_date object is automatically converted to a string in ISO format; that is yyyy-mm-dd. For example, the following statement displays today's date in ISO format:

print ("Today's date is", ael.date_today( ))

However, if you want to present dates in another format or include an ael_date object in a string expression, you need to explicitly convert the ael_date object to a string. If the ISO format is acceptable, you can use the standard Python str( ) operator, like this:

print ("Today's date is " + str(ael.date_today( )))

To present ael_date objects in another format than ISO, you use the to_string( ) method of the ael_date object. You can also present an ael_date object as a Python tuple representing year, month, and day.

3.9 Accessing text and binary objects Several data items are stored in the ADM database as text and binary objects, not directly accessible from ASQL. However, for some of these objects, there are AEL methods on the relevant ael_entity objects.

Developer Guide: AEF Python (FCA2831-17)

3 Working with the ael module 17 of 72

The tables involved are TradeFilter, TextObject, YieldCurve, OrderDescription, OrderMarket, and InfoMarket.

The methods are get_query( ) and set_query( ) on the TradeFilter table, get_text( ) and set_text( ) on the TextObject table, and get_data_message( ) and set_data_message( ) on the other tables.

3.10 Handling enumerated values When accessing the ADM database through ADS, enumerated values are converted from their stored numeric codes to a string that is meaningful to users. When reading and writing these enumerated values when bypassing ADS, you need to know what these numeric codes are.

There are two functions for handling these enumerated values, enum_from_string( ) and enum_to_string( ).

3.11 Accessing ASQL query results from AEL The result of ASQL queries can be accessed from within a Python script with the following function. This feature can significantly simplify many programming tasks by letting appropriate parts of the program logic be expressed as compact easy to write SQL statements. See Developer Guide: ARENA SQL (FCA1050) for details of ASQL syntax.

3.11.1 Syntax ael.asql(query,[extended_translation],variables,defines)

3.11.2 Parameters Entity Description

query String containing the query.

extended_translation Integer (0 or 1) indicating if extended translation is to be performed (data returned converted to ael entities), optional (default=0). See 3.12 Extended translation for further details.

variables List of variable names (strings). See 3.13 User input variables for further details.

defines List of variable defines corresponding to names (strings).

3.11.3 Returns The function returns the result as a list of sub queries (each union statement results in a new sub query, if no union statement is used the list will only contain one item). Each sub query is a list of rows where each row is represented as a tuple.

Example Executing: import ael,pprint result = ael.asql("""select insid, insaddr,instype from instrument where insid like 'ABB4D45%'""") pprint.pprint(result)

Gives the following output:

Developer Guide: AEF Python (FCA2831-17)

3 Working with the ael module 18 of 72

[['insid', 'insaddr', 'instype'] [[('ABB4D45', 62024, 'Option'), ('ABB4D45SD', 24380, 'Combination'), ('ABB4D45X', 25428, 'Option')]]]

Tip: The standard module pprint is useful for displaying nested data structures such as the result sets returned by asql( ) in a readable form.

When working with SQL statements Pythons triple quote feature (""") allows the queries to be formatted nicely.

3.12 Extended translation When the extended translation object is enabled the asql( ) function will attempt to transform all object references into AEL entities. This is extremely useful in the common programming construct where a select statement is used to retrieve a set of objects for further manipulation. See the following example.

Example To rename a set of instruments: sql = """select insaddr from instrument where insid like 'ABB4D45%'""" for (ins,) in ael.asql(sql,1)[0]: print (ins) clone = ins.clone( ) ... code to update clone.insid ... clone.commit( )

The output of the print statement is: [['insid', 'insaddr', 'instype'] [[<ael entity, Instrument at 1dae670> <ael entity, Instrument at 1dae680> <ael entity, Instrument at 1dae690>]]]

What has happened is that the insaddr field containing values such as 62024 has been translated to the equivalent AEL entity. (Note the use of [0] to retrieve the first union and (ins,) to unpack the rows into individual values.)

Extended translation also works with references to other tables, for example: sql = """select t.trdnbr, t.insaddr from trade t, instrument i where t.insaddr = i.insaddr and t.trdnbr < 10""" for (trade,ins) in ael.asql(sql,1)[1][0]: print (trade, ins)

Developer Guide: AEF Python (FCA2831-17)

3 Working with the ael module 19 of 72

The translation takes care of both trdnbr, which is the primary key in its table as well as the reference to the instrument record. The result is: <ael entity, Trade at 1dae6a0> <ael entity, Instrument at 1dae6b0> <ael entity, Trade at 1dae670> <ael entity, Instrument at 1dae680> <ael entity, Trade at 1dae690> <ael entity, Instrument at 1dae6c0> <ael entity, Trade at 1dae6d0> <ael entity, Instrument at 1dae6e0> <ael entity, Trade at 1dae6f0> <ael entity, Instrument at 1dae700>

3.13 User input variables The last two optional parameters allow or specification of user input variables. The first list specifies the names of the variable and the define list contains their values (the lists must thus have the same length). The lists may only contain strings. All occurrences of the variable value in the query text are replaced with the corresponding definition. A convention is to start variable names with the @ character. The following example demonstrates the usage of variables:

Example ael.asql("""select userid,name from user where name > @name """ ,0,['@name'],["'t'"])

Important: Variable substitution is a direct string substitution so if the variable is substituted where a string is expected in the ASQL query then the definition must contain the single quotes used to delimit strings in ASQL

In the previous example this is demonstrated in the definition of the @name variable, where the t is surrounded by two sets of quotes, the outermost (" ") are the regular Python quotes, the inner (' ') are replaced into the query, resulting in:

name > 't'

3.13.1 Performance guidelines Executing queries is an expensive operation; it involves many steps such as parsing, optimising and executing the query. As such, you should be aware of when to use the asql( ) function and when there are other more efficient alternatives to prefer.

Example For a quick illustration compare 1000 executions of the following methods of selecting all FRAs in the system (in this test case there are 16 of them):

a) for (i,) in ael.asql("""select insaddr from instrument where instype='FRA'""",1)[0]: pass

b) for i in ael.Instrument.select('instype=FRA'): pass

time a = 0.809

Developer Guide: AEF Python (FCA2831-17)

3 Working with the ael module 20 of 72

time b = 0.00789

Using the select( ) function is more than 100 times faster than using a query.

3.13.2 Recommendations Use of asql( ) queries is appropriate when:

• The query is complex involving joins and multiple tables. The ASQL processor will be able to optimise such queries extensively often performing the operations much more efficiently than an equivalent solution in pure Python.

• Saving development time is more important than saving execution time, such as when writing code that is to be run once (migration tasks and so on), or where performance is not a key issue (such as reporting tasks).

• Existing queries can be leveraged. Do not use asql( ) queries when:

• Either of select( ) or read( ) could be used instead. • The code is performance critical. • Avoid use of queries in valuation hook functions. These are almost always performance

critical and inappropriate use of queries in them could cause a severe slowdown of the entire system!

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 21 of 72

4 Working with the acm module The Python module called acm provides a wide range of classes, methods, and functions for working with business and application objects in Front Arena, including ADM database tables and data, and PRIME user interface elements.

To use this module, including the following statement at the beginning of your own Python modules:

import acm

4.1 ACM classes and abstraction The ACM is all-inclusive in that everything in the system from basic datatypes like integers through application constructs like rows in a portfolio sheet to physical connections to a market place are instances of some class in the ACM. However, you do not generally see basic datatypes as class objects within whatever programming language you are using; they are normally presented directly as language datatypes.

The basic design principle of the ACM is abstraction. Wherever possible, classes are organised to inherit the properties of some abstract, base class that specifies the common characteristics of all the classes that inherit from it. It is these shared characteristics that determine the semantics of the model.

The most abstract class is FObject, at the top of the ACM class hierarchy. FObject methods thus access the most general features of the model, such as keys and properties.

All classes in the ACM also provide string representations of themselves, through methods like StringKey( ) and AsString( ). All classes also tell you what class or category they are in, through the Class( ), ClassName( ), and Category( ) methods.

How each class actually implements these methods is transparent to you as a programmer. Using such methods, you can thus write generic code, in line with the fundamental principles of object-oriented programming. You do not need to change your code to handle a new category of object; the system handles this for you behind the scenes.

ACM classes can be abstract or concrete:

• Abstract classes like FObject cannot be instantiated. • Concrete classes like FBond can be instantiated. To check if a class is abstract or concrete, you can use the IsAbstract( ) method; it returns true if the class is abstract:

import acm print (acm.FObject.IsAbstract( )) print (acm.FBond.IsAbstract( ))

4.2 ACM mapping of the ADM The tables in the ARENA Data Model (ADM) are mapped to classes in the ACM.

Relationships are encapsulated as instances of FPersistentRelation, a subclass of FAttribute. These classes are metaclasses, providing a similar role in the ACM to the system catalogue in a relational database management system.

The actual data in the ADM database is represented in instances of subclasses of FPersistent; the tables mapping to classes and the columns to attributes. In some cases, one table is mapped to several classes in the ACM. This is generally done through a type column in the table, such as instype in Instrument table and type in the Party table. AEF Browser provides details of these mappings.

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 22 of 72

The concept of a class is represented by FClass, a datatype whose values are objects. This class is a subclass of FDomain (which represents all datatypes in the system). So, classes map to both tables and domains in the relational model. The difference between the two is that a domain represents a set of all possible values for a column, while a table represents the actual values at any one time, in general a subset of the domain of values. The ACM thus provides an elegant means of unifying the entity-relational view of data with that of the object-oriented view.

If you require information about the structure of the database itself, you can obtain this through the Tables method in the FDatabase class, a subclass of FStorage. This, in turn provides information on tables and columns through the FTable class (a subclass of FStorage) and FColumn (a subclass of FAttribute).

As the ACM is designed according to the principle of information hiding in program design, you cannot access the attributes that map to columns directly. Rather, the ACM provides methods to access these attributes, instances of FAccessGetMethod and FAccessSetMethod. These methods are dynamically created at run-time from information held in an internal mapping table.

The name of an access method in the ACM is the same as its corresponding attribute except that it has an initial capital letter. ACM attributes that map to ADM columns sometimes have different names from those in the ADM. For instance, all primary keys in the ADM are mapped to an attribute called oid (object ID) in domain id and many columns that indicate a particular type are mapped to cid (class ID).

As well as the access methods for each FPersistent subclass, many of these also implement published methods written in C++ as instances of FCppMethod.

(There is nothing special about FPersistent subclasses here. Many non FPersistent classes have published C++ methods. Indeed, some of these classes also dynamically implement access methods for published attributes. However, as these attributes are not externally visible, as a programmer using the ACM interface, you do not need to be concerned about these distinctions.)

There is, however, one type of method that is unique to FPersistent subclasses. Those ASQL methods that reference an ADM table in their first argument, generically called ARENA functions, are implemented as methods on the appropriate FPersistent subclass, instances of the FArenaFunction class.

4.3 Other business classes As well as the business classes in the ACM that map to ADM tables, there are a number of other business classes that represent some construct within the application. These are particularly relevant to ADFL expressions in extension attributes published in the Trading Manager. The following table lists the classes that represent rows in the Trading Manager, including those that are FPersistent classes. Note that the class of a row object can be overridden by the class attribute in the column definitions for particular columns.

Sheet Row object class

Order Book FPriceAggregate

Order FOwnOrder

Agent FAgent

Quote FPriceAggregate

Portfolio (portfolio rows) FPortfolioInstrumentAndTrades*

Portfolio (folder/group rows) FMultiInstrumentAndTrades

Portfolio (instrument rows) FSingleInstrumentAndTrades

Portfolio Projected Risk Sheet FTimeBucketAndObject

Trade FTradeRow*

Cash Analysis FMoneyFlowAndTrades

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 23 of 72

Sheet Row object class

Pricing FTradeRow*

Call/Put Order Book FPriceAggregate

Call/Put Quote FPriceAggregate

Time FTimeBucketAndObject

Vertical Portfolio FPortfolioInstrumentAndTrades*

4.4 Core classes There are a number of core classes that you might well find useful in your programming. One of the most common of these is FCollection and its subclasses, for several C++ methods return instances of these classes and expect them as arguments.

The FCollection class is abstract class for collections of objects, such as arrays, dictionaries, and sets, implemented as instances of FArray, FDictionary, and FSet, respectively.

Some common methods on these classes are Size( ), At( ), AtPut( ), and Add( ). However, when programming in Python, you do not need to use these methods as they are implemented directly as Python constructs, as described in 4.12 Calculating ADFL values using Python.

The class FClass also provides many useful facilities. For instance, you can obtain an array of all known instances of a class through the Instances method and of all instances of all subclasses through InstancesKindOf.

If you want to obtain a subset of these instances, you can use the Select01( ) and Select( ) methods in FClass and SqlSelect( ) in FPersistentClass. Note that the first two of these can be used with any class, not only on those classes that map to the ADM.

When applied to objects whose class is FPersistentClass, the Select01( ) and Select( ) methods are analogous to the select and read methods on the ael_table class in the ael module in Python. However, there is one significant difference. The constraint attribute in these methods can refer to any column or columns in the table, not just those that are indexed.

4.5 Server methods To obtain an instance of FClass, you can use the GetClass( ) method in the FTmServer class, or directly through the FClass objects in the acm module in Python. You can create an instance of instantiable classes through the Create( ) method in the same class, or directly using Python syntax.

There are several other server methods in the FTmServer class that you might well find useful. If you need to update the database through transaction processing, you can do so using the methods BeginTransaction( ) and CommitTransaction( ).

Several methods in the ACM are designed to throw exceptions when the result is not as expected. You can use the server method LastException( ) to handle these situations. This returns an instance of the FException class, which has a method Message( ), which returns the error message. However, in Python, you can obtain this information directly through the Python error handling mechanism.

Note that subclasses of FException exceptionally begin with an E, not an F.

4.6 FClass objects Rather than using GetClass to obtain FClass objects, as in the previous example, you can obtain FClass objects directly from the acm module, like this:

Stock = acm.FStock

For instantiable ACM classes, you can create instances of a class by calling the FClass object as a function or directly from the FClass objects in the acm module. For example:

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 24 of 72

Stock = acm.FStock newStock = Stock( ) newStock = acm.FStock( )

These are equivalent to:

newStock = acm.Create('FStock')

When creating a new instance of a class, you can use Python keyword arguments to set the values of attributes. For example:

newStock = Stock(name = 'XYZ') print (newStock.Name( ))

Note that here the keyword names are class attribute names, while you need to use a method name, with a capitalised initial letter, to access the attribute. Name( ) here is an access get method.

You can then change the value of the attribute using the access set method, like this:

stock.Name('LMN')

Alternatively, you can use a Python assignment statement to set the value of the attribute, like this:

stock.Name = 'PDQ'

Notice that you use the name of the access method, with an initial capital letter, rather than the attribute name. This is because as attributes in the ACM are not directly accessible they can only be accessed through methods. And Python makes no distinction in its syntax between attributes and methods. So a method name without parentheses is treated as an attribute by Python on the left-hand side of an assignment statement.

As well as using Python's ( ) callable construct to initialise new class instances, class objects in FPersistentClass can also use Python's [ ] indexing mechanism as a selection construct to retrieve information from the ADM database, in a similar manner to AEL on ael_table objects. As in AEL, you can either use the primary key as the index or a unique name column.

Example Writing: trade = acm.FTrade[35623]

Is equivalent to: trade = acm.FTrade.Select01('oid = 35623', '')

Or: trade = acm.FTrade.Select01('name = 35623', '')

Also: abb = Stock['ABB']

Is equivalent to: abb = Stock.Select01('name = "ABB"', '')

4.7 Handling ACM classes in Python In general, ACM classes and their methods are handled directly as Python classes and methods. This means that you can use the Python dot (.) operator to execute methods on classes, as in this example:

stock = acm.FInstrument['ABB'] print (stock.Currency( ))

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 25 of 72

Here Currency( ) is a method implemented in the FInstrument class and stock is an instance of the FStock class, a subclass of FInstrument.

In addition, a number of ACM classes behave in a Python manner, enabling you to use other Python constructs in these situations. In addition, a number of ACM classes have corresponding features in Python, enabling you to use other Python constructs in these situations. The classes involved are FClass, FIndexedCollection, FDictionary, FRow, FFunction, and FMethod.

4.8 FObject methods All ACM objects inherit from FObject and share its methods. Some of these methods are mapped onto the corresponding Python behaviour to make it easier to work.

4.8.1 Attributes You can change the value of the attribute using the access set method, like this:

stock.Name('LMN')

Alternatively, you can use a Python assignment statement to set the value of the attribute, like this:

stock.Name = 'PDQ'

Note: You use the name of the access method, with an initial capital letter, rather than the attribute name. This is because as attributes in the ACM are not directly accessible, they can only be accessed through methods. And Python makes no distinction in its syntax between attributes and methods. So, a method name without parentheses is treated as an attribute by Python on the left side of an assignment statement.

4.8.2 String representation When taking the string representation of an object the ACM method AsString( ) is called, so the following statements are equivalent:

i = acm.FInstrument['ABB'] print (i) print (i.AsString( )) print (str(i))

For some cases, like the previous, this representation is quite verbose (several pages of text) and you would like a short identifier. The following will do just that:

i = acm.FInstrument['ABB'] print (i.StringKey( ))

4.8.3 Testing for type ACM objects in Python do not implement the __class__ method. Instead you can use the ACM method Class( ). The following code demonstrates correct ways to do type checking:

i = acm.FInstrument['ABB'] print (i.Class( ) == acm.FInstrument) # False as this is a Stock print (i.IsKindOf( acm.FInstrument )) # True print (i.Class( ) == acm.FStock) # True

The following code will not behave correctly and is wrong:

print (i.__class__ == acm.FInstrument)# Error, no attribute __class__ print (isinstance(i,acm.FInstrument)) # FInstrument isn't a proper # Python class or type

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 26 of 72

4.8.4 Others Comparison and hash values are taken from the ACM functions Compare( ) and Hash( ), so these will generally provide correct behaviour.

The Inspect( ) method opens a properties inspector for the current object. This is useful for debugging.

4.9 FRow Instances of FRow and its subclasses are also implemented as Python sequence types. This provides a more concise way of accessing the values in each row than using the ColumnValues( ) method on each instance of FRow. For example:

cols = 'name, contractSize, strikePrice' rows = acm.FOption.SqlSelect(cols, 'name like PQ$') for r in rows: print (r[0], r[1], r[2])

4.10 FMethod Instances of FMethod are also implemented as Python callable types. Python has several ways of handling methods as callable objects.

A method that is associated with a class, but not a specific instance of the class, is unbound. You can create an unbound method object with the GetMethod( ) method in the FClass class, as in this example:

tiu = acm.FPortfolio.GetMethod('TradesIn', 1)

You can then invoke this unbound method object as a function, by giving it an object of its class as the first argument, followed by the other arguments required by the method. For example, if port and ins are FPortfolio and FInstrument objects respectively, then trades is set to an array of trades in the specified instrument and portfolio in this example:

trades = tiu(port, ins)

Because the function tiu( ) is unbound, you could then apply it to any other FPortfolio object.

Python also handles methods as objects bound to a particular instance of the relevant class. You create a bound method object as in this example:

tib = port.TradesIn

Here port is an instance of FPortfolio, as in the example above. You can then call the tib( ) function with different instruments as arguments to give an array of trades in each instrument and the port object, as in this example:

trades = acm.FDictionary( ) for i in port.Instruments( ): trades[i] = tib(i)

ACM methods and functions can also be called with keyword arguments, as described on http://docs.python.org/2/tutorial/controlflow.html#keyword-arguments.

4.11 Accessing ADFL calculated values from Python Calculated values, such as those typically displayed in Trading Manager sheets, can also be calculated via Python.

Calculated values are associated with an extension attribute and are calculated through ADFL expressions.

For more information about extension attributes and ADFL expressions, see Developer Guide: ARENA Data Flow Language (FCA1559).

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 27 of 72

A calculated value can be accessed from Python by creating a calculation space (a virtual Trading Manager application) for a specific context and sheet type:

import acm #Set context, sheet type, column id, and instrument context = acm.GetDefaultContext( ) sheet_type = 'FOrderBookSheet' column_id = 'Price Theor' ins = acm.FInstrument[ 'XXX' ] #Create CalculationSpace (a virtual Trading Manager) calc_space = acm.Calculations( ).CreateCalculationSpace( context, sheet_type ) #Get raw value value = calc_space.CalculateValue( ins, column_id ) print (value.Value( ) ) #Get formatted data calculation = calc_space.CreateCalculation( ins, column_id ) print (calculation.FormattedValue( ))

4.12 Calculating ADFL values using Python From ADFL, you can call your Python functions by defining, in the Extension Editor, an extension of type FCustomFunction.

Python functions in the Standard context should not be called directly from ADFL as the parameters are not typed and this will often cause problems, particularly with collections.

Like extension attributes, custom functions are stored in modules and are defined at the class level. Inheritance principles, similar to those for extension attributes, apply to them. Each custom function specifies the datatype of the function's parameters and the datatype of its returned value, for example:

[Default]FObject:createFilePath = Definition = createFilePath(string path, string fileName): FSymbol Function = FVaRFileParsing.merge_path_and_filename

Sometimes, however, you just have to call a Python function directly (for example when its module is not in the Standard context). In these circumstances you can use the ACM function py( ). For more information on calling Python functions from ADFL, see Developer Guide: ARENA Data Flow Language (FCA1559).

4.13 Exception handling You can use the Python exception mechanism to handle exceptions. A couple of simple examples describing this process are as follows.

import acm msg = 'Zero or one stocks expected' try: stock = acm.FStock.Select01("contractSize=1", msg) except RuntimeError as e: print (e)

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 28 of 72

As many stocks could have a contract size of one, the value of e is the value of msg passed to the Select01( ) method.

import acm a = acm.FArray( ) a[0] = "Fred" try: print (a.IntAt(0)) except TypeError as e: print (e)

Note that the value of e in these examples is the same as would be returned with this statement:

print (acm.LastError( ).Message( ))

The benefit of using the LastError( ) method is that if you ask the object it returns its class with the ClassName( ) method, it will tell you the type of error that ACM has detected.

The major elements of the acm module are a number of functions and class objects for all the classes in the ACM, whether they contain published methods or not. ACM objects are passed to Python in one of three ways:

• Directly as arguments to the ADFL function py( ). • As arguments to some hook functions. • Through the facilities of the class and function objects in the acm Python module. If you need to use the acm module, you access it with this Python statement in the normal way:

import acm

Otherwise, it is not strictly necessary to include this statement. In the code examples in this section it is excluded for brevity. There are no memory allocation issues at all when working with the acm library, the Python garbage collector works in cooperation with Front Arena client's garbage collection.

For all work with ACM, the AEF Browser, accessed from the Session Manager (System>AEF Browser), is a good source of information.

4.14 Collections Instances of FIndexedCollection, such as arrays, are implemented as Python sequences. This means that you can use Python indexing and slicing operations on such objects and iterate on them in using the 'for' statement.

Instances of any type of FCollection can be iterated using Python's 'for' statement.

For example, given: prf = acm.FPhysicalPortfolio['DANROO01'] instruments = prf.Instruments( )

Then:

len(instruments)

This is equivalent to this expression using an ACM method:

instruments.Size( )

Similarly, you can write:

for i in instruments: print (i.Name( ))

Instead of:

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 29 of 72

for i in range(ins.Size( )): print (ins.At(i).Name( ))

The list comprehension operators also work, so to get a list of the ISINs you could write:

isins = [instrument.Isin( ) for instrument in instruments]

The indexing and slicing operations work in the expected manner, except for one feature. To add an object to an array with, for example, 17 elements, write:

instrument[17] = newIns

This is equivalent to:

instrument.Add(newIns)

To convert a FIndexedCollection to a regular Python list you can do:

list(instruments)

You can use Python standard arithmetic operators with ACM objects including collections. The following operators are implemented for collections (and also numbers) in ACM:

Binary +, -, /, *

Unary +, -

For numbers, nonzero test, float, int, and long conversion are also supported.

4.15 Dictionaries Instances of FDictionary are support similar behaviour to normal Python mapping types.

Example: mydict = acm.FDictionary( ) mydict['ABC'] = 1

Is equivalent to: mydict = acm.FDictionary( ) mydict.AtPut('ABC', 1)

When using the 'for' statement for iterating FDictionaries, you will get the same behaviour as when iterating Python dictionaries.

Example #Will print (ABC then DEF) mydict = acm.FDictionary( ) mydict['ABC'] = 1 mydict['DEF']=2 for k in mydict: print (k)

4.16 FFunction Instances of FFunction are implemented as Python callable types. The instances of FFunction are ADFL operators and functions and ARENA functions, retrievable by the GetFunction( ) function in the acm module. With the exception of the ael( ) and py( ) functions, this means that you can execute ADFL and ARENA functions as if they were Python ones, like this:

mb = acm.GetFunction('msgBox', 3)

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 30 of 72

ret = mb('Warning', 'Do you really want to do this?', 4) if ret == 6: print ("OK")

4.17 Datatype mappings and implicit conversions As ACM objects are handled directly as Python objects there is little datatype conversion necessary between PRIME and Python when these objects are required as arguments to methods or are returned by these methods. However, there is a mapping between the two systems for some ACM objects and for ordinary datatypes.

These mappings are summarised in the following tables.

4.17.1 Mapping of ACM datatypes to Python When ACM datatypes are passed to Python, they are mapped as follows:

ACM datatype Python datatype

array(variant) FVariantArray Python wrapper

blob string

bool boolean

byte integer

char string

date string

dateperiod string

datetime string

datetimespan float

denominatedbasket FDenominatedBasket Python wrapper

denominatedvalue FDenominatedValue Python wrapper

double float

enum string

FDatePeriod string

FDateTime string

FDictionary FDictionary Python wrapper

FIndexedCollection FIndexedCollection Python wrapper

float float

FObject FObject Python wrapper

FPythonModule Python module

FString string

FSymbol FSymbol Python wrapper

int integer

int64 long

short integer

string string

time integer

Developer Guide: AEF Python (FCA2831-17)

4 Working with the acm module 31 of 72

ACM datatype Python datatype

void [none]

4.17.2 Mapping of Python datatypes to ACM When Python datatypes are passed to ACM, they are mapped as follows:

Python datatype ACM datatype

boolean bool

dictionary FDictionary

float double

integer int

long int64

[none] void

collection, tuple, or list of homogenous values (e.g. ints)

array (e.g. of ints)

collection, tuple, or list of mixed values (e.g. ints and strings)

FIndexedCollection

4.17.3 Implicit conversion of Python strings to ACM datatypes Python datatype ACM datatype

string in ISO date format or locale format date

string in ISO date-time format or locale format

datetime

string in ISO time format or locale format time

string or integer enum enum

string, np, where n is an integer and p is one of 'd', 'w', 'm', and 'y' standing for day, week, month, and year

Dateperiod

string of another format string

Attributes in FPersistentClass objects that map to foreign keys in the database are objects of the referenced class. They therefore normally expect objects as arguments to access set methods. However, if you specify the oid or name of the referenced object, then these integers or strings will generally be converted to the relevant referenced object. If there is no object with the specified oid or name, an exception is thrown.

Example stock.Currency = 'SEK'

This converts SEK to a currency object before assigning it to the currency attribute in the stock object.

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 32 of 72

5 Updating and subscribing to the ADM database Either AEL or ACM can be used to update and subscribe to the ADM database.

5.1 Updating and subscribing with ael As well as retrieving information from the ADM database in AEL, as you can also do in ASQL queries, you can also update the ADM database from AEL programs. This is something that is not possible from ASQL itself, although you can update specific fields through the Information Manager window.

Generally, use these AEL facilities for updating the database, rather than Transact SQL, as these updates pass through ADS, which minimises the risk of integrity problems arising. For example, AEL ensures that it is not possible to create a new record that requires a parent without the parent being specified. AEL also ensures that any mandatory fields are filled in when new objects are created.

If you really need to bypass the ADS, you can do so as described in 10 The sybdb and calldb modules, or by using the dbsql function, but do read section 13.1 Avoid direct SQL access before doing so. Also, refer to the AEF Browser. Be aware that in bypassing the ADS, you have no support from ADS and that these facilities should only be used by programmers with a good understanding of the ADM.

As well as updating the ADM database from AEL, you can also subscribe to the ADS, to be notified of all changes that are made to the database.

5.2 Updating single and related entities The AEL methods that enable you to write to the database are:

• new( ): Creates a new entity. • clone( ): Creates a copy of an entity that its properties can be edited. (Normal ael_entity

objects created with read( ) and select( ), for example, cannot be changed.) • commit( ): Executes changes that have been made to the database and also updates the

original ael_entity object from which the committed object was cloned. The method operates on one ael_entity object at a time, with the exception of ael_entity objects that are parents of other objects, such as Instrument or Party. In these cases, all children of the parent are also committed when the parent is committed. To update any other set of related ael_entity objects to ensure database integrity, you must create a transaction.

Note: Updating of child records is not blocked but when updating child records, the top parent record of the "business object" should be cloned and committed instead. If this is not done, then updates are not correctly picked up by other clients, version numbering will become incorrect and the transaction history and the audit file will become incorrect.

• delete( ): Deletes an entity and operates immediately on the database, a following method is not required.

• original( ): Can be used if you need to access the ael_entity object from which a clone was created. This will retrieve the actual state of the database.

• suggest_id( ): Used when creating a new instrument to return a suggested name for the instrument based on the suggestname.ttt description file, refer to System Administration: PRIME (FCA1086).

• regenerate( ): Also used when creating or updating instruments to create or regenerate cash flows for the instruments legs.

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 33 of 72

• set_default_accounts( ): Sets the default accounts for a trade according to the settings in the client.

Example: modifying and renaming a swap # Example: Open Swap for editing # change currency and calendar of each leg and # regenerate cash flows. Finally rename the instrument # and write to database import ael MySwap = ael.Instrument['GBP/IRS/BO-F/020827-070827/5.50'] print ('Opening for editing: ', MySwap.insid) MySwap = MySwap.clone( ) MyNewCurr = ael.Instrument['GBP'] MyNewCal = ael.Calendar['London'].calid #Make changes to the instrument MySwap.curr = MyNewCurr for leg in MySwap.legs( ): #Set new currency and calendar for each leg leg.curr = MyNewCurr leg.pay_calnbr=MyNewCal leg.regenerate( ) print ('Data/cashflows updated for leg: ', leg.legnbr) #Rename instrument MySwap.insid=MySwap.suggest_id( ) #Commit changes to database MySwap.commit( ) print ("Updated: ", MySwap.insid) # The following output is printed: # # Opening for editing: USD/IRS/LI-F/000920-050920 # Data/cashflows updated for leg: 1006796 # Data/cashflows updated for leg: 1006795 # Updated: GBP/IRS/LI-F/000920-050920

Important: In AEL, the parent is not always updated when a child is modified. This means that version_id, update_time, and update_user in the parent entity will not be updated, and this in turn causes problems with update collision and inconsistent transaction history. This happens when committing a cloned child. When modifying a child, it is important to always commit a cloned parent and never just the child.

Example Updating a cloned leg in AEL does not update the parent instrument: ##BAD import ael

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 34 of 72

leg = ael.Leg[1] print (leg.insaddr.version_id) leg_c = leg.clone( ) leg_c.spread=0.6 leg_c.commit( ) ael.poll( ) print (leg.insaddr.version_id)

The right way to do this in AEL is to always clone the parent: #GOOD leg = ael.Leg[1] print (leg.insaddr.version_id) # clone parent instrument ins_c = leg.insaddr.clone( ) leg_c = ins_c.legs( )[0] leg_c.spread=0.6 # commit cloned parent instrument ins_c.commit( ) ael.poll( ) print (leg.insaddr.version_id)

5.3 Updating multiple entities When updating a set of entities, AEL creates a transaction when committing a related set of ael_entity objects. However, there may be occasions when you need to explicitly define a transaction for committing a multiple set of ael_entity objects.

For instance, you might need to do this for either integrity or performance reasons. In the latter case, it is generally quicker to commit a large batch of objects in one transaction than in a separate set of transactions for each individual commit.

There are three functions to define transactions in AEL. They are begin_transaction, commit_transaction, and abort_transaction. When AEL executes a commit within an explicitly defined transaction, it does not create its own transaction as it usually does.

5.4 Synchronising with ADS When committing changes to the database from an AEL program through an ADS client, the client is not told immediately of the changes that have been made to the database. This is because the AEL program prevents the client from 'listening' for messages from ADS.

If you need to ensure that your program is up-to-date with the database while it is still running, you can use the poll( ) function to synchronise your program with ADS.

You might also want to have an AEL program running over a period of time, 'listening' for changes to the database, which you can log in your own way, or deal with in a manner appropriate for your operational procedures.

There are three methods/functions designed to help you here. They are the methods subscribe( ) and unsubscribe( ) and the function main_loop( ). You will also need to write your own functions to handle callbacks from ADS.

5.4.1 Callback handlers Callback handlers are AEL functions that are called whenever a change is made in a subscribed set of objects.

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 35 of 72

Note that inserts, updates, and deletes on a set are not necessarily the same as their equivalents in the database. For instance, if a trade is moved from one portfolio set to another, then the trade is deleted from one portfolio set, and inserted into the other, a change which is reflected by an UPDATE in the database, but DELETE and INSERT in the relevant sets.

Note: When reloading a module that contains a callback function, AEL removes subscriptions to the function to avoid duplicate calls to the function. If the callback function is in another module from the one where the subscription was made, this means that the subscription module needs reloading.

The exception to this is if the callback function is embedded within another function. AEL does not remove subscriptions for these local functions.

If an entity is updated and saved to the database in a callback function, this will cause the program to run into an infinite recursion loop, since an update of the entity triggers a call of the function. The function updates the trade that triggers a call and so on. A solution to this problem is to recognise the update as being invoked by the callback handler, by means of checking for instance update_usrnbr.

5.4.2 Syntax func(object, entity, arg, event)

Where the arguments are as follows:

Argument Type Description

func function Name of function to handle callbacks for the specified set of objects.

object ael_table, ael_selection, or ael_entity

An ael_table, ael_selection, or ael_entity object that has been subscribed. In the case of subscriptions to ael_entity objects, this argument is the same as the next one.

entity ael_entity The ael_entity object that has changed in the object. If the object is an ael_selection object created with one of the selection methods, and the 'parent' is deleted, None is passed.

arg any The optional arguments specified in the subscribe( ) method. If no argument is specified, None is passed to the function.

event string The type of change that has been made to the set of objects, one of insert, update, or delete.

Example def sub_func(obj, ins, arg, event): print ("%s %s" % (ins.insid, event)) def set_sub_func(obj, trade, arg, event): if trade: print ("%s %d %s" % (obj, trade.trdnbr, event)) else: print ("Instrument deleted") ins = ael.Instrument['EUR/FRA/3.5'] trade_sel = ins.trades( ) ins.subscribe(sub_func) trade_sel.subscribe(set_sub_func)

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 36 of 72

5.5 Updating data with acm To update persistently stored objects in the simplest manner, you can do the following:

i = acm.FInstrument['drOpt'] i.StrikePrice = i.StrikePrice( ) + 1 i.Commit( )

The change becomes visible throughout the current session as soon as you assign the new value to StrikePrice( ) and is persisted to the database once the Commit( ) method is called.

When a value is modified on a stored object, then the whole object will change. This means that:

• The IsModified( ) method will return True. • The Undo( ) method will undo all changes and revert back to the stored version. • The Commit( ) method will store the change in the database. If the Commit( ) method fails,

for some reason, all changes to the object will be undone and the object will be reverted back to the stored version.

• If the object is updated in the database from another session, this update will not be reflected in the current session.

• The object cannot be opened in some windows, for example the instrument definition window, when it is in a modified state. Once Commit( ) or Undo( ) has been performed it can be opened again.

The last three points might cause problems. Therefore, it is recommended to work with clones when updating objects. There are two types of clones:

• Standard clone • Storage image

5.5.1 Using standard clones When using standard clones, the Clone( ) method is used, which creates a copy of the original object. All changes are performed on the clone, which are later transferred to the original object by using the Apply( ) method just before doing the commit of the original object. If the commit of the original object fails, the original object is reverted back to the stored version, but the clone is left with the changes so that the errors (for example, missing references) can be fixed.

t = acm.FTrade[23] clone = t.Clone() clone.Quantity = 10 t.Apply(clone) t.Commit()

Note: If the clone is committed instead of being applied back to the original trade, a new trade will be created and stored in the database.

RegisterInStorage Some functionality (see 5.5.5 Best practices) requires that objects are registered in the internal storage cache, which holds local entities for and at the client. A standard clone will not be registered in storage automatically. To do this, the RegisterInStorage( ) method can be used on the clone.

newTrade = acm.FTrade() newTrade.Instrument = "BMW" newTrade.RegisterInStorage() newTrade.Quantity = 10

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 37 of 72

newTrade.Commit()

5.5.2 Using storage images A storage image is an editable copy of a persisted object with the following features:

• When a storage image is committed, all changes will be stored on the original object. • Similar to the clone method in AEL. • Original will never end up in a modified state. • If the commit of the storage image succeeds, the changes will be persistently stored on the

original object. • If the commit fails, the original will be unchanged. The storage image will keep all

previously information, which makes it possible to implement the required changes. Creating a new storage, for example for a trade:

t = acm.FTrade[23] image = t.StorageImage() image.Quantity = 10 image.Commit()

StorageSetNew A storage image can be disconnected from its original by using the StorageSetNew( ) method. After this method call, the cloned object it is no longer a storage image. Instead it is a standard clone that is registered in storage. When the clone is committed, a new object will be created and the original object is unchanged.

t = acm.FTrade[23] image = t.StorageImage() image.StorageSetNew() image.Quantity = 10 image.Commit()

StorageNew If you know in advance that you want to disconnect the storage image from its original, then this can be achieved directly by using the StorageNew( ) method. When the clone object returned by StorageNew( ) is committed, a new object will always be created.

t = acm.FTrade[23] newTrade = t.StorageNew() newTrade.Quantity = 10 newTrade.Commit()

5.5.3 Reverting changes The Undo( ) method can be used to remove any changes made to an object. In the following this is used in an exception handler to remove any partial changes made if an error occurs:

i = acm.FInstrument['drOpt'] try: i.StrikePrice = i.StrikePrice( ) + 1 i.StrikeType = 'Delta' i.Commit( ) except Exception: i.Undo( ) raise

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 38 of 72

5.5.4 Transactions Standard transaction support is available through the acm.BeginTransaction( ) and acm.EndTransaction( ) methods:

t1, t2 = acm.FTrade['328274'], acm.FTrade['328273'] acm.BeginTransaction( ) try: tmp = t1.Quantity( ) t1.Quantity = t2.Quantity( ) t2.Quantity = tmp t1.Commit( ) t2.Commit( ) acm.CommitTransaction( ) except: acm.AbortTransaction( ) raise i.Commit( )

Note that, these should not be nested (in practice they can be and a reference count of the transaction depth is kept, this will lead to counterintuitive effects, such as an AbortTransaction( ) not aborting changes if it is nested within an outer transaction block).

Important: You must not under any circumstances keep transaction locks when returning control to the Front Arena client.

This transaction mechanism interoperates with the one described for AEL.

5.5.5 Best practices These are the recommended ways to update values on ACM objects:

• When editing values on persisted stored objects, use StorageImage( ) to create an editable copy.

• When creating a new object from an existing object, use StorageNew( ). • When creating a new object from scratch, call RegisterInStorage( ) for the new object if any

calculations or business logic methods are going to be used on the new object.

5.6 Subscribing to data You can subscribe to objects by using the AddDependent( ) method:

import acm class TradeUpdateHandler: def ServerUpdate(self,sender,aspect,param): print ("Trade nbr ", sender.Oid( )) print ("Aspect ", aspect) #print ("Param ", param) # the trade again print ("Quantity ", sender.Quantity( )) u = TradeUpdateHandler( ) t1 = acm.FTrade['328274'] t1.AddDependent(u) t1.Quantity = t1.Quantity( ) + 1 t1.Commit( ) t1.RemoveDependent(u)

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 39 of 72

You can subscribe to any object in the system, including persistent collections. The following code will subscribe to additions, deletions or updates of trades to the relevant instrument:

import acm class UpdateHandler: def ServerUpdate(self,sender,aspect,param): print ("Sender ", sender.StringKey( )) print ("Aspect ", aspect) print ("Param ", param.StringKey( )) u = UpdateHandler( ) acm.FInstrument['drOpt'].Trades( ).AddDependent(u) t1.Commit( )

5.7 Deleting child entities in AEL If you try to remove a child entity like a payment to a trade or a dividend estimate, you will need to clone the parent entity first, then delete the child and recommit the parent.

If you try to remove a dividend estimate using statements like the following, you will get an error message, 'Attempt to modify original entity':

de = ael.DividendEstimate[1438] de.delete( )

To delete a dividend estimate you have to first clone its parent, the dividend stream; to delete a payment, you first have to clone the trade. After you have cloned the parent, you can delete the child and recommit the dividend stream or trade.

Example import ael de = ael.DividendEstimate[1438] dsc = de.stream_seqnbr.clone( ) for d in dsc.estimates( ): if d.seqnbr == de.seqnbr: #see note below d.delete( ) dsc.commit( )

Note that you cannot use the following comparison:

if d == de:

This is because the entities are not the same; one of them is a derivative of a cloning. (The seqnbrs are the same, however.)

5.8 Deleting child entities in ACM Deleting a payment on a clone trade and then applying it to the trade will not work. You cannot delete each separate payment from the clone's payments collection without also removing the payment object from the collection. If not removed from the collection, the original object will try to access the already deleted payment when committing, which causes the commit to fail. Something like the following is needed:

Example: deleting payments on a trade clone import acm PAYMENT_TYPES = ["Cash",] def updateTrade(aTrade):

Developer Guide: AEF Python (FCA2831-17)

5 Updating and subscribing to the ADM database 40 of 72

cTrade = aTrade.Clone() toBeRemoved = acm.FArray() for p in cTrade.Payments(): type = p.Type() if type not in PAYMENT_TYPES: toBeRemoved.Add(p) for p in toBeRemoved: cb2bTrade.Payments().Remove(p) aTrade.Apply(cTrade) aTrade.Commit()

There is no need to call p.Delete( ); this will be done automatically when updating the Payments collection in the Commit( ) method.

Developer Guide: AEF Python (FCA2831-17)

6 Entering Python variables at run time 41 of 72

6 Entering Python variables at run time

6.1 Using Run Script To get the parameter values your Python module needs from a user in run time, you can use the Run Script parameter GUI.

To get the parameter GUI to appear at run time, define the variable ael_variables in your code with a list of the parameters to be input:

ael_variables = [['year', 'Expiry Year (yyyy)', 'string', None, defaultDateList[0], 1, 0], ['month', 'Expiry Month (mm)', 'string', None, defaultDateList[1], 1, 0], ['day', 'Expiry Day (dd)', 'string', None, defaultDateList[2], 1, 0]]

The GUI does some validity checking of the values entered, ensuring that values are of the correct type and that mandatory values are entered, and it allows for some interactivity.

After you click Run, the system will call a function called ael_main. The input argument to this function is a Python dictionary containing the values you enter or select.

6.2 ael_variables format The name and characteristics of the variables you can enter in the Run Script parameter GUI are defined in a Python variable called ael_variables. This variable should be defined as a list of lists, one list for each variable.

Each list contains a number of arguments, as shown in the following table. Optional arguments can be omitted from the end of the list, or by setting the argument's value to None.

Argument Type Mandatory Description

Variable name

string Yes The internal name of the variable.

Display name string Yes Name displayed in the AEL Variables window. If this name contains a '_' character then it is interpreted as follows: fieldlabel_tabname Where a tab will be created for each unique tabname and all variables with that tabname will be placed on it.

Type string Yes One of 'string', 'char', 'float', 'double', 'date', 'int', or 'time', or the name of an ACM class. (For the Boolean type, see 'Candidate values'.)

Candidate values

list No List of candidate values as strings. This is a suggested list only and not a domain of valid values. Users are free to enter any other values provided they match the type. Boolean fields can be coded by setting the 'Type' to 'int' and then specifying the candidate values as a list of [0,1], for example: ael_variables = [('bool', 'Boolean', 'int', [0,1])]

Default string No Default value displayed in the Run Script window.

Developer Guide: AEF Python (FCA2831-17)

6 Entering Python variables at run time 42 of 72

Argument Type Mandatory Description

Mandatory int No If 0, variable is not mandatory, if 1 variable is mandatory. If not specified, default value is 1, that is, mandatory.

Multiple int No If 0, multiple values are not permitted, if 1, multiple values are permitted. If not specified, default value is 0.

Description string No A description that will be used as a tool tip for the given field.

InputHook callable No A function takes two arguments, it will be called when the field is changed by the user. Default is None.

Enabled int No If this is set to 0 the field will be disabled, otherwise it will be enabled.

CustomDialog callable No The name of a custom dialog for selecting entity pairs, for example currency pairs and portfolios, to map from one to another.

6.2.1 Using InputHook Input hooks are used to implement GUI logic. The first argument to hook is an index of variable as defined in ael_variables, the second argument to hook is current edit values of all variables. If the hook changes anything (including of course current edit values) it must return current edit values.

Example A hook implementation that disables a GUI field when you select a check box: def snapshot_cb(index, fieldValues): ael_variables[11][9] = (fieldValues[10] != 'True') return fieldValues

Example A hook that adds a choice to a list in run-time: def add_cb(index, fieldValues): choices.append(fieldValues[17]) return fieldValues

6.3 ael_main( ) This function is invoked after you click OK or Apply in the Run Script parameter GUI after the system has checked the validity of the values of each variable you enter. Note that the module is not reloaded. If any are invalid, an error message is displayed in the Python log window and this function is not invoked.

6.3.1 Syntax ael_main(dict)

Here dict is a Python dictionary object. The keys of the dictionary are the internal names of each variable, and the values are the values you enter.

Example import ael

Developer Guide: AEF Python (FCA2831-17)

6 Entering Python variables at run time 43 of 72

dates = ['2001-01-01', '2001-04-01', '2001-07-01', '2001-10-01'] def trades( ): trades = ael.Instrument['EUR/FRA/3.5'].trades( ) tn = [] for t in trades: tn.append(str(t.trdnbr)) return tn def parties( ): parties = ael.Party.select('type="Counterparty"') pn = [] for p in parties: pn.append(p.ptyid) return pn ael_variables = [['trdnbr', 'Trade', 'int', trades( ), None, 1, 1], ['party', 'Party', 'string', parties( )], ['price', 'Price', 'float', ['123.4', '156.7', '183.5'], '156.7'], ['date', 'Date', 'date', dates, None, 0]] def ael_main(dict): print (dict["trdnbr"], dict["party"], dict["price"], dict["date"])

This will open the following Run Script window:

6.4 Starting the Run Script parameter GUI from a script You can start the RunScript parameter GUI from a script. You can also pass data from a script to another script that will be executed by the parameter GUI, making it possible to collect data from, for example, the active sheet and the user and then further process that data in the second script.

Two methods are available on the FTmServer class for this purpose; RunModuleWithParameters( ) and RunModuleWithParametersAndData( ). Both will execute the module, and if they contain ael_variables and ael_main, the Run Script parameter GUI will be shown.

If RunModuleWithParametersAndData( ) is used and the "data" parameter is not NULL, ael_main_ex will be called instead of ael_main. ael_main_ex should take two parameters; the first one is the same as for ael_main, and the second one is a dictionary where the data passed to RunModuleWithParametersAndData( ) is located at key customData.

Developer Guide: AEF Python (FCA2831-17)

6 Entering Python variables at run time 44 of 72

Example #module RunModuleWithData import acm scriptData = 'Data From Script' acm.RunModuleWithParametersAndData( 'HandleModuleWithData', 'Standard', scriptData) #module HandleModuleWithData import acm ael_variables = [ ['Decimals', 'decimals', 'int', [0,1,2], 1, 0, 0, 'Tooltip1', None, None],] def ael_main_ex(parameter, addData): print (addData.At('customData')) print (parameter['Decimals'])

6.5 Performance when using candidate values Creating candidate values by calling a function that is reading data from the database might impact performance. Often candidate values are created by calling a function like in the following example.

Example def readinstr( ): ins = ael.Instrument.select( ) ls = [] for i in ins: ls.append(i.insid) return ls ael_variables = [('ins_list', 'Instruments', 'string', readinstr( ), None, 1, 0), …

Reading a large number of records causes high memory consumption and may take a long time to execute. Reading some 100,000 instruments will consume more than 100 MB of memory.

Instead of populating a fixed list, you can use the Insert Items window by using the ACM class CreateFASQLQuery to select values like in the following example.

Example import ael, acm def insertIns( ): q = acm.CreateFASQLQuery(acm.FInstrument, 'AND') return q ael_variables = [('inslist', 'Instruments', 'FInstrument', insertIns( ), None, 1, 1), …

Developer Guide: AEF Python (FCA2831-17)

7 User-Written ARENA Functions 45 of 72

7 User-Written ARENA Functions As well as being able to call ARENA functions from Python functions, you can write Python functions that can be invoked from ASQL and ADFL in the same way as the standard supplied functions. Indeed, this is the principal way that you can access Python functions through the graphical user interface.

7.1 Method 1 (ASQL only) Python functions can be invoked in ASQL by calling them directly as with other ARENA and ASQL functions. When a function is called, the system assumes it is an ARENA or ASQL function and attempts to invoke it in the normal manner. If no such ARENA or ASQL function exists, then the system will invoke a function by the same name if it exists in the AEL module FSQL_functions.

When you are writing your own functions, you can either define them in the FSQL_functions module, or in another module, and append the module name to the function when making the call:

mymodule.lc_userid

To give a trivial example, you could code this function in the module called FSQL_functions to convert a user ID in the User table to lower case:

def lc_userid(params): value = params[0][0].userid.lower( ) return value

You could then invoke this Python function with this ASQL code:

select u.userid, lc_userid(u) from user u where u.userid = 'JOHN'

Your function receives a list of lists, where the inner list contains the argument for one call from SQL. So ASQL in effect can batch many calls together, this is done when group is used to enable certain optimisations, try the above with the following ASQL code and notice that there is only one call:

select u.userid, lc_userid(u,1), u.inactive from user u where u.userid < 'D' group by u.inactive

The result type returned by the function is deduced from the context of the function. However, it is also possible to explicitly define what result type the function should return by using a data type identifier after the function name.

Identifier Data type

#c Character

#d Date

Developer Guide: AEF Python (FCA2831-17)

7 User-Written ARENA Functions 46 of 72

Identifier Data type

#i Integer

#f Double

#s String (max length = 256)

#t Time

From the previous example:

sql.lc#s(u)

This can be used to explicitly specify that sql.lc should return a string.

In the case where the string identifier is used it is possible to write for example #s25 to limit the maximum length to 25 characters.

7.2 Method 2 There are also five user-written ARENA functions in AEL that you can use to invoke your Python functions. The names of the Python functions themselves are arguments of these user-written ARENA functions, which are given in the following table, together with the data type returned by the function.

User-written ARENA function Data type

ael_c Character

ael_d Date

ael_f Float

ael_i Integer

ael_s String

7.2.1 Syntax ael_t(table, [module.]function [, param] …)

Where the variables are:

Variable Description

T The data type returned by the function, c, d, f, i, or s.

Table A reference to records in any table.

Module The AEL module where the function is stored. The default is FSQL_functions.

Function The name of the Python function.

Param Up to nine optional arguments.

The following is a simple example of this:

Example def mypv(i,scale,*rest): print ("call to mypv for ", i) return i.present_value( ) * scale select i.insid, ael_f(i,'mypv.mypv',3)

Developer Guide: AEF Python (FCA2831-17)

7 User-Written ARENA Functions 47 of 72

from instrument i where i.insid = 'ABB'

Developer Guide: AEF Python (FCA2831-17)

8 Exception handling 48 of 72

8 Exception handling

8.1 Exception handling in AEL AEL uses the standard error handling mechanism provided by Python, providing additional information where necessary. When testing, you can use the standard traceback information provided by Python to obtain error messages. This provides a traceback of line numbers of each function in the current call stack.

If a user-written Python function that is called from either the Information Manager or through an application hook fails, Python raises an exception that is handled by the system. In this way, errors in user functions should not affect the robustness of the system and are reported appropriately to the user.

However, it is often more elegant and meaningful to users to trap potential errors and make a specific report without the traceback. In this case, you either need to test for potential errors before they occur or use the Python try … except statement.

It is probably easier to use the first method when there is a possibility that no ael_entity object is returned when you expect it.

Example This code raises an AttributeError if JOHN is not a user ID in the User table. You can do either: try: grpnbr = ael.User['JOHN'].grpnbr #code that does something with grpnbr except AttributeError as err: print ("User John not found")

or: user = ael.User['JOHN'] if user: grpnbr = user.grpnbr else: print ("User John not found")

Tip: For simple cases like the above the second approach is much faster for cases where the user is not found (where a lot of exceptions are thrown)

AEL uses the Python errors TypeError, RuntimeError, and IndexError. Refer to http://www.python.org/doc.

8.1.1 TypeError AEL TypeErrors are mostly raised when functions and methods are called with the wrong argument type or enumerated value. In these circumstances, AEL provides additional information to that provided by Python. For example, in functions that require a date period or a daycount method, if these are incorrect, AEL tells you.

8.1.2 RuntimeError You are most likely to encounter RuntimeErrors when you specify the wrong type of column in read( ) and select( ) functions.

Developer Guide: AEF Python (FCA2831-17)

8 Exception handling 49 of 72

Example Statements raising RuntimeErrors of various types: ael.Trade['foo'] RuntimeError: table Trade has no unique name ael.Instrument.read('instype="Bond"') RuntimeError: instype is not a unique key ael.User.read('grpnbr="BO"') RuntimeError: Invalid constraint ael.User.read('foo="BO"') RuntimeError: Invalid constraint ael.Instrument.select('insid=2') RuntimeError: Failed to create selection ael.Instrument.select('foo=2') RuntimeError: Invalid constraint

AEL also raises RuntimeErrors when an error is encountered in the underlying system, for example, in ADS. In these cases, AEL passes on the message it is given to give an indication of what is happening.

8.1.3 AEL IndexError The AEL IndexError is raised when a subscript to an ael_selection object is out of range. For example, this statement is likely to raise an IndexError in most installations:

ael.User.select( )[100000]

8.2 Catching exceptions in ACM You can use the Python exception mechanism to handle exceptions. A couple of simple examples describing this process are as follows.

import acm msg = 'Zero or one stocks expected' try: stock = acm.FStock.Select01("contractSize=1", msg) except RuntimeError as e: print (e)

If many stocks have a contract size of one, an exception is thrown with the message value of msg which was passed to the Select01( ) method. Although most exceptions thrown by the ACM are converted to the rather generic RuntimeError some others are used, for example:

import acm a = acm.FArray( ) a[0] = "Fred" try: print (a.IntAt(0)) except TypeError as e: print (e)

Developer Guide: AEF Python (FCA2831-17)

9 The run time environment 50 of 72

9 The run time environment This section covers some aspects of Python's interaction with the Front Arena run time environment.

9.1 Logging The Front Arena log is available through the function log. For example:

ael.log("Error failed to open input file")

This will be output both to screen and to the log file and is a good way to display important user messages.

9.2 The site-startup module The site-startup module is imported once when the Front Arena client is started. All code in the module will be executed at this time.

One use of this module is to add directories to the Python path:

import sys sys.path.append(r"c:\development\test\valuation_modules")

Another typical use is to display a message to the user in the log window.

9.3 Configuration variables Information about the settings for the configuration variables (set as environment variables, arena.ini (for PRIME 2009.2 and earlier) or arena_<version>.ini (for PRIME 2010.1) and the command line) can be fetched with the function get_config_var:

print (ael.get_config_var('logdir'))

This can be used to get information about used directories, servers and any command parameters used.

9.4 The standard library Python has a large standard library which provides extensive functionality within many areas (the Python marketing slogan is that Python is delivered 'batteries included'). This library is included with the Front Arena clients. It is in a special directory, CommonLib, which is in the root of the Front Arena installation. This directory contains two subdirectories both of which are appended to the Python path at the start of the client:

• PythonLib37: This directory contains the standard library + the .dlls that are part of the standard library (in a normal Python distribution these are in a separate directory called DLLs).

• PythonExtensionLib37: This contains any extension you wish to have available to all clients. By default, the calldb and sybdb extensions are located here.

Note: The 37 in the directory names represents the Python version. If an older client is installed it will be called, for example, PythonLib27.

Developer Guide: AEF Python (FCA2831-17)

10 The sybdb and calldb modules 51 of 72

10 The sybdb and calldb modules There may be occasions when you need to access the ADM database directly, bypassing ADS completely. This can either be accomplished by using either third-party modules or the sybdb and calldb modules supplied with Front Arena.

Direct access to the ADM database is provided through the sybdb and calldb modules, which you need to import into Python. The sybdb and calldb modules provide access to Sybase ASE and SQL Server, respectively, and consist of one function, connect, that creates a sybdb_connection or calldb_connection object, and a class error, which you can use for catching RDBMS errors. The sybdb_connection and calldb_connection objects have two methods, execute( ) and close( ), which are described in this section.

Note: The sybdb and calldb modules use functions in the Sybase ASE and Microsoft Open Client Libraries, respectively, so whether you are using sybdb from Linux or Windows or calldb from Windows, you need to ensure that users of these modules have access to one or other of these Open Client Libraries.

10.1 Setting up sybdb When using sybdb from Python or PRIME client PC, you will need to install the Sybase Open Client libraries from the ASE Sybase Central installation for Windows to get all the correct DLLs installed. The default Sybase Windows installation is on C:\Sybase. Change as required.

Example

Select the custom installation option and select the libraries:

Developer Guide: AEF Python (FCA2831-17)

10 The sybdb and calldb modules 52 of 72

When installation is complete, add the Sybase ASE server to the configuration by running dsedit from Start>Programs>Sybase>Connectivity>Open Client Directory Service Editor:

Add the Sybase database, save and quit.

Developer Guide: AEF Python (FCA2831-17)

10 The sybdb and calldb modules 53 of 72

Right-click on the server Address and choose Ping to verify that the connection is OK. It will create the Sybase Windows interfaces file after exiting in the .\Sybase\ini\ folder called sql.ini (for example, C:\Sybase\ini\sql.ini).

Example content: [SYBASE] master=TCP,teststo05,5000 query=TCP,teststo05,5000

Make sure the Sybase environment variables have been created correctly by running c:\>set in a command window. The important variables are SYBASE=C:\Sybase and SYBASE_OCS=OCS-15_0.

Test the set up in Python Editor:

import os import sybdb import ael #================= Check/Set SYBASE System Variable ================================= try:

Developer Guide: AEF Python (FCA2831-17)

10 The sybdb and calldb modules 54 of 72

if os.environ['OS'] == 'Windows_NT': os.environ['SYBASE'] = r'c:\sybase' except: pass print ('Sybase system variable = ',os.environ['SYBASE']) import sybdb c = sybdb.connect('ADMIN', 'secret', 'sb_serv33') print (c) qe = 'select @@version' ase_version = c.execute(qe) print (ase_version)

The result should look like this:

<sybdb connection, sa at 9bc2590> [[['Adaptive Server Enterprise/15.0.3/EBF 17156 ESD#3/P/Sun_svr4/OS 5.8/ase1503/2726/64-bit/FBO/Fri Feb 5 05:26:23 2010']]]

10.2 Sybdb and calldb functions and methods

10.2.1 The connect( ) function Creates a sybdb_connection or calldb_connection object, enabling access to the Sybase ASE or SQL Server database, respectively. You can have more than one RDBMS connection open at a time.

Syntax sybdb.connect(userid, password, server)

or

calldb.connect(server, userid, password [,encrypt])

Here the arguments are:

Argument Data type Function

userid string RDBMS user ID

password string RDBMS password

server string For calldb, the server ID. For sybdb, the Sybase instance name entered in the Interfaces file. The Interfaces file is the file that specifies what instances of Sybase ASE can be read or connected to from that particular machine. If you need from another machine to the Sybase instance 'TUX02' you would need to put that entry into the interface file for TUX02.)

encrypt integer (Only sybdb) Disable password encryption if set to 0. Default is 1. Sybdb can by default connect to a SYBASE instance running "net password encryption reqd", X (X can be 1 or 0 but not 2).

Examples sybdb.connect('ADMIN', 'secret', 'sb_serv33') calldb.connect('ms_serv33', 'ADMIN', 'secret')

Developer Guide: AEF Python (FCA2831-17)

10 The sybdb and calldb modules 55 of 72

10.2.2 The execute( ) method Executes SQL statements in Transact-SQL, Sybase's or Microsoft's dialect of the SQL language, as described in Sybase Adaptive Server Enterprise Transact-SQL User's Guide and the MSDN Library.

In the case of a query, the method returns a list of lists of lists. There is one list for each SELECT statement in the query not joined by a UNION statement. Each of these lists consists of a list of all records returned. And each record is returned as a list of fields.

So, if a is assigned to the result of a single SELECT statement, a[0][3][2] is the third field in the fourth row (a[union][row][field]).

Syntax c.execute(sql_statements)

Here c is a sybdb_connection or calldb_connection object and sql_statements is a string consisting of one or more SQL statements.

Example q = 'select userid from user_' c.execute(q)

Note: Some tables in the RDBMS database have different names from those used in the ADM. As user and group are reserved words in Transact-SQL, in the RDBMS the corresponding tables to User and Group in AEL are user_ and group_, respectively.

In general, other tables that have a capital letter other than in the first letter have the capital letter replaced by a preceding underscore. For example, TradeFilter in AEL becomes trade_filter in the RDBMS database.

One exception to this rule is EMUCurrency, which becomes emu_currency in the RDBMS. The other exceptions are the risk factor tables. The prefixes of these tables are converted from RiskFactor to rf_. For example, RiskFactorSpec becomes rf_spec.

Refer to the ADM section of AEF Browser.

10.2.3 The close( ) method Closes the connection to the SYBASE ASE or SQL Server database.

Syntax c.close( )

Here c is a sybdb_connection or calldb_connection object.

Examples c = sybdb.connect('ADMIN', 'secret', 'sb_serv33') c.close( ) c = calldb.connect('ms_serv33', 'ADMIN', 'secret') c.close( )

Note: The close( ) method does not destroy the sybdb_connection or calldb_connection objects. If you try to use it after closing the RDBMS connection, you get an error.

Developer Guide: AEF Python (FCA2831-17)

11 Using third-party Python modules 56 of 72

11 Using third-party Python modules Python has a rich set of third-party modules, many of which are open source, that provide functionality that can be useful within a Front Arena perspective, such as:

• Numeric calculations, similar to MATLAB, linear, optimisation ODE and so on. • Plotting packages. • Database access. • COM support, and so on. There are a couple of strategies for installing external packages.

11.1 Adding module to ADS For very small packages, which are pure Python code and no compiled extensions, you can add the file to the ADS using the Python Editor. This has the advantage that it is directly available to all users of the system.

11.2 Network file install For larger packages, which may include compiled extension or a nested package structure (see Python documentation for details of packages). You have to keep the files on disk; in this case, you can copy the installation to a network drive and then add this path to the Python path either in the calling module or in site-startup. This approach has the advantage that it is quite simple to administer. On the disadvantage side, you are now dependant on an extra network location.

11.3 Adding to PythonExtensionLib You can add the needed files under the PythonExtensionLib directory (described previously). This is a robust solution, but has the disadvantage of requiring installation on each machine that will be using the module.

11.4 Full Python install If Python is installed on the machine, you can install the third-party module as usual, and it will be available to Front Arena clients running on that machine. For some modules that require use of the Windows registry (such as for full usage of the win32com module) this is the only solution.

Developer Guide: AEF Python (FCA2831-17)

12 Sample Python programs 57 of 72

12 Sample Python programs This section contains some examples of AEL functions that illustrate many of the concepts in this manual. For further examples of AEL and ACM Python code, see the scripts that are delivered as part of Front Arena.

12.1 Subscriptions """timeseries - Sample of how to use subscriptions and transactions in AEL. This program stores an entry in the TimeSeries table for each new and updated trade with status (containing) Confirmed. A new run_no will be added with each update.""" import ael, string def store_market_value(t, ts_spec): """Store the market value for the trade in the TimeSeries table 'Market Value' with a new run_no for each update.""" day = ael.date_today( ) run_no = 0 value = t.used_price( ) # find max run_no from existing series for ts in t.time_series( ): if ts.ts_specnbr == ts_spec and ts.day == day: run_no = max(run_no, ts.run_no) # store new value in TimeSeries ts = ael.TimeSeries.new( ) ts.recaddr = t.trdnbr ts.ts_specnbr = ts_spec ts.day = day ts.run_no = run_no + 1 ts.value = value ts.commit( ) def trade_update(o, t, arg, op): """Add market value for confirmed trades.""" if op in ['insert', 'update']: if string.find(t.status, 'Confirmed') > 0: store_market_value(t, arg) def start( ): """Start subscription on the trade table.""" ts_spec = ael.TimeSeriesSpec['Market Value'] if not ts_spec: raise Exception("TimeSeriesSpec 'Market Value' not found'")

Developer Guide: AEF Python (FCA2831-17)

12 Sample Python programs 58 of 72

ael.Trade.subscribe(trade_update, ts_spec) if __name__=="__main__": # Called from command line, connect first ael.connect('sun23:7771', 'FRED', 'secret', 'TimeSeriesSample') start( ) ael.main_loop( ) else: # Called from GUI client, already connected start( )

12.2 Custom broker fee calculation See Developer Guide: AEF Basic Extensions (FCA3724) for a slightly more detailed example.

Example In this example, there is a function that is called whenever a brokerage fee on a trade is calculated. That is, when a trade is entered in an instrument definition/trading window, or the Acquire Day, Broker, Price, Quantity, Trade Time, or Premium field value changes (or any field that changes those fields)

Open a new Python Editor window and enter this function: import ael def broker_fee(t): print ('broker_fee called') print (t.trader_usrnbr.userid) print (t.broker_ptynbr.ptyid) print (t.quantity, t.price) if t.trader_usrnbr.userid == ael.userid( ): return 1234

The argument, t, is the trade whose fee is to be calculated. Save the module with the name FBrokerage.

Now open a trade where you are the trader. Make any change to the trade, like quantity, price, or broker. You should see the fee 1234 appear in the fee field. You can change the function, and see the results in the trade entry window each time you make a change.

The function, as written, also prints some attributes of the trade in the Python log window. You would normally use such attributes in calculating brokerage fees according to the business rules of your organisation.

12.3 Adding an additional info field Example In this example an additional info field is added onto a trade record. import ael spec = ael.AdditionalInfoSpec['Rebate Factor'] e = ael.Trade[1234] e_c =e.clone( ) ai = ael.AdditionalInfo.new(e_c) ai.addinf_specnbr = spec

Developer Guide: AEF Python (FCA2831-17)

12 Sample Python programs 59 of 72

ai.value = str(0.25)

This demonstrates the basic principles of working with cloned objects and how the child record (additional info) requires a reference to the parent (the trade) when it is created.

12.4 Populating and reading data fields in PriceDefinition To access fields with brackets, like PriceDefinition.data[0] use the Python functions getattr and setattr as shown in the example below:

Example import ael #Retrieve an attribute pdis = ael.PriceDistributor['AMPH_EUREX'] ins = ael.Instrument['ABBN09F14'] curr = ael.Instrument['CHF'] market = ael.Party['EUREX'] pdef = ael.PriceDefinition.read('disnbr=%d and insaddr=%d and curr=%d and source_ptynbr=%d'% (pdis.disnbr,ins.insaddr,curr.insaddr,market.ptynbr)) field = 'data[0]' print (getattr(pdef,field)) #Update an attribute newattr = 'ABBN09F14' pdefc = pdef.clone( ) setattr(pdefc,'data[0]',newattr) pdefc.commit( )

A working example for the limit fields in the CreditLimit table is available in AEF Browser.

Developer Guide: AEF Python (FCA2831-17)

13 Python coding best practices 60 of 72

13 Python coding best practices This section has some recommendations with regards to writing Python code for use within the Front Arena system.

13.1 General

13.1.1 Avoid direct SQL access There are functions for directly accessing the SQL database and in effect bypassing the ADS. It may be tempting to use these from a performance perspective, however because they bypass both the ADS and the local client cache this may degrade performance. For applications involving complex joins where you require the index aware optimisation provided by an SQL engine access to ASQL is available through AEL. You should only resort to the direct SQL execution in cases where it is unavoidable, for example, if you have to call a stored procedure.

13.1.2 When to use acm and when to use ael There is significant functional overlap between the ael and acm modules. For example, a script to import trades from a text file and create them in the ADS could be written using either the acm or ael module. Some guidelines for choosing which module to use:

• Use only one of the modules, avoid mixing usage unless necessary. • Prefer ael for data import/maintenance tasks that are geared towards the ADM. • Use acm when interacting with ACM objects such as in menu extensions.

13.1.3 Code conventions Follow the normal Python convention as described in http://www.python.org/peps/pep-0008.html.

Indent with 4 spaces. Never have tab characters in the Python code.

Avoid using F as a prefix character in module names as this is used by Front Arena.

13.2 Error handling

13.2.1 Catching errors early and indicating location clearly A good example where error handling is required is a simple import statement for an external library, such as win32com:

import win32com

If this library is not properly installed, you will get an ImportError which will be confusing to end users. In this case, you can use the standard exception handling mechanism:

try: import win32com except ImportError: ael.log("Failed to import win32com extensions") print ("To fix this download Python Windows extensions from …") raise

There are several important points about this code:

• It gives a better explanation of the error and more importantly how to resolve it then the default behaviour.

• It writes a message to the ael.log which greatly helps with troubleshooting. • It raises the error again so that program execution is halted.

Developer Guide: AEF Python (FCA2831-17)

13 Python coding best practices 61 of 72

Often it is possible to catch an exception, report the error, abort the current activity, and continue after the exception handling. In these cases, there is no traceback, just printing an error message, which is often not that helpful in locating the source of the problem at a later date. What you really want to do is to print an error and a traceback but continue program execution. The following snippet of code can be used to do this.

import traceback import sys def dump_trace( ): traceback.print_exc(file=sys.stdout) sys.stdout.flush( ) return str(sys.exc_value)+"".join(traceback.format_list( \ traceback.extract_tb(sys.exc_traceback)))

For more advanced applications the built-in Python module logger has similar capabilities.

13.2.2 Being specific in exception handler When catching exceptions, keep handlers as specific as possible. Prefer:

try: fileh = open("c:\\datafile") except IOError: print ("File not found creating ") open("c:\\datafile.txt")

To:

try: fileh = open("c:\\datafile") except Exception: print ("File not found creating ") open("c:\\datafile.txt")

Having handlers that catch all errors, such as the one in the second example, will eventually lead to the handler catching errors unintentionally. These kinds of problems are hard to track down.

13.3 Updates and subscriptions

13.3.1 Avoiding infinite recursion in update handlers When setting up subscriptions to database updates or writing hook code that is executed as an effect of updates (like validate_transaction), you should avoid any use of ael.poll, as this can cause new updates to be sent out to the system. You should also have a strategy in place to make sure that any changes you make do not result in new updates that will trigger an infinite loop.

There are several strategies for doing this, ranging from checking what process made the last update to moving the update work out of the callback handler by placing it in a global queue and having a periodic process perform the updates that are needed.

13.4 Performance

13.4.1 Using static ACM structures whenever possible Unlike the built-in Python datatypes, FDictionary and other hash based ACM data structures can have mutable objects as keys. This is convenient but can lead to strange situations:

d = FDictionary( )

Developer Guide: AEF Python (FCA2831-17)

13 Python coding best practices 62 of 72

key1 = FArray( ) key1.Add(1) key1.Add(2) key2 = FArray( ) key2.Add(1) d[key1] = "key1" d[key2] = "key2" key2.Add(2)

This creates a dictionary with two identical keys. One of them is effectively lost and it is undefined which one will be retrieved. Situations like this should be strictly avoided and one method to eliminate the risk of this occurring is to use static structures such as FStaticArray and FStaticDictionary.

Using static structures for keys will also increase performance significantly.

Developer Guide: AEF Python (FCA2831-17)

14 Debugging Python in PRIME using Winpdb 63 of 72

14 Debugging Python in PRIME using Winpdb This section describes how to perform embedded debugging of Python code within PRIME.

Embedded debugging using Winpdb is easy. No complicated installations are required and the PRIME source code does not have to reside outside of PRIME. In addition, Winpdb is capable of debugging multi-threaded programs.

14.1 About Winpdb Winpdb is a platform independent GPL Python debugger with support for multiple threads, namespace modification, embedded debugging, and encrypted communication, and is up to 20 times faster than pdb.

Winpdb is free and can be downloaded from http://winpdb.org/download/.

Winpdb requires Python 2.7.10 and wxPython, also free, downloadable from http://www.wxpython.org/.

14.1.1 Compatibility note Winpdb is compatible with PRIME 3.2.2 and subsequent releases but was not taken into use until PRIME 4.4. The instructions in this document are not entirely accurate for PRIME releases prior to 4.4. Most significant is that the module Frpdb2 does not exist in PRIME releases prior to 4.4.

14.1.2 Limitations It is not possible to debug Python code executed by the Trading Manager. Trying to do so causes Winpdb and PRIME to enter a deadlock.

14.2 Getting started To enable embedded debugging with Winpdb, two code lines must be added to the beginning of the script you want to debug:

import Frpdb2 Frpdb2.start_embedded_debugger("password", source_provider=callback_function)

When start_embedded_debugger is invoked, the script freezes for a five-minute period while waiting for the debugger to attach. The password is used to secure communication between client and server (debugger and debugee). For more information refer to the Winpdb documentation at http://winpdb.org/docs/embedded-debugging/.

source_provider is a Python function object. It is required for debugging in PRIME to make the Python source code available to Winpdb. This function takes a filename and returns the source.

14.3 Quick start instructions 1. Download the latest Winpdb from http://winpdb.org/download/ and install.

2. If you do not already have Python 2.7.10 installed, then download it from www.python.org/download/ and install.

3. If you do not already have wxPython installed, then download it from www.wxpython.org and install.

4. Start PRIME.

5. Select System>Extension Editor. 6. Select Home>New.

Developer Guide: AEF Python (FCA2831-17)

14 Debugging Python in PRIME using Winpdb 64 of 72

7. Set Type to FPythonCode and Module to the module where you want to save your extension.

8. Copy the following code into the editing pane:

FObject:Frpdb2 """----------------------------------------------------------------------- MODULE Frpdb2 - Wrapper module for rpdb2.py module. Used for embedded debugging in PRIME using the Winpdb debugger ( www.winpdb.org ). Function source_provider_ads is designed to be used as a call-back function for the source_provider parameter for rpdb2.start_embedded_debugger. It returns source code for FPythonCode and FAEL modules. In addition Frpdb2 overrides the function winlower so that module names are not modified. -----------------------------------------------------------------------""" import OS import acm def winlower( path ): """dummy winlower to force rpdb2 to leave filename as is""" return path import rpdb2 rpdb2.winlower = winlower from rpdb2 import * class FPythonCodeNotFoundError( Exception ): pass class FAelModuleNotFoundError( Exception ): pass def get_fpython_code_source( modulename ): ext = acm.GetDefaultContext( ).GetExtension( "FPythonCode", "FObject", modulename ) if not ext: raise FPythonCodeNotFoundError( "FPython code not found" ) source = trim_source( ext.AsString( ) ) return source def trim_source( source ): declarationIndex = source.find( '\n' ) + 1 source = source[ declarationIndex: ] source = source.replace( "\n¤","" )

Developer Guide: AEF Python (FCA2831-17)

14 Debugging Python in PRIME using Winpdb 65 of 72

source = source.replace( "...","" ) return source def get_fael_module_source( modulename ): module = acm.FAel[ modulename ] if not module: raise FAelModuleNotFoundError( "Ael Module not found" ) source = module.Text( ) return source def get_module_name( filename ): filename = str( filename ) splitname = filename.split( '/' ) if len( splitname ) == 2 and splitname[ 0 ][ 0 ] == "[" and splitname[ 0 ][ -1 ] == "]": context_info, modulename = splitname else: modulename = filename return modulename def source_provider_ads( filename ): modulename = get_module_name( filename ) acm.Log( "Winpdb searching for source in < %s >" % ( modulename ) ) source = "" try: source = get_fpython_code_source( modulename ) except FPythonCodeNotFoundError as err: try: source = get_fael_module_source( modulename ) except FAelModuleNotFoundError as err: f = None try: modulename = OS.path.normpath( modulename ) f = open( modulename, "r" ) source = f.read( ) except IOError as err: raise IOError( SOURCE_NOT_AVAILABLE ) finally: if f: f.close( ) return source def debug_prime( password ): start_embedded_debugger( password, source_provider=source_provider_ads )

9. Apply and save the changes.

Developer Guide: AEF Python (FCA2831-17)

14 Debugging Python in PRIME using Winpdb 66 of 72

10. Close the Extension Editor. (The Python module Frpdb2 is included in the extension module AEFDevTools, so as an alternative to creating Frpdb2 in the way described above you could also just import the AEFDevTools extension module.)

11. Create a Python script that invokes the debugger with the following two lines near the top:

import Frpdb2 Frpdb2.start_embedded_debugger( "test", source_provider=Frpdb2.source_provider_ads )

In this case test is the debug password. You will have to provide this to the debugger later. The function debug_prime( ) can be used as a shortcut:

import Frpdb2 Frpdb2.debug_prime( "test" )

12. Start Winpdb. (winpdb.pyw should be available from the Start menu, or use ../python27/Scripts/ winpdp_.pyw.)

13. Run your test script and attach Winpdb.

14. Reload the ael module in PRIME. PRIME hangs when start_embedded_debugger is invoked.

15. In Winpdb, select File>Attach. The Password window is displayed.

16. In the Password window, enter the debug password; test in this case. Click OK. The Attach window is displayed.

17. In the Attach window, select the PID and click OK. Winpdb opens the script source code.

The debugger is now waiting at the start_embedded_debugger function call. At this point you can use all the standard debugging commands available in Winpdb. As soon as you step into code that resides in PRIME, the source_provider callback will be invoked and the source code will be available in the debugger. Breakpoints can be set in this code once it is available in Winpdb.

To stop debugging, select File>Detach in Winpdb and the script will continue running normally.

Developer Guide: AEF Python (FCA2831-17)

14 Debugging Python in PRIME using Winpdb 67 of 72

14.4 The console The console can be used to evaluate Python code for doing such things as evaluating functions and inspecting variable values.

14.4.1 Execute code Python code can be executed using the command line. For example, running:

exec import acm; global x; x = acm.Version( )

sets the acm version to the variable x, the value of x is then shown in the Locals and Globals window in Winpdb.

To print the version in the Python log window in PRIME run:

exec import acm; print (acm.Version( ))

14.4.2 Conditional breakpoints To set conditional breakpoints use the bp command. From the Winpdb documentation, here is an example that sets a conditional break point to the first line of function foo( ):

bp foo, i < 100

14.5 Opening source files during debugging In some cases, you have to open the source file to set a breakpoint. When the debugger is attached, go to File>Open Source and enter the module name. The context is optional.

• For FPythonCode or FAel modules:

Developer Guide: AEF Python (FCA2831-17)

14 Debugging Python in PRIME using Winpdb 68 of 72

• For files or on disk:

14.6 Debugging alternatives For remote embedded debugging start the embedded debugger with the fAllowRemote flag set to true.

To debug a standalone Python module residing outside PRIME, start Winpdb and select File>Launch. Locate the script on disk and the script will start in Winpdb.

14.7 rpdb2.py workaround This is a workaround included in the Frpdb2 module and is for the Winpdb Python module rpdb2.py. This workaround is required for the module name to be passed to the source_provider callback. In the module rpdb2.py, the winlower( ) function is modified so that it does not lower case the path:

def winlower(path): """ Workaround. Returns lowercase version of 'path' on NT systems. On NT filenames are case insensitive so lowercase filenames for comparison purposes on NT.""" return path import rpdb2 rpdb2.winlower = winlower

Developer Guide: AEF Python (FCA2831-17)

15 ARENA Python 69 of 72

15 ARENA Python

15.1 What is ARENA Python? ARENA Python is a standalone Python interpretation in PRIME. From within ARENA Python you have access to both ACM and AEL in the same consistent manner as from other product components like PRIME, ARENA Extract, ATS, and AMBA.

Example C:\> C:\>"C:\Program Files\FRONT\FRONT ARENA\PRIME\2011.1\arena_python.exe" >>> import acm >>> print (acm.Time( ).TimeNow( )) 2010-11-24 19:11:42 >>> ^Z C:\>

Key features include:

• Data availability: All data in PRIME is available for extraction, including calculated values not stored in the database.

• Batch operation: Run Python scripts with arena_pyton.exe as part of a batch process. • Continuous real time operation: arena_python.exe subscribes to data updates from the

ADS just as other clients. This makes is suitable for having a process up and running under extended periods of time.

15.2 Using ARENA Python ARENA Python is purely a command line tool. Usage differs slightly between Windows and Linux.

15.2.1 On Windows arena_python.exe [options]

Where options are command line options, which determine the behaviour of ARENA Python. Generally, at least the options -server, -username, and -password are required. See 15.3 ARENA Python options.

Example c:>\ arena_python.exe -server Prod -username ARENAPYTHON -password aaa123

15.2.2 On Linux arena_python <configuration> [options]

Where configuration is the name of the Front Arena configuration that you are running, for example, prod or test, and options are command line options, which determine the behaviour of ARENA Python. Generally, at least the options -server, -username, and -password are required. See 15.3 ARENA Python options.

Example $ arena_python prod -username ARENAPYTHON -password aaa123

Developer Guide: AEF Python (FCA2831-17)

15 ARENA Python 70 of 72

15.3 ARENA Python options Use -? on the command line to get a complete list of all the options that are available for use with ARENA Python:

c:>\ arena_python.exe -?

Most options are standard PRIME configuration options. For a description of all options and their normal default values, see System Administration: PRIME (FCA1086).

15.3.1 ARENA Python specific options The following options are not described in System Administration: PRIME (FCA1086).

• -do_not_connect: Do not connect to ADS. • -exit_on_disconnect: If given on the command line, ARENA Python will exit if connection

to server is lost. • -filename: Run this Python script file. • -principal: The Kerberos service principal. Refer to System Administration: Kerberos

Authentication (Windows) (FCA4510) for more information. • -pwdfile: File to read password from, alternative to specifying password on command line. • -session: Name of the session (PRIME workspace) containing parameter settings, for

valuations to be used by ARENA Python. • -sso: Single sign on when using Kerberos to authenticate. Local session ticket cache will

be used. Refer to System Administration: Kerberos Authentication (Windows) (FCA4510) for more information.

15.4 Interactive mode ARENA Python can be run in interactive mode by not providing the -file option. End the session with the operating system's end of file (eof) command, for example, Ctrl+Z, Ctrl+C, or Ctrl+D.

Example C:\> C:\>"C:\Program Files\FRONT\FRONT ARENA\PRIME\2011.1\arena_python.exe" >>> import acm >>> print (acm.Time( ).TimeNow( )) 2010-11-24 19:11:42 >>> ^Z C:\>

15.5 Command option values Some command options relate to parameters that can be set in the PRIME User Preferences window (date, time, numeric formats, and so on). The settings currently saved by the user running ARENA Python, will serve as the default values for these command options.

On Windows, command option values are also read from the file arena.ini (by default). Default values are taken from this file. You can use the command option -inifile to supply a different file.

The parameter context for the file specified in the program is Arena.Arena_ext. This can be overridden with the command line parameter -context <new context>.

Contact the Front Arena documentation team at: [email protected]