@@ -35,6 +35,9 @@ const validateIdea = (idea: any, line: number): string | null => {
3535} ;
3636
3737const toMarkdown = ( result : ISimulatorResult ) : string => {
38+ const profitable = result . reports . filter (
39+ ( { totalPnlPercent } ) => totalPnlPercent > 0 ,
40+ ) . length ;
3841 const lines : string [ ] = [ ] ;
3942 lines . push ( `# Simulator Report — ${ result . symbol } ` ) ;
4043 lines . push ( "" ) ;
@@ -43,30 +46,33 @@ const toMarkdown = (result: ISimulatorResult): string => {
4346 lines . push ( `| Ideas (total / directional) | ${ result . ideasTotal } / ${ result . ideasDirectional } |` ) ;
4447 lines . push ( `| Profiles (truncated) | ${ result . profileCount } (${ result . truncatedCount } ) |` ) ;
4548 lines . push ( `| Authors allowed / banned | ${ result . allowedAuthors . length } / ${ result . bannedAuthors . length } |` ) ;
46- lines . push ( `| Grid points | ${ result . reports . length } |` ) ;
49+ lines . push ( `| Profitable corridor | ${ profitable } / ${ result . reports . length } grid points |` ) ;
4750 lines . push ( `| Hold minutes avg / p95 / p99 | ${ result . avgHoldMinutes . toFixed ( 0 ) } / ${ result . p95HoldMinutes } / ${ result . p99HoldMinutes } |` ) ;
4851 lines . push ( "" ) ;
4952 lines . push ( `## Ranking winners` ) ;
5053 lines . push ( "" ) ;
51- lines . push ( `| Criterion | Stop% | Trailing% | Hold | Aligned | Track | HitRate | Trades | PNL% | WinRate | Sharpe | Sortino |` ) ;
52- lines . push ( `| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ` ) ;
54+ lines . push ( `| Criterion | Stop% | Hold | Track | HitRate | Trades | PNL% | WinRate | Sharpe | Sortino |` ) ;
55+ lines . push ( `| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |` ) ;
5356 for ( const best of result . best ) {
5457 if ( ! best . report ) {
55- lines . push ( `| ${ best . criterion } | — | — | — | — | — | — | — | — | — | — | — | ` ) ;
58+ lines . push ( `| ${ best . criterion } | — | — | — | — | — | — | — | — | — |` ) ;
5659 continue ;
5760 }
5861 const { point } = best . report ;
5962 lines . push (
60- `| ${ best . criterion } | ${ point . hardStopPercent } | ${ point . trailingTakePercent } | ${ point . holdMinutes / 60 } h | ${ point . minIdeasAligned } | ${ point . minAuthorTrack } | ${ point . minAuthorHitRate } | ` +
63+ `| ${ best . criterion } | ${ point . hardStopPercent } | ${ point . holdMinutes / 60 } h | ${ point . minAuthorTrack } | ${ point . minAuthorHitRate } | ` +
6164 `${ best . report . trades } | ${ best . report . totalPnlPercent . toFixed ( 2 ) } % | ${ ( best . report . winRate * 100 ) . toFixed ( 0 ) } % | ${ best . report . sharpe . toFixed ( 2 ) } | ${ best . report . sortino . toFixed ( 2 ) } |` ,
6265 ) ;
6366 }
6467 lines . push ( "" ) ;
65- lines . push ( `## Allowed authors (production whitelist)` ) ;
68+ lines . push ( `## Allowed authors (whitelist of the sharpe winner rule )` ) ;
6669 lines . push ( "" ) ;
6770 lines . push ( `| Author | Ideas | Hits | HitRate |` ) ;
6871 lines . push ( `| --- | --- | --- | --- |` ) ;
69- for ( const stat of result . authorStats . filter ( ( { banned } ) => ! banned ) ) {
72+ // артефакт авторов живёт по-победительно в best[] — ран-левел
73+ // authorStats в ISimulatorResult больше нет
74+ const sharpeBest = result . best . find ( ( { criterion } ) => criterion === "sharpe" ) ;
75+ for ( const stat of ( sharpeBest ?. authorStats ?? [ ] ) . filter ( ( { banned } ) => ! banned ) ) {
7076 lines . push ( `| ${ stat . author } | ${ stat . ideas } | ${ stat . hits } | ${ ( stat . hitRate * 100 ) . toFixed ( 0 ) } % |` ) ;
7177 }
7278 return lines . join ( "\n" ) ;
@@ -172,22 +178,31 @@ export const main = async () => {
172178
173179 const symbol = < string > values . symbol || "BTCUSDT" ;
174180
181+ // Проба осуществимости, НЕ поиск заработка (подбор параметров — у
182+ // --tune): собирающая прибыль механика выключена, остаётся вопрос
183+ // "есть ли прибыльный коридор" по стопу x холду x правилу бана —
184+ // 48 точек, быстрый вердикт
175185 addSimulatorSchema ( {
176186 simulatorName : SIMULATOR_NAME ,
177187 exchangeName,
178188 gridAxes : {
179- hardStopPercent : [ 1 , 1.5 , 2 , 2.5 , 3 , 4 , 5 , 7 ] ,
180- trailingTakePercent : [ 0.5 , 1 , 1.5 , 2 , 3 , 4 ] ,
189+ // грубая шкала катастрофы: коридор должен быть широким, не точкой
190+ hardStopPercent : [ 2 , 3 , 5 , 7 ] ,
191+ // инертен: проба не собирает прибыль, выход — по времени или стопу
192+ trailingTakePercent : [ 100 ] ,
181193 holdMinutes : [ 24 * 60 , 2 * 24 * 60 , 3 * 24 * 60 ] ,
182- minIdeasAligned : [ 1 , 2 , 3 ] ,
183- minAuthorTrack : [ 2 , 3 , 5 ] ,
194+ // одного проверенного автора достаточно — консенсус не перебираем
195+ minIdeasAligned : [ 1 ] ,
196+ // правило бана — единственная перебираемая "умность" пробы
197+ minAuthorTrack : [ 3 , 5 ] ,
184198 minAuthorHitRate : [ 0.5 , 0.6 ] ,
185- minWeightAligned : [ 0 , 0.6 , 1.2 ] ,
186- profitLockPercent : [ 0 , 1.5 , 2.5 ] ,
187- authorMetric : [ "close" , "reach" ] ,
199+ minAuthorWilson : [ 0 ] ,
200+ minWeightAligned : [ 0 ] ,
201+ profitLockPercent : [ 0 ] ,
202+ authorMetric : [ "close" ] ,
188203 banCriteria : [ "sharpe" , "pnl" ] ,
189- minAuthorWilson : [ 0 , 0.6 ] ,
190204 } ,
205+ reportOrder : "sharpe" ,
191206 callbacks : {
192207 onProgress : ( symbol , stage , processed , total ) => {
193208 if ( values . verbose ) {
0 commit comments