> ## Documentation Index
> Fetch the complete documentation index at: https://docs.activeviam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuring sources using Spring Beans and properties

This page describes the source topic descriptions defined in the application, and provides an example of adding customization.

## Source Topic Descriptions

The Data Load Controller is used to define the source topics. The following descriptions are defined:

<table><thead><tr><th>Name</th><th>File pattern</th><th>Columns (if explicitly defined)\*</th><th>Target description name</th><th>Custom fields</th><th>Configuration class</th></tr></thead><tbody><tr><td>Countries</td><td><code>glob:\*\*Countries\*.csv</code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>CounterpartyParentChild</td><td><code>glob:\*\*CounterpartyParentChild\*.csv</code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>Counterparties</td><td><code>glob:\*\*Counterparties\*.csv </code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>TradeAttributes</td><td><code>glob:\*\*TradeAttributes\*.csv</code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>RiskFactorsCatalogue</td><td><code>glob:\*\*RiskFactorsCatalog\*.csv</code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>Scenarios</td><td><code>glob:\*\*Scenarios\*.csv</code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>LegalEntityParentChild</td><td><code>glob:\*\*LegalEntityParentChild\*.csv</code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>BookParentChild</td><td><code>glob:\*\*BookParentChild\*.csv</code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>MarketShifts</td><td><code>glob:\*\*MarketShifts\*.csv</code></td><td /><td /><td /><td>CommonStoreCsvLoadConfig</td></tr><tr><td>PnL</td><td><code>glob:\*\*PLPC\*.csv</code></td><td>AsOfDate<br />TradeId<br />Daily<br />Monthly<br />Yearly<br />Lifetime<br />Type<br />PLDriver<br />IsFullReval<br />Ccy<br />MarketDataSet<br />Bucket</td><td>PnL</td><td>TradeKey</td><td>PnLCsvLoadConfig</td></tr><tr><td>DynamicTenors</td><td><code>glob:\*\*DynamicTenors\*.csv</code></td><td>TenorLabel<br />NumberOfDays<br />SensitivityName<br />TenorSet</td><td>DynamicTenors</td><td>TenorIndices</td><td>SensiCsvLoadConfig</td></tr><tr><td>DynamicMaturities</td><td><code>glob:\*\*DynamicMaturities\*.csv</code></td><td>MaturityLabel<br />NumberOfDays<br />SensitivityName<br />MaturitySet</td><td>DynamicMaturities</td><td>MaturityIndices</td><td>SensiCsvLoadConfig</td></tr><tr><td>DynamicMoneyness</td><td><code>glob:\*\*DynamicMoneyness\*.csv </code></td><td>MoneynessLabel<br />Shift<br />SensitivityName<br />MoneynessSet</td><td>DynamicMoneyness</td><td>MoneynessIndices</td><td>SensiCsvLoadConfig</td></tr><tr><td>CorrelationMarketData</td><td><code>glob:\*\*Correlation\_Market\_Data\*.csv</code></td><td /><td /><td /><td>SensiCsvLoadConfig</td></tr><tr><td>DividendMarketData</td><td><code>glob:\*\*Dividends\*.csv</code></td><td /><td /><td /><td>SensiCsvLoadConfig</td></tr><tr><td>SplitRatioMarketData</td><td><code>glob:\*\*SplitRatio\*.csv</code></td><td /><td /><td /><td>SensiCsvLoadConfig</td></tr><tr><td>SensiLadders</td><td><code>glob:\*\*LadderDefinition\*.csv</code></td><td /><td /><td /><td>SensiCsvLoadConfig</td></tr><tr><td>Delta</td><td><code>glob:\*\*DeltaSensitivities\*.csv</code></td><td>AsOfDate<br />TradeId<br />SensitivityName<br />RiskClass<br />MarketDataSet<br />RiskFactorId<br />TenorLabel<br />TenorDate<br />MaturityLabel<br />MaturityDate<br />Moneyness<br />Value<br />Ladder<br />Ccy</td><td>Delta</td><td>TradeKey + 6 anonymous custom field descriptions to handle vectorized inputs if needed</td><td>SensiCsvLoadConfig</td></tr><tr><td>Correlation</td><td><code>glob:\*\*CorrelationSensitivities\*.csv</code></td><td>AsOfDate<br />TradeId<br />SensitivityName<br />RiskClass<br />MarketDataSet<br />RiskFactorId<br />RiskFactorId2<br />TenorLabel<br />TenorDate<br />MaturityLabel<br />MaturityDate<br />Moneyness<br />Value<br />Ladder<br />Ccy</td><td>Correlation</td><td>TradeKey + 6 anonymous custom field descriptions to handle vectorized inputs if needed</td><td>SensiCsvLoadConfig</td></tr><tr><td>CrossGamma</td><td><code>glob:\*\*CrossGammaSensitivities\*.csv</code></td><td>AsOfDate<br />TradeId<br />SensitivityName<br />RiskClass<br />MarketDataSet<br />RiskFactor<br />RiskFactor2<br />TenorLabel<br />TenorDate<br />MaturityLabel<br />MaturityDate<br />Moneyness<br />Value<br />Ladder<br />Ccy</td><td>CrossGamma</td><td>TradeKey + 6 anonymous custom field descriptions to handle vectorized inputs if needed</td><td>SensiCsvLoadConfig</td></tr><tr><td>Volga</td><td><code>glob:\*\*VolgaSensitivities\*.csv</code></td><td>AsOfDate<br />TradeId<br />SensitivityName<br />RiskClass<br />MarketDataSet<br />RiskFactorId<br />TenorLabel<br />TenorDate<br />MaturityLabel<br />MaturityDate<br />Moneyness<br />Value<br />Ladder<br />Ccy</td><td>Volga</td><td>TradeKey + 6 anonymous custom field descriptions to handle vectorized inputs if needed</td><td>SensiCsvLoadConfig</td></tr><tr><td>Vanna</td><td><code>glob:\*\*VannaSensitivities\*.csv</code></td><td>AsOfDate<br />TradeId<br />SensitivityName<br />RiskClass<br />MarketDataSet<br />RiskFactorId<br />RiskFactorId2<br />TenorLabel<br />TenorDate<br />MaturityLabel<br />MaturityDate<br />Moneyness<br />Value<br />Ladder<br />Ccy</td><td>Vanna</td><td>TradeKey + 6 anonymous custom field descriptions to handle vectorized inputs if needed</td><td>SensiCsvLoadConfig</td></tr><tr><td>Vega</td><td><code>glob:\*\*VegaSensitivities\*.csv</code></td><td>AsOfDate<br />TradeId<br />SensitivityName<br />RiskClass<br />MarketDataSet<br />RiskFactorId<br />TenorLabel<br />TenorDate<br />MaturityLabel<br />MaturityDate<br />Moneyness<br />Value<br />Ladder<br />Ccy</td><td>Vega</td><td>TradeKey + 6 anonymous custom field descriptions to handle vectorized inputs if needed</td><td>SensiCsvLoadConfig</td></tr><tr><td>Theta</td><td><code>glob:\*\*ThetaSensitivities\*.csv</code></td><td>AsOfDate<br />TradeId<br />SensitivityName<br />RiskClass<br />MarketDataSet<br />RiskFactorId<br />TenorLabel<br />TenorDate<br />MaturityLabel<br />MaturityDate<br />Moneyness<br />Value<br />Ladder<br />Ccy</td><td>Theta</td><td>TradeKey + 6 anonymous custom field descriptions to handle vectorized inputs if needed</td><td>SensiCsvLoadConfig</td></tr><tr><td>Gamma</td><td><code>regex:(?i).\*Gamma(?\<!CrossGamma)Sensitivities.\*.csv.\*</code></td><td>AsOfDate<br />TradeId<br />SensitivityName<br />RiskClass<br />MarketDataSet<br />RiskFactorId<br />TenorLabel<br />TenorDate<br />MaturityLabel<br />MaturityDate<br />Moneyness<br />Value<br />Ladder<br />Ccy</td><td>Gamma</td><td>TradeKey + 6 anonymous custom field descriptions to handle vectorized inputs if needed</td><td>SensiCsvLoadConfig</td></tr><tr><td>SpotMarketData</td><td><code>glob:\*\*Spot\_Market\_Data\*.csv</code></td><td /><td /><td /><td>SpotMarketDataCsvLoadConfig</td></tr><tr><td>SurfaceMarketData</td><td><code>glob:\*\*Surface\_Market\_Data\*.csv</code></td><td /><td /><td /><td>SurfaceMarketDataCsvLoadConfig</td></tr><tr><td>CurveMarketData</td><td><code>glob:\*\*Curve\_Market\_Data\*.csv</code></td><td /><td /><td /><td>CurveMarketDataCsvLoadConfig</td></tr><tr><td>CubeMarketData</td><td><code>glob:\*\*Cube\_Market\_Data\*.csv</code></td><td /><td /><td /><td>CubeMarketDataCsvLoadConfig</td></tr><tr><td>FxRateMarketData</td><td><code>glob:\*\*FX\_Rate\_Market\_Data\*.csv</code></td><td /><td /><td /><td>FxRateMarketDataCsvLoadConfig</td></tr><tr><td>TradePnLs</td><td><code>glob:\*\*TradePnLs\*.csv</code></td><td>AsOfDate<br />TradeId<br />ScenarioSet<br />CalculationId<br />MarketDataSet<br />RiskFactor<br />RiskClass<br />SensitivityName<br />Ccy<br />MTM<br />PnLVector</td><td>TradePnLs</td><td>TradeKey</td><td>VaRCsvLoadConfig</td></tr><tr><td>SensiBaseStore</td><td><code>glob:\*\*Sensitivity Cube\*.csv\*</code></td><td /><td /><td /><td>SensiSummaryCsvLoadConfig</td></tr><tr><td>BaseStore</td><td><code>glob:\*\*VaR-ES Cube\*.csv\*</code></td><td /><td /><td /><td>VaRSummaryCsvLoadConfig</td></tr><tr><td>PnLBaseStore</td><td><code>glob:\*\*PLCube\*.csv\*</code></td><td /><td /><td /><td>PnLSummaryCsvLoadConfig</td></tr></tbody></table>

