이축 파라미터 공간: 왜 스윕의 대부분은 거의 공짜여야 하는가
"환상 없는 백테스트" 시리즈의 일부입니다.
차원의 저주는 보통 경고의 형태로 이야기됩니다: 파라미터를 하나 추가할 때마다 탐색 공간이 곱셈적으로 커지므로, 18차원 전략은 스윕하기에 절망적이라는 것입니다. 이 프레이밍은 모든 차원이 평가 비용이 동일하다고 조용히 가정합니다. 그렇지 않습니다. 우리의 듀얼/트리플 타임프레임 엔진에서는 파라미터의 3분의 2가 스윕하는 데 거의 공짜에 가깝고, 나머지 3분의 1이 사실상 전체 연산 비용을 짊어집니다. 이 분할을 보고 나면 "탐색 공간이 너무 크다"는 더 이상 올바른 불만이 아니게 됩니다. 진짜 질문은: 당신이 비용을 치르고 있는 것은 어느 차원인가입니다.
재계산이 필요 없었던 차원
우리의 파라미터 탐색 벤치마크는 멀티 타임프레임 모멘텀 전략을 돌립니다: 두세 개의 타임프레임에 걸친 Hull 이동평균(HMA)과 그 삼중 평활화 변형(HMA3)에, 크로스오버가 실행에 나설 만큼 "깨끗한지"를 판단하는 방향성 이격도 게이트를 결합한 전략입니다. 트리플-TF 변형의 전체 탐색 공간은 18차원입니다 — 타임프레임별 기간과 HMA 길이, 그리고 각 타임프레임의 진입·청산을 위한 이격도 임계값 집합입니다.
이를 스윕하는 나이브한 방법은 단일 루프입니다: 파라미터 벡터를 하나 고르고, 모든 지표를 처음부터 빌드하고, 시뮬레이션을 돌리고, 점수를 매기고, 반복합니다. 우리도 거기서 시작했습니다. 느렸고, 그 이유는 이름을 붙이고 나니 민망할 정도로 단순했습니다: 압도적 다수의 인접한 시행에서, 우리는 변하지 않은 지표를 다시 계산하고 있었던 것입니다.
진입 이격도 임계값을 0.03에서 0.035로 살짝 옮기고 다시 실행해 보십시오. 1년치 1분봉에 걸친 Hull MA는 이전 시행과 비트 단위로 완전히 동일합니다 — 임계값은 그 정의 어디에도 등장하지 않습니다. 그런데도 나이브한 루프는 매번 그것을 다시 빌드합니다. 50만 개가 넘는 바에 걸쳐, 세 개의 타임프레임 깊이로, 매번 말입니다. 우리는 연산 예산의 거의 전부를, 실제로 바꾸고 있던 파라미터에 대해 불변인 값들을 다시 도출하는 데 쓰고 있었습니다.
이 관찰이 이 글의 전부입니다. 어떤 파라미터는 지표를 바꾸고 어떤 파라미터는 오직 고정된 지표에 적용되는 의사결정 규칙만을 바꾼다는 것을 알아채는 순간, 파라미터 공간은 평평한 차원들의 구름이기를 멈추고 가격표가 극단적으로 다른 두 개의 중첩된 층이 됩니다.
하나가 아니라 두 개의 축

