Convert from Python to C++ Practice 02

In this post, I will convert this calculate_sector_neutral_tilted_weights function into C++ which is more verbose but robust.

first the header file

#pragma once
#include <string>
#include <vector>
#include <map>
#include <DataFrame/DataFrame.h>  // Using a DataFrame library

struct SectorData {
    std::string sector;
    std::vector<double> weights;
    std::vector<double> act_pass;
    // Other necessary fields
};

class SectorNeutralTilting {
public:
    static DataFrame calculateSectorNeutralTiltedWeights(const DataFrame& df);

private:
    static double sigmoid(double x);
    static std::vector<double> calculateZScores(const std::vector<double>& values);
    static std::vector<double> clipValues(const std::vector<double>& values, double min, double max);
};

Next the source cpp file:

#include "sector_neutral.h"
#include <cmath>
#include <numeric>
#include <algorithm>

double SectorNeutralTilting::sigmoid(double x) {
    return 1.0 / (1.0 + std::exp(-x));
}

std::vector<double> SectorNeutralTilting::calculateZScores(const std::vector<double>& values) {
    // Calculate mean
    double sum = std::accumulate(values.begin(), values.end(), 0.0);
    double mean = sum / values.size();

    // Calculate standard deviation
    double sq_sum = std::inner_product(values.begin(), values.end(), values.begin(), 0.0);
    double stdev = std::sqrt(sq_sum / values.size() - mean * mean);

    // Calculate z-scores
    std::vector<double> z_scores;
    z_scores.reserve(values.size());
    for (double value : values) {
        z_scores.push_back((value - mean) / stdev);
    }
    return z_scores;
}

std::vector<double> SectorNeutralTilting::clipValues(const std::vector<double>& values, 
                                                    double min, double max) {
    std::vector<double> clipped;
    clipped.reserve(values.size());
    for (double value : values) {
        clipped.push_back(std::clamp(value, min, max));
    }
    return clipped;
}

DataFrame SectorNeutralTilting::calculateSectorNeutralTiltedWeights(const DataFrame& df) {

    // Group data by sector

    std::map<std::string, SectorData> sector_groups;

    // Assuming df has methods to access columns and iterate rows

    for (size_t i = 0; i < df.rows(); ++i) {

        std::string sector = df.get<std::string>("Sector", i);

        double weight = df.get<double>("Weight", i);

        double act_pass = df.get<double>("act_pass", i);

        sector_groups[sector].weights.push_back(weight);

        sector_groups[sector].act_pass.push_back(act_pass);

    }

    DataFrame result;

    std::map<std::string, double> sector_weights;

    double total_weight = 0.0;

    // Process each sector

    for (auto& [sector, data] : sector_groups) {

        // Calculate Z-scores within sector

        auto z_scores = calculateZScores(data.act_pass);

        // Cap Z-scores

        auto capped_z_scores = clipValues(z_scores, -3.0, 3.0);

        // Apply sigmoid

        const double sigmoid_scale = 10.0;

        std::vector<double> tilting_factors;

        tilting_factors.reserve(capped_z_scores.size());

        for (double z : capped_z_scores) {

            tilting_factors.push_back(sigmoid(sigmoid_scale * z));

        }

        // Calculate sector market weights

        double sector_total = std::accumulate(data.weights.begin(),

                                            data.weights.end(), 0.0);

        std::vector<double> sector_mkt_weights;

        std::vector<double> sector_tilted_weights;

        for (size_t i = 0; i < data.weights.size(); ++i) {

            double mkt_weight = data.weights[i] / sector_total;

            sector_mkt_weights.push_back(mkt_weight);

            sector_tilted_weights.push_back(mkt_weight * (1.0 + tilting_factors[i]));

        }

        // Normalize sector tilted weights

        double tilted_sum = std::accumulate(sector_tilted_weights.begin(),

                                          sector_tilted_weights.end(), 0.0);

        for (double& weight : sector_tilted_weights) {

            weight /= tilted_sum;

        }

        // Store sector weight for final adjustment

        sector_weights[sector] = sector_total;

        total_weight += sector_total;

        // Add to result DataFrame

        // ... (implementation depends on DataFrame library used)

    }

    // Normalize sector weights

    for (auto& [sector, weight] : sector_weights) {

        weight /= total_weight;

    }

    // Apply sector neutrality and final normalization

    // ... (implementation depends on DataFrame library used)

    return result;

}

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.