\*If no fields are present in the configuration, the datastore’s target store fields are taken into account.

## Example

For the purposes of this example, we will add a column to the file
`DynamicTenors.csv` and load the data from this column into the
`DynamicTenors` store. In this case, our new columnâs header is “TestField”,
and all data in this column is “Testdata”. Here’s how the input file looks like:

<table><thead><tr><th><strong>TenorSet</strong></th><th><strong>TenorLabel</strong></th><th><strong>SensitivityName</strong></th><th><strong>NumberOfDays</strong></th><th><strong>TestField</strong></th></tr></thead><tbody><tr><td>DEFAULT</td><td>N/A</td><td>N/A</td><td>0.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>0.25Y</td><td>N/A</td><td>90.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>0.5Y</td><td>N/A</td><td>180.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>1Y</td><td>N/A</td><td>360.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>2Y</td><td>N/A</td><td>720.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>3Y</td><td>N/A</td><td>1080.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>5Y</td><td>N/A</td><td>1800.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>10Y</td><td>N/A</td><td>3600.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>15Y</td><td>N/A</td><td>5400.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>20Y</td><td>N/A</td><td>7200.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>N/A</td><td>N/A</td><td>0.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>0.5Y</td><td>N/A</td><td>180.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>1Y</td><td>N/A</td><td>360.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>2Y</td><td>N/A</td><td>720.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>30Y</td><td>N/A</td><td>10800.0</td><td>Testdata</td></tr><tr><td>DECADE</td><td>10Y</td><td>N/A</td><td>3600.0</td><td>Testdata</td></tr><tr><td>DECADE</td><td>20Y</td><td>N/A</td><td>7200.0</td><td>Testdata</td></tr></tbody></table>

