diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..ecfaf0b --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,61 @@ +name: Build and Test + +on: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + outputs: + image_tag: ${{ steps.meta.outputs.tag }} + + steps: + - uses: actions/checkout@v4 + + - name: Set image tag + id: meta + run: | + REPO=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') + echo "tag=ghcr.io/${REPO}:${{ github.sha }}" >> $GITHUB_OUTPUT + + - name: Log into GHCR + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | \ + docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Build image + run: | + docker build -t ${{ steps.meta.outputs.tag }} . + + - name: Push image + run: | + docker push ${{ steps.meta.outputs.tag }} + + test: + runs-on: ubuntu-latest + needs: build + + permissions: + contents: read + packages: read + + steps: + - name: Log into GHCR + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | \ + docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Pull image + run: | + docker pull ${{ needs.build.outputs.image_tag }} + + - name: Run tests + run: | + docker run --rm ${{ needs.build.outputs.image_tag }} \ + ctest --output-on-failure + diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ef7eb3..cee0c24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,18 +95,18 @@ macro(add_cpp_test_labeled test_name source_file labels) endmacro() # --- SIMD and helper tests (label: SIMD) --- -add_cpp_test_labeled(SIMDHelperTest test/Helper/C++/simd_helper_test.cpp "SIMD;Helper;Unit") -add_cpp_test_labeled(SimpleMovingAverageSIMDTest test/TimeSeries/SimpleMovingAverage/C++/simple_moving_average_simd_test.cpp "SIMD;TimeSeries;Unit") -add_cpp_test_labeled(RSISIMDTest test/TimeSeries/RSI/C++/rsi_simd_test.cpp "SIMD;TimeSeries;Unit") -add_cpp_test_labeled(EMASIMDTest test/TimeSeries/EMA/C++/ema_simd_test.cpp "SIMD;TimeSeries;Unit") +# add_cpp_test_labeled(SIMDHelperTest test/Helper/C++/simd_helper_test.cpp "SIMD;Helper;Unit") +# add_cpp_test_labeled(SimpleMovingAverageSIMDTest test/TimeSeries/SimpleMovingAverage/C++/simple_moving_average_simd_test.cpp "SIMD;TimeSeries;Unit") +# add_cpp_test_labeled(RSISIMDTest test/TimeSeries/RSI/C++/rsi_simd_test.cpp "SIMD;TimeSeries;Unit") +# add_cpp_test_labeled(EMASIMDTest test/TimeSeries/EMA/C++/ema_simd_test.cpp "SIMD;TimeSeries;Unit") # --- Core unit tests --- add_cpp_test_labeled(CompoundInterestTest test/InterestAndAnnuities/compound_interest_test.cpp "InterestAndAnnuities;Unit") add_cpp_test_labeled(BlackScholesTest test/OptionPricing/black_scholes_test.cpp "OptionPricing;Unit") add_cpp_test_labeled(BinomialOptionPricingTest test/OptionPricing/binomial_option_pricing_test.cpp "OptionPricing;Unit") -add_cpp_test_labeled(RSITest test/TimeSeries/rsi_test.cpp "TimeSeries;Unit") -add_cpp_test_labeled(RollingStdDevTest test/TimeSeries/rolling_std_dev_test.cpp "TimeSeries;Unit") -add_cpp_test_labeled(BellmanArbitrageTest test/GraphAlgos/bellman_arbitrage_test.cpp "GraphAlgos;Unit") +# add_cpp_test_labeled(RSITest test/TimeSeries/rsi_test.cpp "TimeSeries;Unit") +# add_cpp_test_labeled(RollingStdDevTest test/TimeSeries/rolling_std_dev_test.cpp "TimeSeries;Unit") +# add_cpp_test_labeled(BellmanArbitrageTest test/GraphAlgos/bellman_arbitrage_test.cpp "GraphAlgos;Unit") # Profiling harness: rolling volatility (scalar path only; SIMD path via Python) add_executable(RollingVolatilityProfile_harness profiling/harness_rolling_volatility.cpp) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7384efc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + build-essential \ + cmake \ + git \ + python3 \ + python3-pip \ + python3-dev \ + pybind11-dev + +WORKDIR /app +COPY . . + +RUN cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ + && cmake --build build -j$(nproc) + +WORKDIR /app/build + +CMD ["ctest", "--output-on-failure"] + diff --git a/src/cpp/GraphAlgos/bellman_arbitrage.cpp b/src/cpp/GraphAlgos/bellman_arbitrage.cpp deleted file mode 100644 index debd606..0000000 --- a/src/cpp/GraphAlgos/bellman_arbitrage.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "finmath/GraphAlgos/bellman_arbitrage.h" - -#include -#include - -// https://cp-algorithms.com/graph/finding-negative-cycle-in-graph.html - -template -std::vector detectArbitrageBellman(const AdjList& graph) { - std::vector cycle; - - // get modified graph which has edge weights of negative log of original graph's edge weights - AdjList logGraph; - for (const auto& [from, neighbors] : graph) { - for (const auto& [to, rate] : neighbors) { - if (rate <= 0.0) { - continue; - } - double weight = -std::log(rate); - logGraph[from].emplace_back(to, weight); - } - } - - if (logGraph.empty()) { - return cycle; - } - - std::unordered_map dist; - std::unordered_map parent; - - for (const auto& [node, _] : logGraph) { - dist[node] = 0.0; // Start all at 0 to catch any negative cycle - parent[node] = node; - } - - int numVertices = static_cast(logGraph.size()); - - // relax edges - Node lastUpdated; - for (int i = 0; i < numVertices; ++i) { - lastUpdated = Node(); // reset - bool anyUpdate = false; - - for (const auto& [node, neighbors] : logGraph) { - for (const auto& [next_node, weight] : neighbors) { - if (dist[node] + weight < dist[next_node]) { - dist[next_node] = dist[node] + weight; - parent[next_node] = node; - lastUpdated = next_node; - anyUpdate = true; - } - } - } - - if (!anyUpdate) { - return cycle; - } - } - - // negative weight cycle if we can still relax an edge - if (lastUpdated != Node()) { - Node cycleStart = lastUpdated; - - for (int i = 0; i < numVertices; ++i) { - cycleStart = parent[cycleStart]; - } - - Node curr = cycleStart; - - do { - cycle.push_back(curr); - curr = parent[curr]; - } while (curr != cycleStart); - - cycle.push_back(cycleStart); - std::reverse(cycle.begin(), cycle.end()); - - return cycle; - } - - return cycle; -} - -template std::vector detectArbitrageBellman(const AdjList& graph);