파라미터를 재평가할 때 무엇을 다시 계산하도록 강제하는지에 따라 나누어 봅시다:
-
비싼 축 — 지표 파라미터. 타임프레임 기간과 HMA 길이입니다. 둘 중 하나라도 바꾸면 전체 가격 시계열에 걸쳐 지표를 다시 빌드해야 합니다: 상위 타임프레임을 리샘플링하고, Hull 가중치를 계산하고, 크로스오버 시계열과 각 크로스에서의 이격도를 다시 계산해야 합니다. 이것은 히스토리의 바 하나당 O(n) 스윕이며, 서로 다른 조합마다 그 비용을 온전히 치러야 합니다. 트리플-TF 전략에서 이 축은 여섯 개의 파라미터입니다: 상위·중위·하위 타임프레임 각각에 대한
(period, hma_length)입니다. -
싼 축 — 의사결정 임계값. 이미 계산된 크로스오버 시계열과 이격도 시계열이 주어졌을 때 진입할지 청산할지를 결정하는 방향성 이격도 게이트입니다. 임계값을 바꿔도 어떤 지표도 움직이지 않습니다. 미리 계산된 시그널에 대해 단일 O(n) 패스를 한 번 더 돌려서 게이트를 확인하고 체결을 기록하기만 하면 됩니다. 트리플-TF 전략에서 이 축은 열두 개의 파라미터입니다(세 개의 타임프레임 각각에 대해 진입-매수, 진입-매도, 청산-매수, 청산-매도라는 네 개의 이격도 임계값). 듀얼-TF 변형은 여덟 개입니다(두 개의 타임프레임 각각에 네 개의 임계값).
그러므로 이 공간의 진짜 모양은 이렇습니다: 트리플-TF는 비싼 것 6개 + 싼 것 12개 = 18개, 듀얼-TF는 비싼 것 4개 + 싼 것 8개 = 12개. 차원의 3분의 2가 싼 축에 살고 있습니다. 그리고 두 축은 개수만 다른 것이 아닙니다 — 단위 비용이 세 자릿수 넘게 차이 나며, 이 숫자가 이 글의 나머지 부분이 다루는 내용입니다.
이 구조는 엄격하게 중첩되어 있습니다. 비싼 축 위의 각 점 — 타임프레임마다 하나의 구체적인 (period, hma_length) — 은 고정된 시그널 배열 집합을 정의합니다. 그 고정된 토대 위에 싼 축의 시트 전체가 놓입니다: 수천 개의 임계값 벡터가, 각각 동일한 배열에 대한 빠른 패스로 존재합니다. 토대에 대한 비용은 한 번만 치르고, 그것을 시트 전체에 걸쳐 상각합니다.
지표가 임계값에 대해 불변인 이유
이 캐싱이 작동하는 것은 운 좋은 구현상의 디테일이 아니라 수학적 사실 때문이며, 이는 이 글 전체를 떠받치는 가정이므로 정확히 짚어둘 가치가 있습니다. 지표 배열은 오직 (period, hma_length)만의 함수입니다. 이격도 임계값은 그 정의 어디에도 전혀 등장하지 않습니다.
구체적으로, 각 타임프레임에 대해 엔진은 베이스 인덱스에 정렬된 네 개의 배열을 미리 계산합니다: Hull MA hma, 그 삼중 평활화 버전인 hma3, 크로스오버 시계열 cross(+1 매수 / −1 매도 / 0), 그리고 각 크로스에서의 separation 퍼센트입니다. 이들 각각은 가격과 두 개의 지표 파라미터로부터 도출됩니다. 임계값 — separation을 비교하는 대상이 되는 숫자 — 은 나중에, 의사결정 시점에 적용됩니다. 그것은 배열을 결코 건드리지 않습니다.
이것은 사소하게 안전한 재배열이 아닙니다. 이것이 안전한 이유는 오직 우리가 정보가 언제 이용 가능해지는지에 신경을 썼기 때문이며, 이는 이 시리즈의 다른 곳에서 다루는 미래 참조 편향에 관한 연구의 관심사입니다. 베이스 바 i에서의 상위 타임프레임 지표는 마감된 상위-TF 캔들에, 진행 중인 종가가 현재 베이스 종가 close[i]와 같은 형성 중인 캔들 하나를 더한 것으로부터 계산됩니다 — 이는 바 i 시점에 알려진 값입니다. 미래로부터의 정보는 전혀 새어 들어오지 않습니다. 그래서 바 i에서 미리 계산된 시그널은 라이브 봇이 바 i 시점에 실제로 보았을 값과 정확히 같으며, 나중에 그것에 대해 어떤 임계값을 테스트하든 유효한 상태로 남습니다. 누출이 있는 시그널을 캐싱하는 것은 그 누출을 캐싱하는 것일 뿐입니다. 인과적인 시그널을 캐싱하는 것은 실제로 거래할 수 있는 무언가를 캐싱하는 것입니다.
이 불변성은 우리에게 인수분해를 제공합니다. 하나의 완전한 파라미터 벡터 θ = (indicator_params, threshold_params)의 평가를 다음과 같이 쓸 수 있습니다:
signals = build_indicators(indicator_params) # EXPENSIVE, depends only on indicator_params
score = simulate(signals, threshold_params) # CHEAP, reuses signals across all thresholds
build_indicators는 threshold_params를 읽지 않습니다. 바로 이 하나의 사실이 캐시를 정당화합니다: indicator_params를 고정한 채로 threshold_params를 자유롭게 바꾸면, signals는 한 번만 계산하면 되는 상수가 됩니다.
아키텍처: 한 번 계산하고 여러 번 스윕하기