We will also attach a column calculator that multiplies the **NumberOfDays** by 2 into a new column, as well as a tuple publisher that will add a value into the **SensitivityName** field.

### Step 1 - Defining customizations to datastore

Before we can load these new columns into our cube, we need to make sure
that our datastore has fields that can accept them.

To add new fields to an existing store, we need to create a `DatastoreConfiguratorConsumer` bean, which appends the required fields to the `DynamicTenors` store:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
public static String DOUBLE_NUMBER_OF_DAYS = "DoubleNumberOfDays";
public static String TEST_FIELD = "TestData";

@Bean
public DatastoreConfiguratorConsumer addFieldToStore() {
    return configurator -> {
        configurator.appendFields(DYNAMIC_TENOR_STORE_NAME,
                List.of(
                        new CustomField(DOUBLE_NUMBER_OF_DAYS, ILiteralType.DOUBLE),
                        new CustomField(TEST_FIELD, ILiteralType.STRING)
                )
        );
    };
}
```

See [Customizing the Datastore with the Datastore
Helper](https://docs.activeviam.com/products/tools/dash/3.4.0-AS6.1/online-help/)
for more information.

### Step 2 - Implementing the column calculator and the tuple publisher

Now that we have modified our store, we need to make sure the ETL is
correctly set up, so that the field will be properly populated.

In most cases, the topic configuration uses the store fields as the expected file columns, which would require no further configuration.

For the `DynamicTenors` store used in this example, file columns are defined explicitly, so any new columns are not auto-configured.
The DynamicTenors store also has a previously defined column calculator creating indices for each of the tenors.

To define the column calculator, we create the following bean, containing the required logic:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
@Bean
public CustomFieldDescription dynamicTenorsColumnCalculator() {
    return CustomFieldDescription.of("MyCustomDynamicTenorColCalculator",
            scope -> new GenericLambdaCalculator<>(DOUBLE_NUMBER_OF_DAYS,
                    context -> {
                        Double originalValue = (Double) context.getValue(StoreFieldConstants.TENOR_NUMBER_OF_DAYS);
                        if (originalValue == null) {
                            return null;
                        }
                        return 2.0 * originalValue;
                    }
            )
    );
}
```

