<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2026-10-06T04:42:01+00:00</updated><id>/feed.xml</id><title type="html">Nick’s Misc Blog</title><subtitle>Random musings related to my hobbies + projects</subtitle><entry><title type="html">Recent Experiences with Direct to Consumer Manufacturing</title><link href="/pcb/glasses/manufacturing/2026/09/26/rise-direct-to-consumer.html" rel="alternate" type="text/html" title="Recent Experiences with Direct to Consumer Manufacturing" /><published>2026-09-26T07:00:00+00:00</published><updated>2026-09-26T07:00:00+00:00</updated><id>/pcb/glasses/manufacturing/2026/09/26/rise-direct-to-consumer</id><content type="html" xml:base="/pcb/glasses/manufacturing/2026/09/26/rise-direct-to-consumer.html"><![CDATA[<p>Direct to consumer manufacturing is a business model where the manufacturer sells directly to the end customer. It’s different from traditional methods since no middle-man is involved, and products are commonly built to order.</p>

<p>I recently came across this concept when designing a circuit board and ordering a new pair of glasses. In both cases, I was surprised by how streamlined the experience was, the quality of the output and how similar the websites were to each other.</p>

<h3 id="jlcpcb-experience">JLCPCB Experience</h3>

<p>I designed the circuit board w/ the help of Opus 4.8 and some external plugins and sent the design files to JLCPCB to manufacture. The design is simple, consisting of wiring up five slide pots to two ADCs in a form factor that can sit over a rpi 5 with an audio hat attached.</p>

<div style="display: flex; justify-content: center; gap: 10px;">
  <div style="width: 40%; text-align: center;">
    <img src="/imgs/direct_to_consumer/gerberpreview.png" style="width: 100%; height: auto;" alt="gerber pcb image" />
    <p><strong>Front Preview</strong></p>
  </div>
  <div style="width: 40%; text-align: center;">
    <img src="/imgs/direct_to_consumer/gerberpreviewback.png" style="width: 100%; height: auto;" alt="gerber pcb image" />
    <p><strong>Back Preview</strong></p>
  </div>
</div>

<p>There were a couple hiccups during the design process related to identifying component dimensions, running the proper router and fixing DRCs, but there were no major issues that took over a day to solve. I tested the assembled PCB after it arrived and it seats and works perfectly too.</p>

<h3 id="zenni-experience">Zenni Experience</h3>

<p>The glasses were a much simpler process, only requiring me to request my prescription from my optometrist (w/ PD) and enter the measurements into Zenni (a made to order glasses website) to manufacture a pair. I looked through a couple different styles and just picked a random one that caught my eye, image below.</p>

<div style="text-align: center;">
  <img src="/imgs/direct_to_consumer/zenniglasses.png" style="width: 55%; height: auto;" alt="zenni glasses" />
</div>
<p><br /></p>

<h3 id="process-similarities---pcb--glasses">Process Similarities - PCB + Glasses</h3>

<p><strong>Cost</strong>
<br />
I can’t price in the PCB since I don’t know any alternatives, but the glasses were pretty cheap. They cost me $28 without insurance and the PCB run cost $40 for 2 assembled + 3 PCB only. For comparison, my old glasses consistently cost ~$300. Damn.
<br />
I think there’s a real argument to be made that glasses from an optometrist tend to be costlier due to name brands, multiple high quality coatings and thin lenses. However, the Zenni’s work perfectly and I feel that a large amount of the cost traditionally comes from the available in-person choices forcing consumers towards pricier choices rather than addressing their needs.
<br />
<br />
<strong>Manufacturing location</strong>
<br />
Both are made in China. I saw a video recently about sendcutsend, a website that does small batch orders for machining and they mentioned that most metal shops in the US won’t even consider taking smaller orders. US manufacturing tends to focus on huge volume + low margin (mass production), or huge profit + low volume (boutique).
<br />
I think this phenomenon comes from the high operating costs in the US, combined with the lack of easy supplier access compared to other countries such as China. Until those issues are addressed, it’ll be rare to see direct to consumer fabrication located w/in the US.
<br />
Despite the geographical distance, both products arrived in ~1.5 weeks which I feel is standard for glasses anyways, even from an optometrist.
<br />
<br />
<strong>Ease of Use</strong>
<br />
Both websites try to make the product comparison, evaluation, and ordering process easy.
<br />
JLCPCB’s website expects you to know what you’re doing a bit more compared to Zenni, but it’s very simple to get something working (different from use proficiently) after watching some videos and reading their help guide.
<br />
<br />
<strong>Manufacturing Transparency</strong>
<br />
This was my favorite part of the process and what inspired me to even document my thoughts.
<br />
After sending in the manufacturing order, both websites will show you a pipeline of what stage your order is in (grinding lenses, placing parts on the PCB, moving between warehouses). It’s really fun to look at and I recall checking on the JLCPCB page multiple times a day while I was waiting for my parts to arrive.
<br />
<br /></p>

<h3 id="future-challenges">Future Challenges</h3>
<p>Having used both of these sites, I think that the biggest challenge to making more and better direct to consumer products is a lack of consumer confidence. For both cases, it still felt like a bit of a leap of faith that whatever I ordered was going to work.</p>

<p>I think the PCB case is more understandable as this is my first venture into the field and more experienced designers would have ran a comprehensive test suite and more checks. However, I think it’s a real issue that I was unsure of what size of glasses to buy + how accurate the style was going to be. The AI face measurer + styler felt a bit inaccurate and unrealistic. One potential solution could involve Zenni mailing out a paper copy of the glasses in different sizes, so customers can try them on and see how they’d look.</p>

<p>With that said, both websites were great and I believe that the direct to consumer market will only continue to grow, especially as the need for specialized products at cheaper prices increases. I foresee instilling consumer confidence as the biggest challenge in that regard.</p>]]></content><author><name></name></author><category term="pcb" /><category term="glasses" /><category term="manufacturing" /><summary type="html"><![CDATA[Direct to consumer manufacturing is a business model where the manufacturer sells directly to the end customer. It’s different from traditional methods since no middle-man is involved, and products are commonly built to order.]]></summary></entry><entry><title type="html">Dimensioning Mystery Slide Pots</title><link href="/potentiometers/hardware/technical-drawings/2026/09/11/dimensioning-mystery-slide-pots.html" rel="alternate" type="text/html" title="Dimensioning Mystery Slide Pots" /><published>2026-09-11T07:00:00+00:00</published><updated>2026-09-11T07:00:00+00:00</updated><id>/potentiometers/hardware/technical-drawings/2026/09/11/dimensioning-mystery-slide-pots</id><content type="html" xml:base="/potentiometers/hardware/technical-drawings/2026/09/11/dimensioning-mystery-slide-pots.html"><![CDATA[<p>I was recently working on a project involving slide potentiometers and wanted to look for a cheap source to pick up ~10 of them (only needed 5, but wanted to get some extras in case).</p>

<p><img src="/imgs/slidepots/amz_pg.png" alt="amazon page image" /></p>

<h3 id="looking-for-dimensions">Looking for Dimensions</h3>

<p>After breaking them out to a breadboard and validating my initial project design, the next step required finding their dimensions for my PCB design. After looking for a while, I couldn’t find any link to a datasheet on Amazon even after reviewing the original page and looking for similar components.</p>

<p>An online search for similar <code class="language-plaintext highlighter-rouge">75mm B103 slide pots</code> yielded a near-identical looking component with a wiring + dimensioning diagram at the <a href="https://www.sc-qixing.com/sale-41455993-b103-75mm-mixer-fader-b10k-dual-channel-straight-sliding-potentiometer-integrated-circuits.html">following link</a>. Some info on the page is wrong, such as the travel being 75mm when the length is 75mm and the travel is 60mm (diagram is right, table is wrong).</p>

<h3 id="measurements--usage">Measurements + Usage</h3>

<p>Just in case, I bought a cheap pair of Amazon calipers and averaged measurements from the 10 pots I bought, and found that there were only 2 notable differences. To use the measurements for your own project, I recommend using the manufacturer spec + my mounting lug dimensions.</p>
<ul>
  <li>I had the measurements for the 2 center mounting lug positions (they only use the pins)</li>
  <li>The distance between the top and bottom pins are ~1mm longer from my measurements (70.8mm vs 70mm)</li>
</ul>

<p>The diagrams of my measurements vs the vendor’s are below.</p>

<p><img src="/imgs/slidepots/vendor_mounting_hole_dimensions.svg" alt="vendor measurements" />
<img src="/imgs/slidepots/slide_pot_dimensions.svg" alt="my measurements" /></p>

<p>Below is a wiring diagram for a single channel of the pot (view from bottom, flipped vs measurement diagrams which are viewed from above).</p>

<p><img src="/imgs/slidepots/slide_pot_single_channel_pins.svg" alt="wiring diagram" /></p>]]></content><author><name></name></author><category term="potentiometers" /><category term="hardware" /><category term="technical-drawings" /><summary type="html"><![CDATA[I was recently working on a project involving slide potentiometers and wanted to look for a cheap source to pick up ~10 of them (only needed 5, but wanted to get some extras in case).]]></summary></entry><entry><title type="html">Thoughts on ProgramBench Part 1</title><link href="/2026/05/10/thoughts-on-programbench-part1.html" rel="alternate" type="text/html" title="Thoughts on ProgramBench Part 1" /><published>2026-05-10T07:00:00+00:00</published><updated>2026-05-10T07:00:00+00:00</updated><id>/2026/05/10/thoughts-on-programbench-part1</id><content type="html" xml:base="/2026/05/10/thoughts-on-programbench-part1.html"><![CDATA[<p>Meta (in conjunction w/ Stanford + Harvard) released a <a href="https://arxiv.org/pdf/2605.03546v1">paper last week called <strong>ProgramBench</strong></a> detailing the state of language models for writing code repos. It consists of the paper, the <a href="https://github.com/facebookresearch/programbench">github</a> to run the eval, and the <a href="https://huggingface.co/datasets/programbench/ProgramBench-Tests">huggingface link</a> to download the test blobs.</p>

<p>They defined the task by scraping 200 github repos based off some heuristics, generating tests using a synthetic pipeline, then feeding the golden compiled program and the repo’s documentation to <a href="https://github.com/SWE-agent/mini-swe-agent">mini-swe-agent</a> and having it generate the repo + a build script. The build script + code is then ran against the synthetic TB.</p>

<p>Notably, they found that no runs resulted in a 100% TB pass although a couple runs came close. I had some thoughts on how this experiment was carried out and wanted to dive into them in this post.</p>

<p>A full recreation of the repo w/ the necessary modifications to run my experiments can be found at <a href="https://github.com/NickCheng0921/ProgramBench/tree/main/modifications">my fork</a>. I’ve added support to make single runs w/ Openrouter as a provider.</p>
<ul>
  <li>main repo is eval only, I’ve added the steps + extra code to pull the TBs + run the task</li>
</ul>

<h1 id="fast-run-through-of-paper">Fast run through of paper</h1>

<p>Following images are from the paper.</p>

<p><a href="https://arxiv.org/pdf/2605.03546v1#page=3">Tasks were created</a> by scraping Github w/ some heuristics to create testbenches. TBs were made in Python using <code class="language-plaintext highlighter-rouge">mini-swe-agent</code> w/ <code class="language-plaintext highlighter-rouge">Sonnet 4.5</code> (interesting choice given they had access to better models).</p>

<p>The hand icon is <code class="language-plaintext highlighter-rouge">mini-swe-agent</code>’s logo and refers to an agentic step. <code class="language-plaintext highlighter-rouge">mini-swe-agent</code> is a lightweight single agent pipeline/flow, comparative to claude-agent-sdk.</p>

<p><img src="/imgs/programbench/programbench_task_creation.png" alt="alt text" /></p>

<p>The authors have provided docker images per task, and run the agent in there w/ network access disabled along w/ some additional checks to prevent cheating.</p>

<p><img src="/imgs/programbench/pbench_task_run.png" alt="alt text" /></p>

<h1 id="thoughts-on-methodology">Thoughts on Methodology</h1>

<p>None of these ideas were created/assisted by AI, they occured as I was going through the paper. I agree with a lot of the methodology and I think the work is novel, I just had some points I was interested in.</p>

<h3 id="1---task-formulation">1 - task formulation</h3>

<p>The goal of the paper is “to measure the ability of software engineering agents to develop software holisitically”. Having the golden executable when creating software seems like a bit of a stretch.</p>

<p>I see the reasoning for it, as we lack a clean specification, but this doesn’t seem to match any software development task I’ve seen outside of reverse engineering and the paper targets “holistic” software development.</p>

<h3 id="2---questionable-test-quality-from-synthetic-pipeline">2 - questionable test quality from synthetic pipeline</h3>

<p>Due to the scale of the benchmark in comparison to the size of the research team, I see the use of a synthetic pipeline.</p>

<p>However, agents can be <strong>pretty awful</strong> at writing good integration tests w/o human oversight and we can see this in the repo. Cmatrix (one of the tested libs) has very few tests checking the actual animation functionality of the library and a majority of the tests are unit tests checking CLI arg behavior. What tests do exist for animation just check that something happened.</p>

<p>An executable that produces an unsatisfactory animation, but handles the CLI args properly would score extremely high on the benchmark (and could even pass it).</p>

<p>This issue is fundamental to synthetic pipelines being used to create TBs. Without human oversight, it’s very hard to describe the <strong>use</strong> of software. It’s trivially easy for the model to enumerate the flags and create combinations and write hundreds of garbage tests.</p>

<h3 id="3---lack-of-replication">3 - lack of replication</h3>

<p>Each of the 200 tasks was ran w/ each of the 9 LMs once. LMs are non-deterministic and I believe that output quality varies more as turns increase (variance of a random walk).</p>

<p>It’s standard practice to use pass@k or make multiple runs to capture output variability and I don’t see a real reason that wasn’t done here.</p>

<p>I could see an argument for cost?, but less LMs or tasks could have been selected.</p>

<h3 id="4---lack-of-open-source-models">4 - lack of open source models</h3>

<p>self-explanatory</p>

<h3 id="5---model-memorization">5 - model memorization</h3>

<p>Training on github repos is standard practice for LMs now and it’s <a href="https://arxiv.org/html/2512.10218v2">made model/agent evaluation</a> very difficult. There’s a real possibility of contamination here, especially w/ the newer models.</p>

<p>I’ll come back to this point in my experiment.</p>

<h1 id="my-experiment">My experiment</h1>

<p>So where does this leave us?</p>

<p>I have two goals I want to pursue from here:</p>
<ol>
  <li>Recreate the experiment and note any learnings</li>
  <li>Improve on the methodology (multiple ideas I’m saving for a future post)</li>
</ol>

<p>To do this, I made a <a href="https://github.com/NickCheng0921/ProgramBench/tree/main/modifications">fork</a> that runs tasks w/ OpenRouter as the provider. I used <code class="language-plaintext highlighter-rouge">deepseek-v4-flash</code> because I don’t have a superintelligence sized budget (the 14 runs I made cost me ~4 buckaroos), but I’m planning to test more down the line.</p>

<p>I selected 3 C repos (<a href="https://github.com/abishekvashok/cmatrix">cmatrix</a>, <a href="https://github.com/arq5x/bedtools2">bedtools2</a>, <a href="https://github.com/doxygen/doxygen">doxygen</a>) that most models did well on to recreate the runs for, because I’m the most competent w/ C out of the compilable languages the authors selected.</p>

<p>I used <code class="language-plaintext highlighter-rouge">mini-swe-agent</code> w/ the same sys prompt from the repo, and used the following task msg since I couldn’t find the original.</p>

<p><code class="language-plaintext highlighter-rouge">Write some tests for the existing executable, then write your own code and check if it passes. Complete once done by issung COMPLETE_TASK. If a binary errors with Error opening terminal or similar, it's a curses app — focus on --help, version, and error-path testing rather than trying to run it interactively. Use script -qc only if a specific test requires the binary to start successfully under a pty.</code></p>

<h3 id="experiment-results--findings">Experiment Results + Findings</h3>

<p>Deepseek values are from my runs, other values are from the paper/website.</p>

<p>Note that <strong>pass rate</strong> is <strong>test pass %</strong>. No runs resulted in a 100% TB test, they all get partial scores.</p>

<h3 id="cmatrix"><strong>Cmatrix</strong></h3>

<p>I noticed that deepseek outputted the original authors in the headers of the code for 1 of 4 runs. Compared to the next 2 tasks, all the models did well leading me to believe that the models strongly memorized the original code, or the TB is trivial because the application is GUI focused and hard to test.</p>

<p>Runs 2-4 gave 99% TB pass rate (the code is different). I wonder if this is related to the provider (Novita through OpenRouter) potentially caching some of my calls.</p>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th>Pass rate</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>deepseek-v4-flash</td>
      <td>81%, 99%, 99%, 99%</td>
    </tr>
    <tr>
      <td>opus-4.7</td>
      <td>95%</td>
    </tr>
    <tr>
      <td>gemini-3.1-pro</td>
      <td>94%</td>
    </tr>
    <tr>
      <td>gpt-5.4</td>
      <td>92%</td>
    </tr>
  </tbody>
</table>

<p><img src="/imgs/programbench/cmatrix.png" alt="alt text" /></p>

<h3 id="bedtools2--doxygen"><strong>Bedtools2 + Doxygen</strong></h3>

<p>0% deepseek runs are compile failures due to buggy code.</p>

<p><img src="/imgs/programbench/bedtools2.png" alt="alt text" /></p>

<p><img src="/imgs/programbench/doxygen.png" alt="alt text" /></p>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th>bedtools2 pass rate</th>
      <th>doxygen pass rate</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>deepseek-v4-flash</td>
      <td>24%, 16%, 19%, 0%, 23%</td>
      <td>0%, 34%, 18%, 24%, 23%</td>
    </tr>
    <tr>
      <td>opus-4.7</td>
      <td>2%</td>
      <td>35%</td>
    </tr>
    <tr>
      <td>gemini-3.1-pro</td>
      <td>20%</td>
      <td>16%</td>
    </tr>
    <tr>
      <td>gpt-5.4</td>
      <td>17%</td>
      <td>30%</td>
    </tr>
  </tbody>
</table>

<p>We can see that multiple runs of deepseek gave a big spread. I’m curious to see what the spread of the closed source models looks like.</p>

<h3 id="next-steps">Next Steps</h3>

<ol>
  <li>
    <p>Improve on methodology</p>
  </li>
  <li>
    <p>Does the model creating the TB matter? Maybe Anthropic models performed a bit better here because Sonnet 4.5 created the TB</p>
  </li>
  <li>
    <p>Investigate sensitivity of model performance to task prompt</p>
  </li>
</ol>]]></content><author><name></name></author><summary type="html"><![CDATA[Meta (in conjunction w/ Stanford + Harvard) released a paper last week called ProgramBench detailing the state of language models for writing code repos. It consists of the paper, the github to run the eval, and the huggingface link to download the test blobs.]]></summary></entry><entry><title type="html">Conferences Attended</title><link href="/conferences/2026/05/05/conferences.html" rel="alternate" type="text/html" title="Conferences Attended" /><published>2026-05-05T07:00:00+00:00</published><updated>2026-05-05T07:00:00+00:00</updated><id>/conferences/2026/05/05/conferences</id><content type="html" xml:base="/conferences/2026/05/05/conferences.html"><![CDATA[<p>List will be updated as I attend more conferences :)</p>

<h2 id="2026">2026</h2>
<ul>
  <li><strong>SNUG</strong> - <a href="https://www.synopsys.com/community/snug/snug-silicon-valley.html">Synopsys User Group</a></li>
  <li><strong>Coreweave</strong> - <a href="https://www.coreweave.com/fully-connected-2026">Fully Connected 2026</a></li>
</ul>

<h2 id="2025">2025</h2>
<ul>
  <li><strong>ICLAD</strong> - <a href="https://iclad.ai/lad25-home-page">International Conference on LLM-Aided Design</a>
    <ul>
      <li>attended Hackathon event as well</li>
    </ul>
  </li>
  <li><strong>DAC</strong> - <a href="https://dac.com/2025/62nd-dac">Design Automation Conference</a></li>
</ul>]]></content><author><name></name></author><category term="Conferences" /><summary type="html"><![CDATA[List will be updated as I attend more conferences :)]]></summary></entry><entry><title type="html">Monte Carlo State-Value Estimation for BlackJack</title><link href="/monte/carlo/python/blackjack/reinforcement/learning/2026/04/28/mc-blackjack.html" rel="alternate" type="text/html" title="Monte Carlo State-Value Estimation for BlackJack" /><published>2026-04-28T07:07:45+00:00</published><updated>2026-04-28T07:07:45+00:00</updated><id>/monte/carlo/python/blackjack/reinforcement/learning/2026/04/28/mc-blackjack</id><content type="html" xml:base="/monte/carlo/python/blackjack/reinforcement/learning/2026/04/28/mc-blackjack.html"><![CDATA[<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>

<script id="MathJax-script" async="" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>

<p>I’ve been looking into reinforcement learning lately to create a <a href="https://github.com/NickCheng0921/GodotRLRacer">racing agent</a>.</p>

<p>As part of this process, I’m reading <strong>Reinforcement Learning: An Intro 2nd Edition</strong> by Sutton &amp; Barto as my background in ML is supervised/unsupervised learning (<a href="https://github.com/NickCheng0921/BandSplitRNN-Pytorch">blind source separation</a>, <a href="https://oaktrust.library.tamu.edu/server/api/core/bitstreams/1e4c3130-48ee-4356-955d-b82f1ed548ed/content">variance pretraining</a>). It’s not too useful for my project implementation as I’m experimenting with newer methods like PPO and reward design, but it helps me build a stronger foundation and intuition.</p>

<p>Chapter 5 (Monte Carlo Methods) shows an interesting looking plot of the state-value estimation of an arbitrary player strategy in BlackJack. The plot tells us what the expected return is for a given state under the following strategy: player hits until &gt;= 20 and dealer hits til &gt;= 17.</p>

<p><img src="/imgs/sutton_barto_rl_mc_blackjack.png" alt="img" /></p>

<p>I wanted to try and recreate this from scratch in Python out of curiosity. Pdfs of the book float around online, such as <a href="https://web.stanford.edu/class/psych209/Readings/SuttonBartoIPRLBook2ndEd.pdf">here</a> (above fig from pdf pg 130).</p>

<h3 id="more-info-on-the-environment">More Info on the Environment</h3>

<p>Agent env is standard BJ, with rewards {1, -1, 0} for winning, losing and drawing. We assume an infinite deck, and the agent’s observation space is their current hand value, whether they have a usable ace, and the visible dealer card. The environment auto hits the player to &gt; 11 so we can’t have &gt; 1 usable ace (if you were wondering why usable ace is binary).</p>

<p>This gives us an observation space of 200 discrete states (Player hand value [12, 21] = 10 states, Dealer Card [A, 10] = 10 states, Usable Ace {0, 1} = 2 states). The player can hit or stand which is a discrete action of 2 states.</p>

<p>The value estimation formula can be found in the core code in the next section, we estimate the value using V(state) = V(s) + (G - V(s))/N where V is the value, G is return from this state to episode end, and N is the total # of times we reached the state.</p>

<h3 id="code">Code</h3>

<p>Core logic to run the MC sim is at <a href="/projects/monte_carlo_blackjack/core.txt">core.txt</a></p>

<p>To get the plots, run <a href="/projects/monte_carlo_blackjack/sweep.txt">sweep.txt</a> (you’ll need core as well, rename both to .py files)</p>

<h3 id="results">Results</h3>

<p>The following plot shows the value estimation improving from 1K to 50K episodes. The usable ace cases are rarer so it takes a bit longer for the values to converge.</p>

<p><img src="/imgs/mc_bj_1K_50K_sweep.gif" alt="img" /></p>

<p>Here’s what it looks like after 500K episodes (much closer to the book image than the 50K episode one from above).</p>

<p><img src="/imgs/mc_bj_500K.png" alt="img" /></p>

<p>Notice how most of the plot is dark blue/purple? That means we’re expected to lose at that point since our strategy of hitting til 20 is suboptimal compared to the dealer one. Let’s try playing the same strategy as the dealer instead.</p>

<p><img src="/imgs/mc_bj_player17_sweep.gif" alt="img" /></p>

<p><img src="/imgs/mc_bj_player17_500K_sweep.png" alt="img" /></p>

<p>Much better, the brighter colors (higher values) show that our new strategy doesn’t lose as hard as before.</p>]]></content><author><name></name></author><category term="Monte" /><category term="Carlo" /><category term="Python" /><category term="Blackjack" /><category term="Reinforcement" /><category term="Learning" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Fast Monte Carlo Ticker Permutation w/ Pandas</title><link href="/pandas/monte/carlo/permutation/backtest/2025/10/08/faster-pandas-permutation.html" rel="alternate" type="text/html" title="Fast Monte Carlo Ticker Permutation w/ Pandas" /><published>2025-10-08T07:03:00+00:00</published><updated>2025-10-08T07:03:00+00:00</updated><id>/pandas/monte/carlo/permutation/backtest/2025/10/08/faster-pandas-permutation</id><content type="html" xml:base="/pandas/monte/carlo/permutation/backtest/2025/10/08/faster-pandas-permutation.html"><![CDATA[<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>

<script id="MathJax-script" async="" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>

<p>This post details how using native Pandas operations gives significant speedup over a naive implementation in the context of permuting price paths.</p>

<h3 id="motivation">Motivation</h3>

<p>Lately, I’ve been looking into how to perform more rigorous backtests for my trading indicators.</p>

<p>The main source of my study is <a href="https://books.google.com/books/about/Permutation_and_Randomization_Tests_for.html?id=SiJczQEACAAJ">Permutation and Randomization Tests for Trading System Development</a> by Timothy Masters. The book details methodologies for estimating strategy performance such as permuting existing price data to form multiple alternate paths, allowing us to run our strategy on multiple possible realities.</p>

<p>Suppose we want to generate one of these walks for our asset \(S\) from time \(t_0\) to \(t_n\) which moves as \(S_0, S_1, ..., S_n\). \(S_i\) can be Microsoft’s closing price at day \(i\) as an example.</p>

<p>The movement of the asset can be modelled as a small multiplicative modifier \(\sigma\) where \(S_0\cdot\sigma_0=S_1\) and \(S_0\cdot\sigma_0\cdot\sigma_1=S_1\cdot\sigma_1=S_2\) (assuming \(\sigma\) comes from a normal dist. gives us a geometric brownian motion, handy for basic pricing + modelling).</p>

<p>The rest of this post assumes that we work w/ log pricing as this makes the movement additive and gives the benefit of normalizing price differences over long horizons.</p>

<p>Under log pricing, our new equation for asset movement is then \(S_n = S_0 + \sigma_0 + \sigma_1 + ... + \sigma_{n-1}\). Notice how <strong>reordering</strong> the \(\sigma\)’s doesn’t change the final price as the movements are commutative.</p>

<p>We can obtain \(S_3\) from (\(S_0 + \sigma_0 + \sigma_2 + \sigma_1\)) or (\(S_0 + \sigma_2 + \sigma_1 + \sigma_0\)) despite them being separate paths.</p>

<p>As such, we can generate nearly any number of alternate paths that start at \(S_0\) and end at \(S_n\) by reordering \(\sigma\). The book details why we want to do this, but as a TL:DR, path permutation destroys patterns and preserves trends.</p>

<h3 id="naive-implementation">Naive Implementation</h3>

<p>Here’s a basic implementation of the path permutation algorithm in Python.</p>

<p>Note 3 key differences in the implementation vs the explanation:</p>
<ul>
  <li>the <code class="language-plaintext highlighter-rouge">Close</code>, <code class="language-plaintext highlighter-rouge">High</code> and <code class="language-plaintext highlighter-rouge">Low</code> are moved relative to the daily <code class="language-plaintext highlighter-rouge">Open</code> to preserve intraday trends</li>
  <li>we permute <code class="language-plaintext highlighter-rouge">Open</code> -&gt; <code class="language-plaintext highlighter-rouge">Close</code> -&gt; <code class="language-plaintext highlighter-rouge">Open</code> to preserve interday trends rather than <code class="language-plaintext highlighter-rouge">Close</code> -&gt; <code class="language-plaintext highlighter-rouge">Close</code></li>
  <li>we permute interday and intraday movements separately to break up predictable patterns</li>
</ul>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">get_example_data</span><span class="p">():</span>
    <span class="kn">import</span> <span class="nn">yfinance</span> <span class="k">as</span> <span class="n">yf</span>
    <span class="n">df</span> <span class="o">=</span> <span class="n">yf</span><span class="p">.</span><span class="n">download</span><span class="p">([</span><span class="s">'MSFT'</span><span class="p">],</span> <span class="n">period</span><span class="o">=</span><span class="s">'10y'</span><span class="p">,</span> <span class="n">auto_adjust</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">df</span><span class="p">.</span><span class="n">columns</span> <span class="o">=</span> <span class="p">[</span><span class="s">'Close'</span><span class="p">,</span> <span class="s">'High'</span><span class="p">,</span> <span class="s">'Low'</span><span class="p">,</span> <span class="s">'Open'</span><span class="p">,</span> <span class="s">'Volume'</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">df</span>

<span class="k">def</span> <span class="nf">get_perm_slow</span><span class="p">(</span><span class="n">log_df</span><span class="p">,</span> <span class="n">seed</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">log_perm_df</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">copy</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">log_df</span><span class="p">))</span>
    <span class="c1"># intraday relative movements of high, low, close relative to open
</span>    <span class="c1"># keep first bar constant or else first and last bar won't be identical
</span>    <span class="n">intra_h_o</span> <span class="o">=</span> <span class="p">(</span><span class="n">log_df</span><span class="p">[</span><span class="s">'High'</span><span class="p">]</span> <span class="o">-</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Open'</span><span class="p">])[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="n">intra_l_o</span> <span class="o">=</span> <span class="p">(</span><span class="n">log_df</span><span class="p">[</span><span class="s">'Low'</span><span class="p">]</span> <span class="o">-</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Open'</span><span class="p">])[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="n">intra_c_o</span> <span class="o">=</span> <span class="p">(</span><span class="n">log_df</span><span class="p">[</span><span class="s">'Close'</span><span class="p">]</span> <span class="o">-</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Open'</span><span class="p">])[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="c1"># shift(-1) is next row val, shift(1) is prev
</span>    <span class="n">inter_c_o</span> <span class="o">=</span> <span class="p">(</span><span class="n">log_df</span><span class="p">[</span><span class="s">'Open'</span><span class="p">].</span><span class="n">shift</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Close'</span><span class="p">])[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

    <span class="c1"># keep price of first bar stable
</span>    <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
    <span class="n">perm_ids</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">permutation</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">log_df</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)))</span>
    <span class="n">perm_ids2</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">permutation</span><span class="p">(</span><span class="n">perm_ids</span><span class="p">)</span>
    
    <span class="n">log_perm_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">'Close'</span><span class="p">,</span> <span class="s">'High'</span><span class="p">,</span> <span class="s">'Low'</span><span class="p">,</span> <span class="s">'Open'</span><span class="p">])</span>
    <span class="n">log_perm_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">copy</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">log_df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
    
    <span class="c1"># starting from first bar
</span>    <span class="k">for</span> <span class="n">idx</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">log_df</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span>
        <span class="n">pidx</span> <span class="o">=</span> <span class="n">perm_ids</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
        <span class="n">pidx2</span> <span class="o">=</span> <span class="n">perm_ids2</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
        <span class="c1"># new open = last Close + permuted inter-bar movement
</span>        <span class="n">o</span> <span class="o">=</span> <span class="n">log_perm_df</span><span class="p">[</span><span class="s">'Close'</span><span class="p">].</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">inter_c_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">pidx</span><span class="p">]</span>
    
        <span class="c1"># h,l,c = new open + permuted log inter diff
</span>        <span class="n">h</span> <span class="o">=</span> <span class="n">o</span> <span class="o">+</span> <span class="n">intra_h_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">pidx2</span><span class="p">]</span>
        <span class="n">l</span> <span class="o">=</span> <span class="n">o</span> <span class="o">+</span> <span class="n">intra_l_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">pidx2</span><span class="p">]</span>
        <span class="n">c</span> <span class="o">=</span> <span class="n">o</span> <span class="o">+</span> <span class="n">intra_c_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">pidx2</span><span class="p">]</span>
    
        <span class="n">log_perm_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">log_perm_df</span><span class="p">)]</span> <span class="o">=</span> <span class="p">[</span><span class="n">c</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">l</span><span class="p">,</span> <span class="n">o</span><span class="p">]</span>
    
    <span class="n">log_perm_df</span><span class="p">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">log_df</span><span class="p">.</span><span class="n">index</span>
    <span class="k">return</span> <span class="n">log_perm_df</span></code></pre></figure>

<p>It gets the job done, but runs fairly slowly averaging .51 seconds per call on a 10 year chunk of OHLC data.</p>

<h3 id="back-to-the-drawing-board">Back to the drawing board</h3>

<p>We want this code to run thousands of times per strategy, meaning we want to squeeze more performance. The bottleneck is the <code class="language-plaintext highlighter-rouge">for</code> loop recreating the price path by going through each movement in a random order.</p>

<p>We can greatly expedite this process if we can find a way to write that loop using Pandas operations, and letting the library handle the optimization.</p>

<p>The <code class="language-plaintext highlighter-rouge">for</code> loop can be broken down into two operations:</p>
<ul>
  <li>get a new <code class="language-plaintext highlighter-rouge">Open</code> by adding interday movement from the last <code class="language-plaintext highlighter-rouge">Close</code></li>
  <li>get <code class="language-plaintext highlighter-rouge">High</code>, <code class="language-plaintext highlighter-rouge">Low</code> and <code class="language-plaintext highlighter-rouge">Close</code> by adding intraday movement relative to <code class="language-plaintext highlighter-rouge">Open</code></li>
</ul>

<p>Careful reading of the code + algorithm shows that we can perform these updates independently w/ vectorized calls as long as we take care to propagate the <code class="language-plaintext highlighter-rouge">Close</code> -&gt; <code class="language-plaintext highlighter-rouge">Open</code> shift properly.</p>

<p><code class="language-plaintext highlighter-rouge">High</code> and <code class="language-plaintext highlighter-rouge">Low</code> are intraday movement relative to <code class="language-plaintext highlighter-rouge">Open</code> and rely on <code class="language-plaintext highlighter-rouge">Close</code>, so we can trivially vectorize their movement relative to the <code class="language-plaintext highlighter-rouge">Open</code>.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1"># Old
</span><span class="k">for</span> <span class="n">idx</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">log_df</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span>
    <span class="n">pidx2</span> <span class="o">=</span> <span class="n">perm_ids2</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
    <span class="n">h</span> <span class="o">=</span> <span class="n">o</span> <span class="o">+</span> <span class="n">intra_h_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">pidx2</span><span class="p">]</span>

<span class="c1"># New
</span><span class="n">x</span><span class="p">[</span><span class="s">'High'</span><span class="p">]</span> <span class="o">+=</span> <span class="n">intra_h_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">perm_ids2</span><span class="p">].</span><span class="n">to_numpy</span><span class="p">()</span></code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">Open</code> only depends on the intraday movement from the last <code class="language-plaintext highlighter-rouge">Close</code> so we don’t need to modify it.</p>

<p>Intraday <code class="language-plaintext highlighter-rouge">Close</code> movement is trickier and requires 2 vectorized operations:</p>
<ul>
  <li>Add the permuted movement of <code class="language-plaintext highlighter-rouge">Close</code> relative to <code class="language-plaintext highlighter-rouge">Open</code> as a cumulative sum (each <code class="language-plaintext highlighter-rouge">Close</code> movement permanently affects every subsequent <code class="language-plaintext highlighter-rouge">Close</code>)</li>
  <li>propagate this movement to every subsequent bar except <code class="language-plaintext highlighter-rouge">Close</code> (intraday <code class="language-plaintext highlighter-rouge">Close</code> update lags by 1 timestep to every other bar)</li>
</ul>

<p>Lastly, we add the interday movement from <code class="language-plaintext highlighter-rouge">Close</code> to <code class="language-plaintext highlighter-rouge">Open</code> using the same trivial vectorization as the <code class="language-plaintext highlighter-rouge">High</code> and <code class="language-plaintext highlighter-rouge">Low</code> bar, except we use a cumulative sum to propagate the movement to subsequent rows.</p>

<h3 id="better-implementation">Better Implementation</h3>

<p>Putting all of this together gives us the following code.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">get_perm</span><span class="p">(</span><span class="n">log_df</span><span class="p">,</span> <span class="n">seed</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
    <span class="c1"># decouple intraday bar from interday shift
</span>    <span class="n">perm_ids</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">permutation</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">log_df</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)))</span>
    <span class="n">perm_ids2</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">permutation</span><span class="p">(</span><span class="n">perm_ids</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
    
    <span class="n">x</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">log_df</span><span class="p">)),</span> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">'Close'</span><span class="p">,</span> <span class="s">'High'</span><span class="p">,</span> <span class="s">'Low'</span><span class="p">,</span> <span class="s">'Open'</span><span class="p">])</span>
    <span class="n">x</span><span class="p">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">log_df</span><span class="p">.</span><span class="n">index</span>

    <span class="n">intra_h_o</span> <span class="o">=</span> <span class="p">(</span><span class="n">log_df</span><span class="p">[</span><span class="s">'High'</span><span class="p">]</span> <span class="o">-</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Open'</span><span class="p">])</span>
    <span class="n">intra_l_o</span> <span class="o">=</span> <span class="p">(</span><span class="n">log_df</span><span class="p">[</span><span class="s">'Low'</span><span class="p">]</span> <span class="o">-</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Open'</span><span class="p">])</span>
    <span class="n">intra_c_o</span> <span class="o">=</span> <span class="p">(</span><span class="n">log_df</span><span class="p">[</span><span class="s">'Close'</span><span class="p">]</span> <span class="o">-</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Open'</span><span class="p">])</span>
    <span class="n">inter_c_o</span> <span class="o">=</span> <span class="p">(</span><span class="n">log_df</span><span class="p">[</span><span class="s">'Open'</span><span class="p">].</span><span class="n">shift</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Close'</span><span class="p">])[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

    <span class="c1">#  Don't move first bar
</span>    <span class="n">intra_h_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">intra_l_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">intra_c_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="n">start</span> <span class="o">=</span> <span class="n">log_df</span><span class="p">[</span><span class="s">'Close'</span><span class="p">].</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">x</span> <span class="o">+=</span> <span class="n">start</span>
    
    <span class="n">x</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">x</span><span class="p">.</span><span class="n">index</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">:]</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">x</span><span class="p">.</span><span class="n">index</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">:].</span><span class="n">combine_first</span><span class="p">(</span><span class="n">log_df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
    
    <span class="c1"># Intraday permute
</span>    <span class="n">x</span><span class="p">[</span><span class="s">'High'</span><span class="p">]</span> <span class="o">+=</span> <span class="n">intra_h_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">perm_ids2</span><span class="p">].</span><span class="n">to_numpy</span><span class="p">()</span>
    <span class="n">x</span><span class="p">[</span><span class="s">'Low'</span><span class="p">]</span>  <span class="o">+=</span> <span class="n">intra_l_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">perm_ids2</span><span class="p">].</span><span class="n">to_numpy</span><span class="p">()</span>
    <span class="n">x</span><span class="p">[</span><span class="s">'Close'</span><span class="p">]</span> <span class="o">+=</span> <span class="n">intra_c_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">perm_ids2</span><span class="p">].</span><span class="n">to_numpy</span><span class="p">().</span><span class="n">cumsum</span><span class="p">()</span>

    <span class="c1"># Propagate close movement to future bars
</span>    <span class="n">propagate_close</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([[</span><span class="mi">0</span><span class="p">],</span> <span class="n">intra_c_o</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">perm_ids2</span><span class="p">].</span><span class="n">cumsum</span><span class="p">()[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]])</span>

    <span class="c1"># Already updated Close, other bars lag update by 1
</span>    <span class="n">cols_to_update</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">columns</span><span class="p">.</span><span class="n">difference</span><span class="p">([</span><span class="s">'Close'</span><span class="p">])</span>
    <span class="n">x</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">1</span><span class="p">:,</span> <span class="n">x</span><span class="p">.</span><span class="n">columns</span><span class="p">.</span><span class="n">get_indexer</span><span class="p">(</span><span class="n">cols_to_update</span><span class="p">)]</span> <span class="o">+=</span> <span class="n">propagate_close</span><span class="p">[</span><span class="mi">1</span><span class="p">:,</span> <span class="bp">None</span><span class="p">]</span>

    <span class="c1"># Interday permute
</span>    <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([[</span><span class="mi">0</span><span class="p">],</span> <span class="n">np</span><span class="p">.</span><span class="n">cumsum</span><span class="p">(</span><span class="n">inter_c_o</span><span class="p">)]),</span> <span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">x</span></code></pre></figure>

<p>We’ve replaced the <code class="language-plaintext highlighter-rouge">for</code> loop with vectorized operations allowing Pandas to optimize the code using its backend framework (NumPy).</p>

<p>In my testing, the faster implementation gives &gt; 200% speedup, and is able to run the example in ~.002 seconds rather than 0.51.</p>

<p>An example of some permuted <code class="language-plaintext highlighter-rouge">Close</code> paths for the last 10 years of MSFT is shown below.</p>

<p><img src="/imgs/mcpt_walks.png" alt="image of walks" /></p>]]></content><author><name></name></author><category term="Pandas" /><category term="monte" /><category term="carlo" /><category term="permutation" /><category term="backtest" /><summary type="html"><![CDATA[]]></summary></entry></feed>