@ -136,15 +136,14 @@ export class AiService {
/ * *
* Matches the rows of a file to the asset profiles with the agent model , the
* tool search - asset - profiles and the holdings of the tool get - portfolio , and
* to the accounts of the user . Each account name of the rows , also a missing
* one , gives an account hint with the broker of the file , as a file often
* shows the broker only , for example as a logo . A row which does not give a
* valid activity , for example because its asset has no match , is a row
* without a match . An account id which is not the id of an account of the
* user is ignored . The fee , the quantity and the unit price are taken as
* absolute values , as a broker can give a sell or a fee as a negative
* number .
* tool search - asset - profiles and the holdings of the user , and to the
* accounts of the user . Each account name of the rows , also a missing one ,
* gives an account hint with the broker of the file , as a file often shows
* the broker only , for example as a logo . A row which does not give a valid
* activity , for example because its asset has no match , is a row without a
* match . An account id which is not the id of an account of the user is
* ignored . The fee , the quantity and the unit price are taken as absolute
* values , as a broker can give a sell or a fee as a negative number .
* /
public async getActivitiesFromRows ( {
abortSignal ,
@ -414,7 +413,7 @@ export class AiService {
* Gives each fee , interest and liability a custom asset profile with its
* description as name , as the import after the dry run requires a valid
* symbol for the data source MANUAL . The data source of every other activity
* is given as search - asset - profiles and get - portfolio give it , which POST
* is given as search - asset - profiles and the holdings give it , which POST
* api / v1 / import transforms back .
* /
private getActivitiesWithCustomAssetProfiles (
@ -472,67 +471,20 @@ export class AiService {
}
/ * *
* Gives the currency , the data source , the ISIN , the name and the symbol of
* each holding of the table of the holdings which has a data source and a
* symbol . A cash position has no data source , hence it is left out . The ISIN
* is null if the asset profile has none , which is the case for most stocks
* of Yahoo Finance . The columns are found by the header line . A line is
* split at each pipe which is not escaped , as the renderer escapes a pipe in
* a value , and the escaped pipes are restored .
* /
private getHoldingsFromTable ( holdings = '' ) {
const [ headerLine , , . . . lines ] = holdings . split ( '\n' ) . filter ( ( line ) = > {
return line . startsWith ( '|' ) ;
} ) ;
if ( ! headerLine ) {
return [ ] ;
}
const getCells = ( line : string ) = > {
return line
. split ( /(?<!\\)\|/ )
. slice ( 1 , - 1 )
. map ( ( cell ) = > {
return cell . trim ( ) . replaceAll ( '\\|' , '|' ) ;
} ) ;
} ;
const columnNames = getCells ( headerLine ) ;
return lines
. map ( ( line ) = > {
const cells = getCells ( line ) ;
return {
currency : cells [ columnNames . indexOf ( 'Currency' ) ] ,
dataSource : cells [ columnNames . indexOf ( 'Data Source' ) ] ,
isin : cells [ columnNames . indexOf ( 'ISIN' ) ] || null ,
name : cells [ columnNames . indexOf ( 'Name' ) ] ,
symbol : cells [ columnNames . indexOf ( 'Symbol' ) ]
} ;
} )
. filter ( ( { dataSource , symbol } ) = > {
return dataSource && symbol ;
} ) ;
}
/ * *
* Gives the holdings of the user with the tool get - portfolio of the server
* of the Model Context Protocol , without the holdings whose data source the
* import refuses
* Gives the holdings of the user from the server of the Model Context
* Protocol , also the closed positions and the holdings of the excluded
* accounts , without a portfolio calculation and without the holdings whose
* data source the import refuses
* /
private async getHoldingsToMatch ( { userId } : { userId : string } ) {
const [ { content } , dataSources ] = await Promise . all ( [
this . mcpService . getPortfolio ( { userId , withValues : false } ) ,
const [ assetProfiles , dataSources ] = await Promise . all ( [
this . mcpService . getAssetProfilesOfUser ( { userId } ) ,
this . dataProviderService . getDataSources ( )
] ) ;
return this . getHoldingsFromTable ( content [ 0 ] . text ) . filter (
( { dataSource } ) = > {
return assetProfiles . filter ( ( { dataSource } ) = > {
return this . isValidDataSourceForImport ( { dataSource , dataSources } ) ;
}
) ;
} ) ;
}
/ * *
@ -585,27 +537,27 @@ export class AiService {
}
/ * *
* Matches the assets and the account hints with the agent model . The code
* and not the model calls the tools search - asset - profiles , get - accounts and
* get - portfolio of the server of the Model Context Protocol and passes their
* results to the model , as some providers ignore a required tool call , for
* example if the output has a schema . The accounts are given without
* values , the holdings with their currency , data source , ISIN , name and
* symbol only . The holdings let the model select an asset profile which the
* user already has , also if no search gives it , for example as no data
* provider finds its ISIN . A holding whose data source the import refuses
* is not given to the model , so that the model selects an asset profile of
* the search instead and the whole import does not fail .
* If get - accounts fails , the model gets no accounts , so that only the
* account hints have no match . If get - portfolio or the data sources fail ,
* the model gets no holdings . The reasoning is disabled , as it can add
* thousands of output t okens . An asset match is left out if neither the
* search of its own asset nor the holdings gave its asset profile , or if its
* holding has another ISIN than its own asset , as the import refuses a
* symbol which the data source cannot resolve and as the model can mix up
* the ids of the assets . The ISINs are compared without spaces and in upper
* case . A holding without an ISIN is matched by the model by its name only ,
* hence the code cannot check it against the asset .
* Matches the assets and the account hints with the agent model . The code and
* not the model calls the tools search - asset - profiles and get - accounts of the
* server of the Model Context Protocol , gets the holdings from it as well and
* passes their results to the model , as some providers ignore a required tool
* call , for example if the output has a schema . The accounts are given
* without values , the holdings with their currency , data source , ISIN , name
* and symbol only . The holdings let the model select an asset profile which
* the user already has , also if no search gives it , for example as no data
* provider finds its ISIN . A holding whose data source the import refuses is
* not given to the model , so that the model selects an asset profile of the
* search instead and the whole import does not fail . If get - accounts fails ,
* the model gets no accounts , so that only the account hints have no match .
* If the holdings or the data sources cannot be got , the model gets no
* holdings . The reasoning is disabled , as it can add thousands of output
* tokens . An asset match is left out if neither the search of its own asset
* nor the holdings gave its asset profile , or if its holding has another ISIN
* than its own asset , as the import refuses a symbol which the data source
* cannot resolve and as the model can mix up the ids of the assets . The ISINs
* are compared without spaces and in upper case . A holding without an ISIN is
* matched by the model by its name only , hence the code cannot check it
* against the asset .
* /
private async getRowMatches ( {
abortSignal ,