For the tuple publisher, a second bean is created, defining the publishing logic:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
@Bean
public TargetDescription dynamicTenorsTarget(IDatastore datastore) {
    return TargetDescription.of("MyCustomDynamicTenors", DYNAMIC_TENOR_STORE_NAME,
            scope ->
                    new ITuplePublisher<>() {

                        @Override public void publish(IStoreMessage<?, ?> message, List<Object[]> tuples) {
                            // We use the expected index for the SensitivityName field here, while in a complete implementation the index would be an instance variable.
                            tuples.forEach(tuple -> tuple[2] = "TestValue");
                            datastore.getTransactionManager().addAll(StoreConstants.DYNAMIC_TENOR_STORE_NAME, tuples);
                        }

                        @Override
                        public Collection<String> getTargetStores() {
                            return Collections.singleton(DYNAMIC_TENOR_STORE_NAME);
                        }

                    });
}
```

### Step 3 - Update the topic description

We now need to modify the DynamicTenors topic to include the column calculator and tuple publisher. This can either be done with properties, or in Java.

#### Properties

We add the following properties to our existing properties,to define:

* the list of columns read from the input file,
* the calculated column,
* the tuple publisher.

```yaml theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
  csv:
    topics:
      DynamicTenors:
        file-pattern: "glob:**DynamicTenors**.csv"
        parser:
          columns:
            - TenorLabel
            - NumberOfDays
            - SensitivityName
            - TenorSet
            - TestData
        channels:
            - target: MyCustomDynamicTenors
              custom-fields:
                - MyCustomDynamicTenorColCalculator
                - TenorIndices