엔진(scripts/engine_multitf.py, 우리 백테스터의 커밋 bfc8aaa)은 이 인수분해를 이름이 곧 하는 일을 정확히 말해주는 두 개의 부품으로 구현합니다.
SignalCache — 메모이즈된 비싼 축. 이것은 (period_bars, hma_length)를 키로 하는 딕셔너리입니다. 어떤 타임프레임의 시그널을 요청하면, 그 지표 조합이 이전에 빌드된 적이 있다면 캐시된 TFSignals를 반환하고, 그렇지 않다면 한 번 빌드해서 저장합니다. 키가 오직 지표 파라미터뿐이기 때문에, 지표 조합을 공유하는 모든 임계값 설정 — 촘촘한 임계값 스윕에서는 그것이 수천 개나 됩니다 — 이 동일한 캐시 엔트리에 적중합니다. 특히 상위 타임프레임에서 이 효과가 두드러집니다: 예를 들어 네 개의 후보 기간과 몇 개의 HMA 길이로 이루어진 성긴 그리드는 서로 다른 비싼 빌드의 수가 적으며, 그 각각이 그 위에 쌓인 임계값 시트 전체에서 재사용됩니다.
sweep_separations — 배치 처리된 싼 축. 이것은 캐시된 시그널 배열과 임계값 벡터들의 행렬(sps, 형태 [m, 12])을 받아, 이들 전부를 단일 컴파일된 커널로 실행합니다. 각 행은 하나의 O(n) 패스입니다: 바를 순회하고, 게이트를 적용하고, open[i+1]에서 누출 없는 체결을 기록하고, PnL과 포지션 보유 시간을 집계합니다. 이 루프 안에서는 어떤 지표도 다시 빌드되지 않습니다 — 캐시로부터 cross와 separation을 곧바로 읽어옵니다. 내부 시뮬레이션은 JIT 컴파일되어 있어서(Numba), 한 번 워밍업되고 나면 설정당 비용은 Python 오버헤드가 아니라 바에 대한 단일 선형 스캔이 지배합니다.
이 두 부품은 자연스러운 중첩 탐색으로 합쳐집니다: 바깥 루프는 비싼 축을 순회하고(각 반복이 하나의 지표 조합을 빌드하고 캐싱합니다), 안쪽 루프는 그 캐시된 배열에 대해 싼 축 전체로 넓은 배치를 뻗어냅니다. 코드에서도 모양은 정확히 그렇습니다 — 넓은 싼 배치를 감싸는 작은 비싼 루프입니다:
cache = SignalCache(base_close, base_ts) # keyed by (period, hma_length)
for htf_p, htf_h, mtf_p, mtf_h, ltf_p, ltf_h in indicator_grid: # EXPENSIVE axis (coarse)
htf = cache.get(htf_p, htf_h) # built once, then a cache hit forever
mtf = cache.get(mtf_p, mtf_h)
ltf = cache.get(ltf_p, ltf_h)
sps = sample_thresholds(m=4000) # CHEAP axis: [m, 12] threshold vectors
pnl, n_trades, bars_in_pos = sweep_separations( # one compiled batch, no indicator work
base_close, base_open, htf, ltf, sps, mtf=mtf)
비싼 빌더는 지표 그리드가 허용하는 한 최소한으로만 건드리고, 물량은 싼 스윕이 처리하게 둡니다 — 결코 움직이지 않는 배열에 대한 수천 개의 sps 행 말입니다. 이것이 최적화의 전부입니다 — 근사도 없고, 정확도의 손실도 없으며, 그저 변하지 않은 것을 다시 계산하기를 거부하는 것뿐입니다.
"공짜"가 실제로 치르는 비용: 수치로 보기

