YouTube2Text

I Rebuilt Edward Thorp's $800M Pairs-Trading Strategy for 2025 (Full Python Walkthrough) — Transcript

by Algo-trading with Saleh · 7,176 words · 973 segments · language en · Watch on YouTube

Full transcript

  1. 0:00Let's talk about Paris rating again. It
  2. 0:01is the main strategy used by Edward Torp
  3. 0:04to make his 800 million fortune. Now, if
  4. 0:06you don't know who that is, he's the guy
  5. 0:08who invented card counting and proved
  6. 0:10that blackjack can be beaten
  7. 0:11mathematically. He also invented the
  8. 0:13first variable computer, but not to see
  9. 0:15time or anything like that, but to beat
  10. 0:17the roulette game. He tried it secretly
  11. 0:19in Las Vegas using his physicist buddy
  12. 0:22Claude Shannon. Now, if you don't know
  13. 0:23who that is, he's the father of
  14. 0:25information theory, and together they
  15. 0:27literally hacked the roulette game. He
  16. 0:28also invented quantitative trading with
  17. 0:30his most dominant strategy being pair
  18. 0:32trading or as he usually called it
  19. 0:34statistical arbitrage which is a market
  20. 0:36neutral strategy meaning that you're
  21. 0:38going to make money whether the price
  22. 0:39goes up down or whatever because at each
  23. 0:41point we're going to have one long and
  24. 0:43short position simultaneously so the
  25. 0:45trend of the market is not what we are
  26. 0:46looking for now if you want to know more
  27. 0:48about Edward Torp himself I suggest
  28. 0:50reading his book a man for all markets
  29. 0:52in this video I will go through the
  30. 0:54basics of persing very quickly so if you
  31. 0:56haven't watched my previous video about
  32. 0:57this topic Don't worry about it. But if
  33. 0:59you have, don't skip this video because
  34. 1:01in this one, I'm going to extend where I
  35. 1:02left the previous video and share with
  36. 1:04you the experience that I had running
  37. 1:06this strategy for a while. And before we
  38. 1:08continue, I got to say I am not a
  39. 1:09financial adviser and this video is
  40. 1:11merely a tutorial. All right, let's get
  41. 1:12to it.
  42. 1:14To show you the basics, I'm going to use
  43. 1:16a Jupyter notebook, which if you want to
  44. 1:18run it yourself, feel free to copy the
  45. 1:19code and run it inside a Jupyter
  46. 1:21notebook. But since we are using the
  47. 1:22Jesse framework in order to import the
  48. 1:24data and some other stuff, you need to
  49. 1:26ensure that this notebook is inside the
  50. 1:28root of your Jessie project. Now, if
  51. 1:30you're wondering what your project is in
  52. 1:31Jesse, well, it's the one that has the
  53. 1:33folders of strategies and storage. And
  54. 1:35here, as you can see, is my Jupyter
  55. 1:37notebook. Now in it I am simply
  56. 1:38importing the research module of Jesse
  57. 1:40and its utility functions which are
  58. 1:42really helpful and you're going to see
  59. 1:43it in a second and its helpers and the
  60. 1:46numpy library the daytime and the math
  61. 1:48plot lib so I can print out some
  62. 1:50beautiful charts for you guys and you
  63. 1:52need to ensure to choose the correct
  64. 1:54cond environment where Jesse itself is
  65. 1:56installed and for me this is the path to
  66. 1:58that one so now I'm able to run this now
  67. 2:01next I'm going to choose the binance
  68. 2:03perpetual futures as the exchange
  69. 2:05because in order to go long and short
  70. 2:07simultaneously. You cannot do that on a
  71. 2:09spot market, right? So, you need a
  72. 2:10perpetual futures market. As for the
  73. 2:12start date, I'm picking the first day of
  74. 2:152025 and the finish date is the first
  75. 2:17day of September. And then I'm using the
  76. 2:19research modules get candles function of
  77. 2:21Jesse to get the candles. Here's the
  78. 2:23exchange, the symbol, and the time
  79. 2:25frame. I'm using 50 minutes. This, by
  80. 2:27the way, could be anything. So, you just
  81. 2:29need to play around with it and find the
  82. 2:31correct time frame for your strategy.
  83. 2:33Obviously, the lower the time frame,
  84. 2:34it's going to take more trades. So you
  85. 2:36will be catching more opportunities, but
  86. 2:38as a result, you will be taking more
  87. 2:40trades and end up paying more trading
  88. 2:42fees. So that's the downside of choosing
  89. 2:44a lower time frame. By the way, the
  90. 2:46reason I'm using an underscore here is
  91. 2:48because I'm telling Python, hey, drop
  92. 2:50the first value that you're getting from
  93. 2:51this function here. And I'm only
  94. 2:53interested in the second one because the
  95. 2:55first one is the warm-up candles, which
  96. 2:56we don't really need it in this Jupyter
  97. 2:58notebook. But if you are executing some
  98. 3:01actual back test with it, you're going
  99. 3:02to need it. But anyways, just forget
  100. 3:04about it and use it as this because
  101. 3:06we're going to run the actual back test
  102. 3:07inside DJs framework itself and we don't
  103. 3:09need to know about these stuff and it
  104. 3:11will take care of them behind the scenes
  105. 3:12for us. All right, so let's run this.
  106. 3:15And as you can see, it's going to take a
  107. 3:16while and it's done. Next, I made an
  108. 3:19array of times in order to be able to
  109. 3:21show this on a chart. And then I'm using
  110. 3:23the utility function of Jesse called
  111. 3:25prices to returns to convert the prices
  112. 3:27into returns. And the reason I'm doing
  113. 3:29that is you see we're using Ethereum and
  114. 3:31Ethereum Classic for this example. And
  115. 3:32Ethereum itself has a price ranges of
  116. 3:35thousands of dollars, but Ethereum
  117. 3:36Classic has prices in hundreds of
  118. 3:38dollars. So in order to be able to do
  119. 3:40the calculations, we need them both to
  120. 3:42be inside the same range. And to do
  121. 3:44that, we're going to convert the prices
  122. 3:45into returns. And next, I'm simply
  123. 3:47creating a chart using math plot li. So
  124. 3:50let's see how this looks like. All
  125. 3:51right. So you see they both begin from
  126. 3:53the same point, which makes sense. And
  127. 3:55at some point Ethereum Classic which is
  128. 3:57this red line here by the way you can
  129. 3:58see in here is above the price of
  130. 4:01Ethereum itself and sometimes it's below
  131. 4:03this one. So in simple words what that
  132. 4:05means is that if I were to go long
  133. 4:08Ethereum Classic here and short Ethereum
  134. 4:10itself and then at this point if I were
  135. 4:12to close that position I would have made
  136. 4:14money and I could have done the
  137. 4:16opposite. So here I could have gone
  138. 4:17short on Ethereum Classic and long on
  139. 4:19Ethereum itself and then at another
  140. 4:21point such as here I could have closed
  141. 4:22that position. And there's been so many
  142. 4:24other opportunities in here, right? So
  143. 4:26you see that's what I meant when I said
  144. 4:28we don't really care about the trend
  145. 4:29like it doesn't matter if where the
  146. 4:31actual price is going because in reality
  147. 4:33yes both Ethereum and Ethereum classic
  148. 4:35move together they are correlated right
  149. 4:37so they either move up together or down
  150. 4:39together but not exactly right so it's
  151. 4:41not right to the point and those are the
  152. 4:43times that we want to use as our edge in
  153. 4:46order to make money so the question is
  154. 4:48how do we exactly measure this so this
  155. 4:50difference that I'm seeing on the chart
  156. 4:51and I explained it right now how do we
  157. 4:53measure it with actual numbers so we can
  158. 4:55write the code to do it automatically
  159. 4:57for us. Now, to do that, we're going to
  160. 4:58need something called zcore, which is
  161. 5:00not really that complicated. So, it
  162. 5:01basically needs how much further the
  163. 5:03price is moving away from the mean of
  164. 5:05itself. Now, first we calculate the
  165. 5:07spread, which is the difference between
  166. 5:08the two price ranges and then we
  167. 5:10calculate the zcore. And Jesse already
  168. 5:12gives us a utility function for that.
  169. 5:13But if you are interested about the
  170. 5:15formula of that, so here it is. So, the
  171. 5:17price ranges minus the mean of that
  172. 5:19series divided by a standard deviation
  173. 5:21of the same series. So, it's nothing
  174. 5:22fancy, but very useful. Then we get the
  175. 5:24mean using numpai's mean function. And
  176. 5:27now if we plot it again. So you see the
  177. 5:29blue line here is the zcore. And this
  178. 5:31red and green lines are simply two
  179. 5:34thresholds that we are defining. So I
  180. 5:36have used the numbers 1.2. You could
  181. 5:38choose whatever you want. But the lower
  182. 5:40the threshold number, the more trades
  183. 5:42you're going to take. All right. So this
  184. 5:43is very simple. Basically we want to say
  185. 5:45that whenever the zore goes above this
  186. 5:48red threshold, we want to go short the
  187. 5:50first asset and long the other one. And
  188. 5:51then we close it whenever it reaches the
  189. 5:53mean which is this black line here. And
  190. 5:55whenever it goes below the minus 1.2
  191. 5:58threshold line we want to go along the
  192. 5:59first asset and short the other one. Now
  193. 6:01we can also check statistically to
  194. 6:03ensure the two assets are in fact
  195. 6:06because we don't just want them to be
  196. 6:07correlated which is they are moving
  197. 6:09together. We also want to make sure they
  198. 6:10are conjugated. Now I'm super
  199. 6:12simplifying these terms. So if you want
  200. 6:13to know about them make sure to look
  201. 6:15them up. But since Jess is providing
  202. 6:16these functions for us it's not going to
  203. 6:18be that hard to calculate it. And if I
  204. 6:19run this, you can see the result which
  205. 6:22is it's saying yes, it is going
  206. 6:23integrated. So these two assets are fine
  207. 6:25to trade. But maybe the assets that you
  208. 6:27want to trade are not so obvious like
  209. 6:28Ethereum and Ethereum class. Maybe you
  210. 6:30want to run it on two assets such as the
  211. 6:32token of two different exchanges such as
  212. 6:34DYDX and let's say the hype token.
  213. 6:36Before running it, you want to ensure
  214. 6:38they are indeed integrated. So for that
  215. 6:40you can use this utility function. All
  216. 6:42right. So there's one issue. If I were
  217. 6:44to write the strategy and run it with
  218. 6:46these exact numbers, I would have fall
  219. 6:48into the look bias. What that means is
  220. 6:50that because we already have all the
  221. 6:52data and we are calculating the mean for
  222. 6:54the entire data and then we are choosing
  223. 6:56these threshold lines such as 1.2, we
  224. 6:59are using data from the future. So if
  225. 7:01you execute the strategy with this,
  226. 7:03we're going to see good numbers. But
  227. 7:04when you actually run the same strategy,
  228. 7:06it is not going to perform as well. So
  229. 7:08to fix that, we need to ensure that we
  230. 7:10only use the data that we already have.
  231. 7:11But one of the good things about JC
  232. 7:13framework is that when you use the data
  233. 7:14inside the strategy, you will only have
  234. 7:16access to the data so far. That means
  235. 7:18you will never have access to the future
  236. 7:20data. So you don't really need to worry
  237. 7:21about this. But I just needed to mention
  238. 7:23this because I know some of you who know
  239. 7:25the math better than me are now thinking
  240. 7:27oh what this guy just did is the look at
  241. 7:29bias. Now here's a previous video that I
  242. 7:30made about this topic and in the
  243. 7:32description section of the video I was
  244. 7:34linking to this GitHub repository. Now
  245. 7:36in it we simply have two strategies
  246. 7:37press rating and press trading two. So
  247. 7:39press rating two is actually simpler. So
  248. 7:41let me show you that first. All right.
  249. 7:43So you see this strategy is simply
  250. 7:45following whatever logic the first
  251. 7:46strategy is giving. So it's reading the
  252. 7:48values that have been set by the first
  253. 7:50strategies using the self.shart vs
  254. 7:53dictionary of Jesse to know whichever
  255. 7:55position should go long or short or
  256. 7:58whether or not it is time for it to
  257. 7:59close or liquidate the current position.
  258. 8:01So it's pretty simple and in fact we
  259. 8:03don't need to change this code like at
  260. 8:05all. So instead let's go to the first
  261. 8:07strategy and check out the source code.
  262. 8:08So let's make this a little bigger. All
  263. 8:10right. So first we have the typical
  264. 8:12imports of just a strategy. So we're
  265. 8:13importing the main strategy class, the
  266. 8:16indicators package and the utility
  267. 8:18functions and we simply define the pair
  268. 8:20rating class and it is inheriting from
  269. 8:22the main strategy class. So this is 101
  270. 8:24strategy in Jesse and in fact when you
  271. 8:26generate a strategy using the dashboard
  272. 8:28it will generate these things for you.
  273. 8:29Now since we were doing our calculations
  274. 8:31using the price returns and not even
  275. 8:33price data let alone to something like
  276. 8:35candle 6. That is why in here we also
  277. 8:38need to convert the candle data into
  278. 8:40price returns. So we're using the
  279. 8:42utility function of Jesse called prices
  280. 8:44returns. And in it we're using the get
  281. 8:46candles function of Jesse to fetch the
  282. 8:48current data for our trading time frame
  283. 8:51symbol and exchange. And we're selecting
  284. 8:52only the closing data. And then we're
  285. 8:54picking the last 200 candles. And this
  286. 8:57is expected right because if we want to
  287. 8:58do some calculation based on the past
  288. 9:00existing data we need some kind of look
  289. 9:03back period, right? And in this strategy
  290. 9:05I chose the number 200. You can test
  291. 9:07your own number to see whichever works
  292. 9:08best for you. And next, we did the same
  293. 9:10thing except using the other symbol. So,
  294. 9:12by the way, I could have hardcoded this.
  295. 9:14So, for example, this could have been
  296. 9:16ETH and this one could have been etc.,
  297. 9:18but I use the self.oute array of JC,
  298. 9:21which will give me basically a list of
  299. 9:22the routes that I'm trading. And because
  300. 9:24in this case, I'm supposedly trading two
  301. 9:26symbols. So, let's say ETH and ETC, then
  302. 9:29I know that the first one is going to be
  303. 9:30ETH and the second is going to be ETC.
  304. 9:32Next, we calculate the Zcore very
  305. 9:34simply. So we get the spread by
  306. 9:36subtracting C1 minus C2 and I'm
  307. 9:39selecting since index of one until the
  308. 9:41end because the first value is going to
  309. 9:43be a non value which is going to mess
  310. 9:45our calculation. So we need to escape
  311. 9:46that one and then we use the Zcore which
  312. 9:48simply accepts the spread and then I'm
  313. 9:51returning the last value of the Zcore
  314. 9:53because that's really the one that I
  315. 9:54care for. So you remember we were
  316. 9:56calculating Zcore in the Jupyter
  317. 9:58notebook as well and we were plotting it
  318. 9:59here, right? So here's the thing. The
  319. 10:01only value that we actually care for is
  320. 10:03the last one because if that one for
  321. 10:05example is below this threshold, we want
  322. 10:07to go long. If it's here, we want to go
  323. 10:08short. Right? So we only care about the
  324. 10:10last value. And that's why in here I'm
  325. 10:12also selecting only the last value. Now
  326. 10:14here's where actually the Jesse
  327. 10:16framework comes in. So we're using the
  328. 10:18before method which is a built-in method
  329. 10:20of Jesse. What it does is that it will
  330. 10:22get executed before every single candle
  331. 10:24closes. What it does is that right after
  332. 10:26a candle closes when you're trading this
  333. 10:29function is the first method that will
  334. 10:31get executed which is a great place to
  335. 10:33put some extra calculations that you
  336. 10:35want to do so that later you're going to
  337. 10:37use to decide whether or not you want to
  338. 10:39open a position or close an existing
  339. 10:40one. Now I'm saying that if it's the
  340. 10:42first index, so if I have just started
  341. 10:44my session, we need to fill in these
  342. 10:46values, right? So S1 position and S2
  343. 10:48position. So what these two variables
  344. 10:50are going to do for me is to decide at
  345. 10:52each point whether the position should
  346. 10:53be long, short or closed. Now zero is
  347. 10:56pointing to closed and once every 24
  348. 10:58hours and this is how I'm calculating
  349. 11:00that, right? So I'm saying if the index
  350. 11:02is zero or if the remaining of the
  351. 11:04division by this equals to zero, I want
  352. 11:07to check to ensure that the symbols that
  353. 11:10I'm trading are still integrated.
  354. 11:12Practically I never use this, but if
  355. 11:14you're doing some research and you're
  356. 11:15not even sure if your symbols are
  357. 11:17integrated in the first place or not, I
  358. 11:19think it's best to include this part of
  359. 11:20the code, but otherwise like you can
  360. 11:22simply just delete these and nothing
  361. 11:25will really happen. But anyway, so we're
  362. 11:27using the Rte integrated utility
  363. 11:28function of JC. So we're not even
  364. 11:30calculating this by ourselves and we're
  365. 11:32saying if they are not integrated then
  366. 11:34simply put the positions to zero which
  367. 11:36which in other words it means do not
  368. 11:38open any long or short trade. Next we
  369. 11:40were getting the zscore and we were
  370. 11:42saying that if the current position is
  371. 11:44closed and the zcore is below minus 1.2
  372. 11:47which is this threshold that we had here
  373. 11:48the first position must be long the
  374. 11:50second one should be short and it should
  375. 11:52also set the position sizing. Now here's
  376. 11:54a code for that. So we're using the
  377. 11:56calculate alpha beta utility function of
  378. 11:58Jesse. We don't really care about the
  379. 11:59alpha value, but we do need the beta.
  380. 12:02And we're saying that using the
  381. 12:03available margin multiplied by these, we
  382. 12:05are calculating the margin one, which is
  383. 12:07how much margin we should be spending on
  384. 12:09the first position and how much we
  385. 12:11should be spending on the second
  386. 12:12position. So, I did some research and
  387. 12:13came up with this formula, but I've seen
  388. 12:15people using different formulas and I
  389. 12:17think this is also one of the parts that
  390. 12:19needs some extra research on our side
  391. 12:21because the size of the position has a
  392. 12:23huge impact on the results that you're
  393. 12:24going to get. And next we're saying that
  394. 12:26if the current position is a long
  395. 12:27position. Now by the way when we're
  396. 12:28saying the current position now we are
  397. 12:30pointing to ETH in the example that I
  398. 12:33gave you before. So we're saying that if
  399. 12:34it's a long position and the zcore is
  400. 12:36above zero which in this case means that
  401. 12:38you know here we went long but now this
  402. 12:41line is above the mean which is this
  403. 12:43black line or zero in other words then I
  404. 12:45want to close both positions. And when
  405. 12:47we set this to zero not just this
  406. 12:49strategy but every other strategy which
  407. 12:51in this case is just another one it will
  408. 12:53also pick up on this. So when it calls
  409. 12:55the object position function of Jesse,
  410. 12:57it will see okay, this value has been
  411. 12:59set to zero and now I need to liquidate
  412. 13:01the current position. And by the way,
  413. 13:02this function only gets executed when
  414. 13:04you do have an open position. That's why
  415. 13:07in here we don't need to check to see if
  416. 13:09we have a open position or not. So you
  417. 13:11see it's little things like this that
  418. 13:13makes framework really easy to work
  419. 13:15with. All right. So next we're saying
  420. 13:16that if it's a short position and the ZS
  421. 13:18score is below zero, we also want to do
  422. 13:20the same thing. And if it's closed and
  423. 13:21the ZS score is above 1.2, to we want to
  424. 13:24go short with the current position and
  425. 13:26we want to go long with the other
  426. 13:28strategy and again we want to set the
  427. 13:29margin values as well and that's it
  428. 13:31really like everything else is super
  429. 13:33easy so next inside the shoot long
  430. 13:34method which is how we tell JC like
  431. 13:36whether we should open a long position
  432. 13:38at this moment or not we're simply
  433. 13:40checking this value which we set in here
  434. 13:43right so if it's one we want to go long
  435. 13:45if it's minus one we want to go short
  436. 13:48and if it is going long well here's
  437. 13:50where we do the actual position sizing
  438. 13:52So we're getting the quantity using the
  439. 13:54size to quantity utility function of
  440. 13:56Jesse. But in it, we're not passing the
  441. 13:58current balance. We're passing this
  442. 13:59value which we set inside the set proper
  443. 14:02margin per route function that we
  444. 14:03defined earlier. And then we're passing
  445. 14:05the current price and the fee price of
  446. 14:07the current exchange. And then we're
  447. 14:08submitting the actual buy order by
  448. 14:10saying self.by equals quantity and then
  449. 14:13self.pric. We're doing the opposite for
  450. 14:15short. And again inside the update
  451. 14:16position method, we're saying that if
  452. 14:18the S1 position equals to zero, I want
  453. 14:20to liquidate the current position. So
  454. 14:22you see it was super simple. If you
  455. 14:24remember the previous video, this was
  456. 14:25the result that we were getting before
  457. 14:27and it looked fantastic. But the problem
  458. 14:29was that we weren't paying any trading
  459. 14:31fees more than 7800 trades. But the
  460. 14:34numbers were great. Like the max was
  461. 14:35only minus 5% which is absolutely
  462. 14:38amazing. Here's the benchmark comparing
  463. 14:40the equity curve of our strategy to the
  464. 14:43other trading assets that we were
  465. 14:44trading which was ETH and ETC. But then
  466. 14:47after including the trading fees, this
  467. 14:49was the result that we were getting,
  468. 14:51which is absolutely horrible. And that
  469. 14:53was with this much trading fees, which
  470. 14:54is even a little bit higher than the
  471. 14:56trading fees that you're going to pay on
  472. 14:58an exchange such as Binance. So, we were
  473. 15:00including the trading fees and a little
  474. 15:01bit of a slippage. And the result was
  475. 15:03this, which is horrible. But like I said
  476. 15:05in the intro of the video, in this one,
  477. 15:07I'm going to extend where I left the
  478. 15:08previous video, and I will show you a
  479. 15:10couple of tricks that I picked up, which
  480. 15:12really improved the result. All right.
  481. 15:13So, let's go to Jess's dashboard and
  482. 15:15create a new strategy. And I'm going to
  483. 15:17call this one first trading 2025. Next,
  484. 15:21we can continue editing this from within
  485. 15:23the builtin editor of the dashboard. Or
  486. 15:26we could copy this, go to another editor
  487. 15:28such as cursor or VS Code, and look up
  488. 15:31the name of that strategy and find it
  489. 15:33here. Now, I find this one more
  490. 15:34convenient. So, this is the one that I'm
  491. 15:36going to go with. So, let's go and copy
  492. 15:38the code that we had before. Paste it
  493. 15:41here. Uh firstly, let's update the name
  494. 15:44to include 225 in it. And now I'm going
  495. 15:48to keep building upon this. So let's
  496. 15:51copy the name of it. Go to Jess's
  497. 15:53dashboard. Go to the backlisting page.
  498. 15:54Look up the name of it. I want to trade
  499. 15:56this on the 15 minutes time frame. So
  500. 15:58let's change this to 15 minutes. And I'm
  501. 16:00going to use BTC and soul trading pairs.
  502. 16:03Now for the second strategy, I'm using
  503. 16:05this one, but it is basically the exact
  504. 16:07same thing that we had as before, except
  505. 16:10that I just made some updates to it to
  506. 16:12make things more dynamic. So for
  507. 16:14example, instead of here saying margin
  508. 16:162, I'm saying margin and then I'm using
  509. 16:19this self.curren route index. And I'm
  510. 16:22doing the same thing in here. And by
  511. 16:23doing this, I'm ensuring that if I, for
  512. 16:25example, were to trade not two symbols,
  513. 16:27but four or six, I wouldn't have to
  514. 16:29change this strategy to update the
  515. 16:31number, right? So from 2 to four or six.
  516. 16:34So I don't have to do that anymore. So
  517. 16:35that's why I did this. But you could use
  518. 16:37the previous code or you could use this
  519. 16:39one. And I will share whatever that I'm
  520. 16:42writing today on GitHub and link to it
  521. 16:44in the description of the video. All
  522. 16:45right. So we picked this one and here it
  523. 16:47is. For the duration, I'm choosing the
  524. 16:49first 6 months of 2025. And you need to
  525. 16:52ensure that fast mode is disabled
  526. 16:53because when it is enabled, you cannot
  527. 16:55trade the strategies that use two
  528. 16:58different trading routes. So, if I
  529. 17:00enable this and try to run it, it will
  530. 17:02give me this error. So, I need to
  531. 17:04disable this. And I'm leaving the
  532. 17:06benchmark. You know what? Let's also
  533. 17:07disable this. Let's also check the
  534. 17:09trading fees. And let's set it to zero
  535. 17:11first. Now, let's run this. And here's
  536. 17:13the result that we're getting. So, at
  537. 17:15this point, it lost a little bit of
  538. 17:17money, but from here to here, it was
  539. 17:19making money. And this is how many
  540. 17:20trades we made. And the maxon is - 41%.
  541. 17:24So, it's not great even with zero
  542. 17:26trading fees, right? But that's kind of
  543. 17:28expected because in the previous video I
  544. 17:29was using ETH and ETC which are
  545. 17:32significantly correlated and coins
  546. 17:34integrate together. But here I'm using
  547. 17:36BTC and Soul which aren't always going
  548. 17:39toe to toe, right? But because I want to
  549. 17:40show you the realistic values, the ones
  550. 17:42that I actually traded. So let's also
  551. 17:44bring back the trading fees. So on
  552. 17:45Binance, this is the actual trading fee
  553. 17:48that you're going to have to pay for
  554. 17:50taker or a market order in other words,
  555. 17:52which is what this strategy is using.
  556. 17:54But we're going to set it to a little
  557. 17:55bit higher to consider the slippage
  558. 17:58value. Now let's run it one more time
  559. 18:00but this time in a new tab because I
  560. 18:02want to compare the two values. Now
  561. 18:04here's what we're getting now. So with
  562. 18:06the same number of trades now we're
  563. 18:09losing 95% of the capital that we were
  564. 18:11trading. The max is - 95%. So it's
  565. 18:14absolutely horrible and untradeable.
  566. 18:16Right now this is before and this is
  567. 18:18after. So now let's extend where we left
  568. 18:20off in the previous video. All right. So
  569. 18:22the first thing that I'm going to do is
  570. 18:24to define a new property and I'm going
  571. 18:26to call it a spread
  572. 18:29and just return this right. So I don't
  573. 18:32want to repeat myself doing this. Now
  574. 18:34the next thing that I did that worked
  575. 18:35really well was instead of using the
  576. 18:37Zcore and some simple hard-coded
  577. 18:40threshold I started using Ballinger
  578. 18:42bands. So that means we can simply
  579. 18:44remove this and I'm going to define this
  580. 18:46as BB and in it we are using TA.
  581. 18:50Ballinger bands which is the correct
  582. 18:52syntax you just see for using the
  583. 18:54Ballinger bands indicator and instead of
  584. 18:55passing some candles I'm passing the
  585. 18:58current spread values which we defined
  586. 19:00here and as period I'm using 20 and we
  587. 19:04don't even need to define these. So
  588. 19:06let's just remove it and we're going to
  589. 19:07go with the defaults. So that means now
  590. 19:09I can go to the before function again
  591. 19:12and here I can remove Zcore and replace
  592. 19:14it with Ballinger bands. And instead of
  593. 19:16saying when Zcore is below minus 1.2
  594. 19:19two, we can say whenever the current
  595. 19:21spread is below the lower band of the
  596. 19:24Ballinger bands. So we can say this. So
  597. 19:26the current spread which we haven't
  598. 19:29defined yet is below the lower band. So
  599. 19:31let's also define this. So let's go up
  600. 19:33here and simply define that. Actually
  601. 19:36cursor is smart enough to help us with
  602. 19:37this. So yeah, the current is spread but
  603. 19:39we're only interested in the last value
  604. 19:40of it. So we are returning the last
  605. 19:42value of this. All right. So let's go
  606. 19:44back and here we can remove this and say
  607. 19:47whenever the current spread is above the
  608. 19:50middle band and we also do the same
  609. 19:52thing here. So whenever the current
  610. 19:53spread is below the middle band we want
  611. 19:55to close the position and here we're
  612. 19:58saying whenever the current spread is
  613. 19:59above the upper band of the Ballinger
  614. 20:01bands we want to go short. So if we were
  615. 20:04looking at it in here so imagine if this
  616. 20:06line here was the lower band and this
  617. 20:08one was the upper band of the Ballinger
  618. 20:09bands right? So we're saying whenever
  619. 20:11the current spread which is this blue
  620. 20:13line is above the upper band you want to
  621. 20:15go short. Whenever it is below it you
  622. 20:17want to go long and whenever it passes
  623. 20:19the middle band which is supposedly this
  624. 20:22black line here you want to close the
  625. 20:23position. Now again guys this was the Z
  626. 20:25score. Okay it wasn't exactly the
  627. 20:27Ballinger bands but the way it works is
  628. 20:29very similar to Ballinger bands which
  629. 20:30I'm sure all you guys already know and I
  630. 20:33got so much better results with this. So
  631. 20:36again, we defined the boundary bands
  632. 20:37here and we just use it in these places.
  633. 20:41So let's go back and rerun this one more
  634. 20:44time, but I'm not expecting to get like
  635. 20:46super good results yet because the main
  636. 20:49ingredient is still left. All right. So
  637. 20:52even as it is, we're getting better
  638. 20:53results. You see? So instead of losing,
  639. 20:56you know, that much, we're losing less.
  640. 20:58And we are also taking less trades, by
  641. 21:00the way. So that's also another reason
  642. 21:02why we're losing less. But overall, I
  643. 21:04found this model to work better. All
  644. 21:06right, the next thing that I did that
  645. 21:07worked really well was using the ADX
  646. 21:09indicator. Now, in case you don't know
  647. 21:11what ADX is, so let's display it on
  648. 21:13trading view. So, here is this average
  649. 21:15directional index. All right, so what it
  650. 21:17does is that it tells us how much
  651. 21:19volatility there is in the market. So,
  652. 21:21for example, at this point where we were
  653. 21:22in a freef fall, you see the value was
  654. 21:24high and in here, for example, where
  655. 21:27there wasn't really any kind of trend,
  656. 21:29the value of it was really low. So
  657. 21:30setting a simple threshold with the ADX
  658. 21:32indicator helps us a lot to find the
  659. 21:35moments that the market has a lot of
  660. 21:36volatility and there's a higher chance
  661. 21:38of insufficiency in the market and
  662. 21:40that's what we want to do right we want
  663. 21:41to find the insufficient moments in the
  664. 21:43market and take trades and take
  665. 21:45advantage of those moments. So I simply
  666. 21:48set a threshold of 30. So on trading
  667. 21:50view that would be something like this.
  668. 21:52And I'm saying whenever the value of ADX
  669. 21:55is below the threshold like in here, I'm
  670. 21:57not taking any trades. But if it's above
  671. 21:59it like in here or here or here, then
  672. 22:02yes, I am looking to take trades. So
  673. 22:05let's define it here. I'm going to
  674. 22:07define new property and I will call it
  675. 22:09ADX.
  676. 22:12And simply in it, I will say return TA.X
  677. 22:15X and then I'm passing well this is
  678. 22:17wrong I want to pass the current trading
  679. 22:19candles which I can get like this in
  680. 22:21Jesse and we can also remove the period
  681. 22:24parameter I will go with the default now
  682. 22:26I want to say when the value of it is
  683. 22:28above 30 that's when I want to take the
  684. 22:30trade so now let's go back here and I
  685. 22:33will also add this
  686. 22:36but we don't need it for closing the
  687. 22:38trade so not here not here but also in
  688. 22:40here I will add this the next thing that
  689. 22:42I did was simply defining a trend filter
  690. 22:45So let's go ahead and do it. So new
  691. 22:48property I'm going to call it trend. And
  692. 22:50in it I'm going to use the comma
  693. 22:53indicator. Feel free to use whichever
  694. 22:54you like. You can use moving averages or
  695. 22:56whatever. But I found comma to be very
  696. 22:59simple and work best for my use case. So
  697. 23:01I'm going to say k equals ka and then
  698. 23:04I'm passing the current trading candles.
  699. 23:07And here I'm saying if the current price
  700. 23:09is above the k value, it's an uptrend
  701. 23:12otherwise it's a downtrend. And then in
  702. 23:14my trading rules, I'm saying that for
  703. 23:16this one, I want it to be in an uptrend
  704. 23:19because I want to go long the first
  705. 23:20position. And in here, I will do the
  706. 23:24opposite. I will say that if it's a
  707. 23:26downtrend, in other words, if the trend
  708. 23:28equals minus one, I want to go short the
  709. 23:31first position and long the second
  710. 23:33position. So, let's go back and run this
  711. 23:35two. You see, it's again improving our
  712. 23:38results a little bit, but we're still
  713. 23:40executing 286 trades and the draw down
  714. 23:44isn't great either. All right, so let's
  715. 23:45go back and define another filter. Now,
  716. 23:49this one is the most important filter
  717. 23:51that I defined. The main problem with
  718. 23:53the previous results that we were
  719. 23:54getting was that we were executing too
  720. 23:57many trades. So, even the trades that
  721. 23:58weren't really worth it, just because
  722. 24:00the value of of the ZS score was below
  723. 24:03threshold, we were taking a trade,
  724. 24:04right? we weren't checking to see if
  725. 24:06piano wise the trade was actually worth
  726. 24:08it. So that's what we're going to do
  727. 24:09now. So let's define it and I'm going to
  728. 24:11call it is worth opening and in it I
  729. 24:13will say return the abstract value of
  730. 24:17the current spread
  731. 24:20and you know its last value to be above
  732. 24:23a certain number and I'm going to use
  733. 24:261.5%.
  734. 24:27Now depending on whichever coin you're
  735. 24:29trading you're going to need to change
  736. 24:31this. So for example, if you're trading
  737. 24:32meme coins, the volatility is going to
  738. 24:34be higher. So maybe you want to catch
  739. 24:36bigger movements, right? So 1.5% may not
  740. 24:40be big enough. So for a memecoin, maybe
  741. 24:41a number such as 4% would be good. But
  742. 24:44for more liquid coins such as BTC or
  743. 24:46soul, I'm going with 1.5. And the
  744. 24:48problem with choosing something below
  745. 24:50that is that the trading fees of the
  746. 24:52exchange is going to kill our results
  747. 24:54and that's just not good enough for me.
  748. 24:56All right, so let's go here and add this
  749. 24:58as another inter rule. And I also do the
  750. 25:01same thing in here. Again, we don't need
  751. 25:03it in here. Now, let's go back and
  752. 25:05remember this number. Right. So,
  753. 25:08actually, let's open a new tab because
  754. 25:10again, I want to compare the results of
  755. 25:12all of them. And there we go. So, it
  756. 25:15drastically improved. Right. So, now
  757. 25:17we're executing only 28 trades, the ones
  758. 25:19that are actually worth it.
  759. 25:22And the win rate is 28%. That's not
  760. 25:24awesome. The max is - 10%. And if we go
  761. 25:28to the benchmarking page, we can compare
  762. 25:30the result here as well. So you see
  763. 25:32they're all negative still. But we went
  764. 25:35from minus12 sharp ratio to minus5 to
  765. 25:38minus2. Now this one was without trading
  766. 25:41fees. So we don't really need to keep
  767. 25:43this open at all. Let's close it. Now
  768. 25:45here's the thing. Just like we are
  769. 25:46saying is the current trend worth for
  770. 25:49opening in the first place. We can also
  771. 25:51say like is it worth to close it?
  772. 25:53Because why should we always wait until
  773. 25:55the value goes above the middle band?
  774. 25:58What if we're not making enough P&L yet?
  775. 26:00So just like that, we can also define
  776. 26:02another one. And with this one, we're
  777. 26:04going to call it is worth closing. Now
  778. 26:06for this, we're going to have to get the
  779. 26:09current P&L of the current position. And
  780. 26:11here's how we get that. So P1 equals the
  781. 26:13current route, the first one, and then
  782. 26:16we're selecting the strategy just so we
  783. 26:18can access its position. And then it's
  784. 26:19P&L. And we're doing the same thing for
  785. 26:21the second position. So by the way, this
  786. 26:23is how you can access the value of the
  787. 26:25other trading routes that you have from
  788. 26:27within one single strategy in Jesse,
  789. 26:29right? So you will use the current
  790. 26:32routes array and then you have access to
  791. 26:34all of them. So you pick the strategy
  792. 26:36and then it position and then it's P&L.
  793. 26:38So then we're saying the total P&L
  794. 26:39equals the P1 added by P2. So remember,
  795. 26:43one of these is probably going to be
  796. 26:44negative, but when we add them together,
  797. 26:46that is going to give us like the
  798. 26:48remaining value. And then we're saying
  799. 26:49the total P&L divided by the current
  800. 26:52position's value multiplied by 100. So
  801. 26:55we can have it in percentage should be
  802. 26:57above a certain value. And because we
  803. 26:58chose 1.5% here, we might as well do the
  804. 27:02same thing in here. So I'm going to
  805. 27:04change this to 1.5.
  806. 27:06So we're saying that we only want to
  807. 27:08close the current position if we are in
  808. 27:10a profit for more than 1.5%. But I don't
  809. 27:14want to always do this because what if
  810. 27:16we are losing money, right? So what if
  811. 27:18the price is going very much against us
  812. 27:20and we're losing more and more. We don't
  813. 27:22want to keep holding the position,
  814. 27:23right? So for that reason I will also
  815. 27:25define another one and I'm going to call
  816. 27:28it time to stop. So this is how we
  817. 27:29define the stop loss here because in a
  818. 27:31typical strategy we submit a stop loss
  819. 27:34as soon as we open the position. But
  820. 27:35because this is a pair trading strategy,
  821. 27:37we don't look at just the panel of the
  822. 27:40current position. We need to add them
  823. 27:42together like what we did here, right?
  824. 27:43So at any point in time, we need both
  825. 27:46values. So that's why it's going to be a
  826. 27:48little bit tricky and we cannot use this
  827. 27:50typical stop-loss order. So instead we
  828. 27:52need to check at each point to see if
  829. 27:55the current losses is bigger than a
  830. 27:57certain percentage we're going to close
  831. 27:58the current position. We're going to
  832. 28:00liquidate it. Right? So to do that we
  833. 28:02will define it like this. Very similar
  834. 28:03to what we just did and we're saying if
  835. 28:06the current total P&L is below a certain
  836. 28:09number and I'm choosing minus 5% here.
  837. 28:12Again feel free to choose whatever
  838. 28:14number that works best for your use
  839. 28:16case. If that's the case, I want to
  840. 28:18close the position. So either close the
  841. 28:20position when it is time to stop because
  842. 28:22we are in so much loss or when we are in
  843. 28:25a good number of profit again. You need
  844. 28:27to play around with this value and for
  845. 28:29this pair I found this number to be good
  846. 28:31enough. So let's go back to our before
  847. 28:33function. Now this one is for closing.
  848. 28:36So we no longer update this part. So
  849. 28:38instead I will do this. So I'm saying
  850. 28:40and either when it is worth closing or
  851. 28:44when time it is time to stop. So
  852. 28:45remember, if you put and instead of or
  853. 28:47here, it's going to ruin everything,
  854. 28:48right? So we did it like this. And
  855. 28:51again, we need to do it one more time.
  856. 28:53And if I go back and run this one more
  857. 28:55time, hopefully it should improve our
  858. 28:57results.
  859. 28:59And there we go. So you see now instead
  860. 29:01of losing money, we are earning a really
  861. 29:04good number. So 18%
  862. 29:07in 6 months, max of only minus 2%, the
  863. 29:11win rate is 87%. So these are really
  864. 29:14good. The sharp ratio is 2.81. And again
  865. 29:17guys, remember this is while we are
  866. 29:20indeed paying for trading fees. So you
  867. 29:22see we started from this. So this is
  868. 29:24what we had in the previous video. And
  869. 29:26in this one we have this. So these
  870. 29:29aren't even comparable, right? By the
  871. 29:31way, I did cherry pick the period of the
  872. 29:33back test. So let's go and go a little
  873. 29:36bit further. So let's choose 2024. And
  874. 29:39here let's move this up to November,
  875. 29:42which we are in right now. and let's see
  876. 29:45what will be the results then. All
  877. 29:47right, so in 2025 it actually continued
  878. 29:49to perform really well but in 2024 it
  879. 29:53wasn't so great especially in this
  880. 29:55period. All right, so here's the thing.
  881. 29:56I ran the strategy in 2025 myself and it
  882. 30:00did perform well and as you can see in
  883. 30:02the back test here's the reason because
  884. 30:04even in the back test it was doing well
  885. 30:06but at the end of 2024 it wasn't doing
  886. 30:09well. So, I was a little bit lucky. But
  887. 30:12here's the thing. I made the changes
  888. 30:14that I did based on what made sense to
  889. 30:16me. I didn't even run optimization mode
  890. 30:19on it. I don't think we even support
  891. 30:21multi- trading routes in our
  892. 30:23optimization mode just yet. So, I didn't
  893. 30:25do that. I just made changes based on
  894. 30:27what made sense to me as I explained
  895. 30:29everything to you. But still, I wouldn't
  896. 30:31say it's a perfect strategy. As you can
  897. 30:33see, there are some periods that it
  898. 30:34could lose money. And besides that when
  899. 30:36I ran it live I faced one very important
  900. 30:39issue and that was the slippage. So in
  901. 30:42the trading fees we set it here to this
  902. 30:45number. So a little bit higher than what
  903. 30:47the trading fees are for market orders
  904. 30:49on an exchange such as Binance but in
  905. 30:51reality the slippage was a bit higher.
  906. 30:53Now I did not trade on Binance. So if
  907. 30:56you are you have access to more
  908. 30:58liquidity than what I did. So, I wasn't
  909. 31:01trading on Binance and the exchange that
  910. 31:03I was trading in, this was a default
  911. 31:05trading fees. So, including a slippage,
  912. 31:07the actual fees that I was paying was
  913. 31:09something like this. So, that's another
  914. 31:11thing that you need to remember,
  915. 31:12especially if you're going to trade this
  916. 31:14strategy on something like a memecoin. I
  917. 31:17would say you're going to get way more
  918. 31:18opportunities and better ones because
  919. 31:20the volatility is higher in meme coins
  920. 31:22and so are the insufficiencies. But that
  921. 31:24also means when the volatility is really
  922. 31:27high and remember exactly in those
  923. 31:29points that's where the market makers
  924. 31:31will stop providing liquidity and that
  925. 31:33will make your slippage number even
  926. 31:34higher. So you need to consider that. So
  927. 31:37increase your trading fees to a number
  928. 31:38such as this or even this to be prepared
  929. 31:41for that. And the other thing is that
  930. 31:43instead of just trading two pairs like I
  931. 31:45did here, why not trade let's say four,
  932. 31:48six, eight or 10 pairs at the same time,
  933. 31:50right? because then you're going to get
  934. 31:53significantly smoother equity curve
  935. 31:55which is really awesome. So all the
  936. 31:57values such as your max roon or calmer
  937. 32:00or sharp ratio are going to better if
  938. 32:02you trade more trading pairs using
  939. 32:04something like this. Now as you guys
  940. 32:06already know we do have a strategy index
  941. 32:08page where I submit all the strategies
  942. 32:10that I show you guys with the back test
  943. 32:12results for other symbols and time
  944. 32:14frames and you can check them out here.
  945. 32:16But I'm not going to submit this one
  946. 32:17because this strategy uses two different
  947. 32:20training pairs and our strategies page
  948. 32:23doesn't support that yet. But a huge
  949. 32:24update is coming to this page and we're
  950. 32:26going to have way more strategies and
  951. 32:28better metrics and everything on this
  952. 32:30page very soon. So if you haven't
  953. 32:31checked out this page for more
  954. 32:32strategies, make sure to do so. As many
  955. 32:34of you guys already know, I am obsessed
  956. 32:36with statistical arbitrage. So this
  957. 32:38isn't really the end. This was just the
  958. 32:40updates that I had for 2025. So, I will
  959. 32:42definitely continue working on this
  960. 32:44strategy and hopefully improving the
  961. 32:46result of it. And before I leave you,
  962. 32:47we're going to have a giveaway. A random
  963. 32:49subscriber who likes the video and
  964. 32:50comments their opinion is going to win 1
  965. 32:53million bank token. All right, let's
  966. 32:54pick the winner for the previous video.
  967. 32:56And the winner is I'm not going to read
  968. 32:58their name, but I do appreciate your
  969. 33:00comments. Now, please reach out to me so
  970. 33:02I can send you your bank tokens. Thank
  971. 33:03you so much for watching. I'll see you
  972. 33:05in the next video.
  973. 33:10>> [music]

About this transcript

This page contains the full transcript of I Rebuilt Edward Thorp's $800M Pairs-Trading Strategy for 2025 (Full Python Walkthrough) by Algo-trading with Saleh, generated from the public captions YouTube serves with the video. The transcript has 7,176 words across 973 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.

What you can do with it

Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to translate the transcript into 100+ languages.

Free YouTube transcript tool

YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, translation to 100+ languages, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.