```

#### Java

We create a new `CsvTopicConfiguration` Spring bean that will override the existing topic bean provided in Atoti Market Risk. We inject into the bean method:

* Our previously defined target and custom field. These will be used to create the channel,
* The existing `tenorIndicesCalculator`, defined in Atoti Market Risk. This will be used to create the channel,
* The existing topic bean. This enables the default parser and file pattern to be reused.

The bean is annotated with `@Order(1)`. This ensures it takes precedence over existing topics in Atoti Market Risk.

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
@Bean
@Order(1)
public CsvTopicDescription customDynamicTenorsTopic(
        TargetDescription dynamicTenorsTarget,
        CustomFieldDescription dynamicTenorsColumnCalculator,
        CustomFieldDescription tenorIndicesCalculator,
        CsvTopicDescription dynamicTenorsCsvLoadTopic) {
    ChannelDescription
            .builder(dynamicTenorsTarget)
            .customFields(Set.of(dynamicTenorsColumnCalculator, tenorIndicesCalculator))
            .build();
    return CsvTopicDescription
            .builder(StoreConstants.DYNAMIC_TENOR_STORE_NAME, dynamicTenorsCsvLoadTopic.filePattern())
            .parser(dynamicTenorsCsvLoadTopic.parser())
            .channel(dynamicTenorsChannel(StoreConstants.DYNAMIC_TENOR_STORE_NAME)).build();
}
```

### Step 4 - Configuration classes

You can include the Beans detailed above in a single class within Atoti Market Risk.

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
@Configuration
public class CustomizationsConfig {

    public static String DOUBLE_NUMBER_OF_DAYS = "DoubleNumberOfDays";
    public static String TEST_FIELD = "TestData";

    @Bean
    public DatastoreConfiguratorConsumer addFieldToStore() {
        return configurator -> {
            configurator.appendFields(DYNAMIC_TENOR_STORE_NAME,
                    List.of(
                            new CustomField(DOUBLE_NUMBER_OF_DAYS, ILiteralType.DOUBLE),
                            new CustomField(TEST_FIELD, ILiteralType.STRING)
                    )
            );
        };
    }

    @Bean
    public CustomFieldDescription dynamicTenorsColumnCalculator() {
        return CustomFieldDescription.of("MyCustomDynamicTenorColCalculator",
                scope -> new GenericLambdaCalculator<>(DOUBLE_NUMBER_OF_DAYS,
                        context -> {
                            Double originalValue = (Double) context.getValue(StoreFieldConstants.TENOR_NUMBER_OF_DAYS);
                            if (originalValue == null) {
                                return null;
                            }
                            return 2.0 * originalValue;
                        }
                )
        );
    }

    @Bean
    public TargetDescription dynamicTenorsTarget(IDatastore datastore) {
        return TargetDescription.of("MyCustomDynamicTenors", DYNAMIC_TENOR_STORE_NAME,
                scope ->
                        new ITuplePublisher<>() {

                            @Override public void publish(IStoreMessage<?, ?> message, List<Object[]> tuples) {
                                // We use the expected index for the SensitivityName field here, while in a complete implementation the index would be an instance variable.
                                tuples.forEach(tuple -> tuple[2] = "TestValue");
                                datastore.getTransactionManager().addAll(StoreConstants.DYNAMIC_TENOR_STORE_NAME, tuples);
                            }

                            @Override
                            public Collection<String> getTargetStores() {
                                return Collections.singleton(DYNAMIC_TENOR_STORE_NAME);
                            }

                        });
    }

    // This bean is not required if you are using properties to define the topic.
    @Bean
    public CsvTopicDescription customDynamicTenorsTopic(
            TargetDescription dynamicTenorsTarget,
            CustomFieldDescription dynamicTenorsColumnCalculator,
            CustomFieldDescription tenorIndicesCalculator,
            CsvTopicDescription dynamicTenorsCsvLoadTopic) {
        ChannelDescription
                .builder(dynamicTenorsTarget)
                .customFields(Set.of(dynamicTenorsColumnCalculator, tenorIndicesCalculator))
                .build();
        return CsvTopicDescription
                .builder(StoreConstants.DYNAMIC_TENOR_STORE_NAME, dynamicTenorsCsvLoadTopic.filePattern())
                .parser(dynamicTenorsCsvLoadTopic.parser())
                .channel(dynamicTenorsChannel(StoreConstants.DYNAMIC_TENOR_STORE_NAME)).build();
    }
}
```

Then, include this configuration class in the `@Import` annotation of the `MarketRiskConfig` class:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
@Import(value = {
        ...
		CustomizationsConfig.class

})
public class MarketRiskConfig {
    ...
}
```

Importing this customization class and modifying the input file will result in the following data being loaded into the store:

<table><thead><tr><th><strong>TenorSet</strong></th><th><strong>TenorLabel</strong></th><th><strong>SensitivityName</strong></th><th><strong>NumberOfDays</strong></th><th><strong>TenorIndices</strong></th><th><strong>DoubleNumberOfDays</strong></th><th><strong>TestData</strong></th></tr></thead><tbody><tr><td>DEFAULT</td><td>N/A</td><td>TestValue</td><td>0.0</td><td>0</td><td>0.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>0.25Y</td><td>TestValue</td><td>90.0</td><td>1</td><td>180.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>0.5Y</td><td>TestValue</td><td>180.0</td><td>2</td><td>360.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>1Y</td><td>TestValue</td><td>360.0</td><td>3</td><td>720.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>2Y</td><td>TestValue</td><td>720.0</td><td>4</td><td>1440.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>3Y</td><td>TestValue</td><td>1080.0</td><td>5</td><td>2160.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>5Y</td><td>TestValue</td><td>1800.0</td><td>6</td><td>3600.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>10Y</td><td>TestValue</td><td>3600.0</td><td>7</td><td>7200.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>15Y</td><td>TestValue</td><td>5400.0</td><td>8</td><td>10800.0</td><td>Testdata</td></tr><tr><td>DEFAULT</td><td>20Y</td><td>TestValue</td><td>7200.0</td><td>9</td><td>14400.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>N/A</td><td>TestValue</td><td>0.0</td><td>0</td><td>0.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>0.5Y</td><td>TestValue</td><td>180.0</td><td>1</td><td>360.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>1Y</td><td>TestValue</td><td>360.0</td><td>2</td><td>720.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>2Y</td><td>TestValue</td><td>720.0</td><td>3</td><td>1440.0</td><td>Testdata</td></tr><tr><td>REDUCED</td><td>30Y</td><td>TestValue</td><td>10800.0</td><td>4</td><td>21600.0</td><td>Testdata</td></tr><tr><td>DECADE</td><td>10Y</td><td>TestValue</td><td>3600.0</td><td>0</td><td>7200.0</td><td>Testdata</td></tr><tr><td>DECADE</td><td>20Y</td><td>TestValue</td><td>7200.0</td><td>1</td><td>14400.0</td><td>Testdata</td></tr></tbody></table>

The configuration for this behavior is:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
new ATableFormatTuplePublisher<>(datastore, Map.of(INSTRUMENT_ID, RISK_FACTOR)) {
  @Override
  public void publish(IStoreMessage<? extends I, ?> message, List<Object[]> tuples) {
      Queue<Object[]> storeTuples = new ConcurrentLinkedQueue<>();
      tuples.forEach(
              tuple -> {
                  if (checkNull(tmmProps, message, tuple, TENOR_LABELS, TENOR_DATES, MATURITY_LABELS, MATURITY_DATES, MONEYNESS_LABELS,
                          NOMINAL)) {
                      Queue<Object[]> extractedScalarTuples = new ConcurrentLinkedQueue<>();
                      devectorizer.extractScalarTuples(message, tuple, extractedScalarTuples);
                      extractedScalarTuples.forEach(
                              scalarTuple -> storeTuples.add(createTupleForTable(message, SPOT_MARKET_DATA_STORE, scalarTuple))
                      );
                  }
              }
      );
      datastore.getTransactionManager().addAll(SPOT_MARKET_DATA_STORE, storeTuples);
  }

  @Override
  public Collection<String> getTargetStores() {
      return Set.of(SPOT_MARKET_DATA_STORE);
  }
}
```

Within the `createTupleForTable` method, the publisher iterates over the table fields, attempting to retrieve a value in the input tuple by the mapped field (or directly if no mapping is available).

If no value is available in the input tuple, or no matching field is found, the value will be empty. If a tuple field’s value isn’t requested by the publisher, it is ignored.

### Suggested Further Reading

* [Adding cube hierarchies](./add-a-new-cube-hierarchy)
* [Configuring measures using Spring Beans](./configure-measures)
* [Configuring schema selections using Spring Beans](./configure-schema-selections)
* [Adding a new KPI](./add-a-new-kpi)
* [Adding data loading or unloading topics](./add-a-new-data-loading-unloading-topic)