우리는 데모 워크로드에서 두 축을 측정했습니다: 1년치 전체 1분봉 ETHUSDT 바(~527k개 바), 트리플-TF, 지표는 워밍업되어 있고 JIT 컴파일은 타이밍 밖에서 이루어졌습니다.
싼 축에서 sweep_separations은 초당 약 5,600개의 임계값 설정을 지속적으로 처리합니다. 이는 각 설정에 대해 — 게이트, 체결, PnL, 익스포저를 포함한 — 완전한 시뮬레이션이며, 50만 개가 넘는 바에 걸쳐 설정당 대략 180마이크로초입니다. 이렇게 빠를 수 있는 이유는 지표 작업을 전혀 하지 않기 때문입니다: 모든 설정이 동일한 캐시된 cross와 separation 배열을 읽습니다.
이제 대안의 가격을 매겨 봅시다. 트리플-TF 지표 세트를 한 번 빌드하는 데는 수백 밀리초 정도(~0.3초)가 걸립니다 — 1년 전체에 걸쳐 세 개의 타임프레임을 리샘플링하고, Hull 가중치를 계산하고, 크로스오버와 이격도를 추출하는 작업입니다. 만약 설정 루프 안에서 지표를 다시 계산한다면 — 나이브한 단일 루프 설계 — 초당 5,600개였던 그 설정들 각각이 대신 전체 지표 빌드 비용을 치러야 합니다. 설정당 비용은 ~180마이크로초에서 ~0.3초로 폭증합니다:
| 설정당 비용 | 초당 설정 수 | |
|---|---|---|
| 싼 축(캐시된 시그널) | ~180 µs | ~5,600 |
| 설정마다 지표 재계산 | ~0.3 s | ~3.4 |
그 비율은 약 1,600배입니다. 캐시된 시그널에 대해 싼 축을 스윕하는 것은, 모든 임계값 벡터마다 지표를 다시 빌드하는 나이브한 설계보다 대략 세 자릿수만큼 저렴합니다. 구체적으로 말하면: 캐시된 경로에서는 1초도 안 걸려 끝나는 수천 개 임계값 설정의 배치가, 각 설정이 지표를 다시 빌드한다면 거의 한 시간 가까이 걸릴 것입니다. 결과도 같고, 정확도도 같으며, 수학에는 어떤 지름길도 없습니다 — 유일한 차이는 하나는 불변량을 다시 계산하고 다른 하나는 그러지 않는다는 것뿐입니다.
이것은 마지막에 살짝 뿌리는 마이크로 최적화가 아닙니다. 이것은 어떤 탐색이 가능한지를 바꿉니다. 초당 5,600개 설정이면 임계값 축은 제대로 탐색할 만큼 촘촘합니다 — 지표 조합마다 촘촘한 그리드나 긴 랜덤/QMC 샘플을 감당할 수 있습니다 — 반면 비싼 축은 의도적으로 성긴 소수의 빌드로 남아 있습니다. 연산 예산은 파라미터가 실제로 비용을 발생시키는 곳으로 흘러갑니다.
차원의 저주, 다시 가격 매기기

이 글이 시작할 때의 프레이밍으로 돌아가 봅시다. 차원의 저주는 탐색 공간이 파라미터 개수에 대해 지수적으로 커지므로, 차원이 더 많은 것은 무조건 더 나쁘다고 말합니다. 이것은 그리드의 크기에 관해서는 사실입니다. 그것을 다 훑는 비용에 관해서는 오해의 소지가 있습니다. 모든 차원의 가격을 동일하게 매기기 때문입니다.
축을 분리하고 나면 그 개수는 다르게 읽힙니다. 트리플-TF 전략은 18차원이지만, 그중 오직 6개 차원만이 비쌉니다. 나머지 12개는 싼 축 차원으로, 의미 있는 수준의 연산 비용 증가 없이 그리드를 확장합니다 — 푼돈으로 수천 개의 설정을 그 위에 던질 수 있습니다. 듀얼-TF 전략은 12차원이며 비싼 차원은 4개, 싼 차원은 8개뿐입니다. 두 경우 모두 "저주"의 대부분은 스윕하는 데 거의 아무 비용도 들지 않는 축에 집중되어 있습니다.
그러므로 탐색 비용을 정직하게 따지는 방법은 "파라미터가 몇 개인가"가 아니라 "비싼 파라미터가 몇 개이고, 그 그리드를 얼마나 성기게 만들 수 있는가"입니다. 비싼 축은 지수적 증가가 실제로 아픈 곳이며, 여기서는 작고 신중하게 고른 그리드를 원합니다 — 소수의 후보 기간, 적당한 범위의 HMA 길이 — 어쩌면 우리가 다른 곳에서 사용하는 적응형 해상도 드릴다운의 정신에 따라 성긴 것에서 세밀한 것으로 다듬어질 수도 있습니다. 싼 축은 넉넉하게 써도 되는 곳입니다. 임계값 설정 하나를 추가하는 데 드는 비용이 180마이크로초에 불과하기 때문입니다.
이 재가격 매기기는 우리의 HMA 전략을 훨씬 넘어서 일반화됩니다. 그 패턴 — 일부 파라미터는 피처를 바꾸고, 대부분의 파라미터는 고정된 피처에 적용되는 규칙을 바꾼다 — 은 시스템 트레이딩 곳곳에서 반복해서 등장합니다. 지표 길이, 리샘플링 주기, 룩백 윈도우는 비쌉니다. 진입/청산 임계값, 스톱 거리, 포지션 사이징 배수, 확인 게이트는 쌉니다. 어떤 파라미터가 이미 계산된 시그널 위에서 의사결정 경계만 다시 그리는 것이라면, 그것은 언제나 싼 축에 속하며 그곳에서 스윕되어야 합니다. 같은 계열의 관련된 캐싱 이득은 멀티 타임프레임 Parquet 캐싱과, 더 넓게는 엔진 속도 사다리에서도 나타납니다.
공짜 점심에도 청구서는 있다
싼 축은 연산에서 쌉니다. 통계에서는 싸지 않으며, 이 둘을 혼동하는 것이야말로 성능상의 승리를 과적합 기계로 둔갑시키는 방법입니다.
당신이 평가하는 임계값 설정 하나하나가 하나의 시행이며, 하나의 데이터셋에 대해 수천 번의 시행을 돌리면 그중 최선의 것은 부분적으로 운 덕분에 좋아 보입니다. 그 시행들을 1,600배 저렴하게 만든다고 해서 그 운이 사라지지는 않습니다 — 오히려 더 쉽게 쌓입니다. 촘촘한 임계값 스윕은 정확히 다중 검정 인플레이션이 가장 심하게 물어뜯는 상황입니다: 많은 후보, 하나의 히스토리, 그리고 최댓값을 보고하는 선택 규칙. 세상에서 가장 빠른 엔진은 당신의 테스트 윈도우의 노이즈에 아름답게 들어맞으면서도 아웃오브샘플에서는 실패하는 임계값 벡터를 기꺼이 건네줄 것입니다.
그러므로 규율은 속도에 맞춰 확장되어야 합니다. 시행 하나의 연산 비용이 0에 가까워지는 순간, 통계적 회계가 실제로 발목을 잡는 제약이 되며, 이를 명시적으로 치러야 합니다:
- 시행 횟수에 대해 디플레이트하십시오. 승자를 0이 아니라 당신이 시도한 설정의 개수에 대해 채점하십시오. 디플레이티드 샤프 비율과 백테스트 과적합 확률이 정확히 이를 위해 존재합니다 — 이들은 "우리는 4,000개의 임계값 벡터를 시도했다"는 사실을 보고된 엣지에 대한 헤어컷으로 바꿔줍니다.
- 폴드별로 아웃오브샘플에서 검증하십시오. 싼 스윕이라도 여전히 정직한 워크포워드 분할 안에서 실행되어야 합니다. 인샘플에서만 이기는 임계값은 아무리 빨리 찾아냈어도 무가치합니다. 우리 엔진이 체결을 누출 없이(
open[i+1]) 유지하는 것은 정확히 싼 축이 엿보기를 통해 성능을 사들이지 못하게 하기 위해서입니다. - 피크보다 플래토를 선호하십시오. 임계값 축이 촘촘하고 빠르기 때문에, 그 argmax뿐 아니라 반응 표면 전체를 지도로 그릴 수 있습니다. 모두 잘 작동하는 넓은 임계값 영역은 진짜 엣지입니다. 뾰족한 단일 최댓값은 피팅된 아티팩트입니다 — 플래토 대 피크 구분이며, 우리가 지금 설명하고 있는 바로 그 속도 덕분에 감당 가능해진 것입니다.
이축 구조를 올바르게 읽으십시오: 그것은 당신에게 더 큰 확신을 사주지 않습니다. 그것은 당신에게 시행당 동일한 확신 비용으로 더 많은 시행을 사줍니다. 이는 진정으로 가치 있습니다 — 싼 축을 촘촘하게 커버하는 것이야말로 플래토를 찾아내고 표면을 특성화할 수 있게 해주는 것입니다 — 다만 통계적 장부를 정직하게 유지하는 한에서만 그렇습니다. 속도는 철저하게 탐색하지 않을 연산상의 변명을 없애줍니다. 철저한 탐색이 찾아낸 것을 할인해야 한다는 의무까지 없애주지는 않습니다.
자신의 탐색을 어떻게 구조화할 것인가
이것을 자신의 전략에 적용하려면, 작업의 대부분은 분류입니다 — 파라미터를 올바른 축으로 정렬하는 것 — 그다음에 중첩 루프가 뒤따릅니다:
- 모든 파라미터를 그것이 무엇을 다시 계산하도록 강제하는지에 따라 라벨링하십시오. 그것을 바꾸면 지표/피처 배열이 바뀐다면, 비쌉니다. 고정된 배열에 적용되는 비교, 임계값, 사이징 규칙만 바꾼다면, 쌉니다. 확신이 서지 않으면 이렇게 물으십시오: 이 파라미터가 지표의 정의 안 어딘가에 등장하는가? 그렇지 않다면, 그것은 쌉니다.
- 비싼 축에 대해서만 캐시를 키잉하십시오. 피처 구축을 지표 파라미터로 메모이즈하십시오(
SignalCache가(period, hma_length)로 하는 것처럼). 그러면 지표 조합을 공유하는 인접한 시행들이 동일한 배열을 공짜로 재사용합니다. - 캐시된 피처에 대해 싼 축을 배치 처리하십시오. 임계값 설정을 완전한 재평가가 아니라, 미리 계산된 시그널에 대한 촘촘하게 컴파일된 루프로 실행하십시오. 당신의 처리량 — 우리의 경우 ~5,600 cfg/s — 은 여기서 나옵니다.
- 루프를 중첩시키십시오: 비싼 것을 바깥에, 싼 것을 안쪽에. 비싼 그리드는 작고 신중하게 유지하십시오(성기게, 또는 성긴 것에서 세밀한 것으로). 싼 스윕은 촘촘하게 두십시오. 예산은 파라미터가 실제로 비용을 발생시키는 곳에 쓰십시오.
- 시행 예산을 시계가 아니라 과적합에 맞춰 책정하십시오. 이제 시계가 제약이 아니게 되었으니, 디플레이티드 샤프 / PBO가 제약이 되게 하십시오. 싼 축 시행을 통계적으로 몇 번까지 감당할 수 있는지 결정하고, 승자를 아웃오브샘플에서 검증하십시오.
엔지니어링상의 보상과 통계적 안전장치는 하나의 아이디어의 양면입니다: 축을 분리하면 싼 차원을 철저하게 탐색할 수 있게 되며, 바로 그렇기 때문에 얼마나 철저하게 탐색했는지를 할인해야만 합니다.
핵심 요약
- 모든 차원이 같은 비용을 치르지는 않습니다. 전략의 파라미터는 비싼 축(지표 — 전체 시계열에 걸쳐 재계산)과 싼 축(임계값 — 미리 계산된 시그널에 대한 O(n) 패스)으로 나뉩니다. 우리 엔진에서는 트리플-TF의 경우 비싼 것 6개 + 싼 것 12개, 듀얼-TF의 경우 4개 + 8개입니다.
- 지표는 임계값에 대해 불변이며, 그 불변성이 최적화의 전부입니다. 피처 구축은 오직 지표 파라미터에만 의존하므로, 한 번 빌드해서
(period, hma_length)로 캐싱하고, 그 조합을 공유하는 모든 임계값 설정에 걸쳐 재사용합니다. - 싼 축은 약 1,600배 저렴하게 돌아갑니다. 캐시된 시그널에서는 초당 ~5,600개의 임계값 설정(각각 ~180 µs)인 반면, 매번 지표를 다시 빌드하면 설정당 ~0.3초입니다. 정확도는 동일합니다 — 유일한 차이는 불변량을 다시 계산하기를 거부하는 것뿐입니다.
- 차원의 저주는 사실 비싼 차원의 저주입니다. 파라미터 개수의 대부분은 스윕하는 데 거의 공짜인 축에 살고 있습니다. 비싼 그리드는 성기게 유지하고, 싼 그리드에는 넉넉하게 쓰십시오.
- 속도는 실제로 발목을 잡는 제약을 연산에서 통계로 옮깁니다. 촘촘하고 빠른 스윕은 다중 검정 기계입니다. 시행 횟수에 대해 디플레이트하고, 폴드별로 검증하고, 피크보다 플래토를 선호하십시오 — 공짜 점심은 진짜지만, 통계적 청구서는 선택 사항이 아닙니다.
전체 엔진 — SignalCache, sweep_separations, 누출 없는 멀티-TF 시뮬레이션, 그리고 이를 라이브의 진행 중인 캔들 의미론에 고정하는 패리티 테스트 — 은 우리 백테스터의 scripts/engine_multitf.py(커밋 bfc8aaa)에 있습니다. 다음에 누군가 18개 파라미터 전략은 탐색하기에 너무 크다고 말하거든, 그 파라미터들 중 실제로 지표를 움직이는 것이 몇 개인지 물어보십시오. 대개는 그중 3분의 1이며, 나머지는 거의 공짜입니다.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.