Skip to content

Commit 384588e

Browse files
committed
Bug fixes in Momentum.
Added additional tests/demonstrations Changes to be committed: modified: Base/Library/JRRtechnical.py new file: Extras/CodeProofs/jrrTA/BTCUSD.txt new file: Extras/CodeProofs/jrrTA/demo.ex2 new file: Extras/CodeProofs/jrrTA/demo.momentum new file: Extras/CodeProofs/jrrTA/demo.obv new file: Extras/CodeProofs/jrrTA/demo.psar new file: Extras/CodeProofs/jrrTA/demo.roc new file: Extras/CodeProofs/jrrTA/demo.volumema
1 parent 444a5a4 commit 384588e

8 files changed

Lines changed: 1195 additions & 0 deletions

File tree

Base/Library/JRRtechnical.py

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,191 @@ def Resistance(self, high_idx=2, period=14):
10421042
self.window[-1].append(resistance_level)
10431043
return self.window
10441044

1045+
# Parabolic SAR
1046+
1047+
def PSAR(self, HighIDX=2, LowIDX=3, startAF=0.02, stepAF=0.02, maxAF=0.2):
1048+
"""
1049+
Compute Parabolic SAR for the current window, fully transparent,
1050+
and append all intermediate values as new columns:
1051+
SAR, Trend (1=up, -1=down), Extreme Point (EP), Acceleration Factor (AF)
1052+
"""
1053+
n = len(self.window)
1054+
1055+
# Ensure enough data to compute
1056+
if n < 2 or self.window[-2][HighIDX] is None or self.window[-2][LowIDX] is None:
1057+
self.AddColumn(None) # SAR
1058+
self.AddColumn(None) # Trend
1059+
self.AddColumn(None) # EP
1060+
self.AddColumn(None) # AF
1061+
return self.window
1062+
1063+
prev_row = self.window[-2]
1064+
curr_row = self.window[-1]
1065+
1066+
sarIDX=len(prev_row)-4
1067+
1068+
# Read previous SAR, trend, EP, AF if they exist
1069+
prev_sar = prev_row[sarIDX] if prev_row[sarIDX] is not None else prev_row[LowIDX] # initial guess
1070+
prev_trend = prev_row[sarIDX+1] if len(prev_row) > sarIDX+1 and prev_row[sarIDX+1] is not None else 1 # assume uptrend
1071+
prev_ep = prev_row[sarIDX+2] if len(prev_row) > sarIDX+2 and prev_row[sarIDX+2] is not None else prev_row[HighIDX]
1072+
prev_af = prev_row[sarIDX+3] if len(prev_row) > sarIDX+3 and prev_row[sarIDX+3] is not None else startAF
1073+
1074+
# Determine current trend
1075+
trend = prev_trend
1076+
ep = prev_ep
1077+
af = prev_af
1078+
sar = prev_sar
1079+
1080+
# Update SAR
1081+
sar = sar + af * (ep - sar)
1082+
1083+
# Check for trend reversal
1084+
if trend == 1: # Uptrend
1085+
if curr_row[LowIDX] < sar:
1086+
trend = -1
1087+
sar = ep
1088+
ep = curr_row[LowIDX]
1089+
af = startAF
1090+
else:
1091+
if curr_row[HighIDX] > ep:
1092+
ep = curr_row[HighIDX]
1093+
af = min(af + stepAF, maxAF)
1094+
else: # Downtrend
1095+
if curr_row[HighIDX] > sar:
1096+
trend = 1
1097+
sar = ep
1098+
ep = curr_row[HighIDX]
1099+
af = startAF
1100+
else:
1101+
if curr_row[LowIDX] < ep:
1102+
ep = curr_row[LowIDX]
1103+
af = min(af + stepAF, maxAF)
1104+
1105+
# Append results as new columns
1106+
self.AddColumn(sar) # SAR
1107+
self.AddColumn(trend) # Trend: 1=up, -1=down
1108+
self.AddColumn(ep) # Extreme Point
1109+
self.AddColumn(af) # Acceleration Factor
1110+
1111+
return self.window
1112+
1113+
# Momentum indicator
1114+
1115+
def Momentum(self, colIDX, period=10):
1116+
"""
1117+
Calculate momentum for a given column index and period.
1118+
Momentum = Current value - value N periods ago
1119+
Appends the result as a new column in the rolling window.
1120+
1121+
:param colIDX: int, the column index in the rolling window to calculate momentum on
1122+
:param period: int, number of periods to look back
1123+
"""
1124+
1125+
# Get the last row
1126+
last_row = self.LastRow()
1127+
1128+
# Ensure there is enough history
1129+
if len(self.window) < period+1:
1130+
self.AddColumn(None)
1131+
return self.window
1132+
1133+
# Calculate momentum
1134+
if len(self.window[-(period + 1)])<colIDX+1:
1135+
self.AddColumn(None)
1136+
return self.window
1137+
1138+
past_value = self.window[-(period + 1)][colIDX]
1139+
if past_value is None:
1140+
self.AddColumn(None)
1141+
return self.window
1142+
1143+
current_value = last_row[colIDX]
1144+
1145+
if past_value is not None and current_value is not None:
1146+
momentum = current_value - past_value
1147+
else:
1148+
momentum = None
1149+
1150+
# Add momentum as new column
1151+
self.AddColumn(momentum)
1152+
return self.window
1153+
1154+
def RateOfChange(self, colIDX, period=10):
1155+
"""
1156+
Calculate the Rate of Change (ROC) for a given column index over a specified period.
1157+
ROC = ((current value - value N periods ago) / value N periods ago) * 100
1158+
The result is appended as a new column to the rolling window.
1159+
1160+
Parameters:
1161+
colIDX (int): Column index to calculate ROC from
1162+
period (int): Number of periods for the ROC calculation
1163+
"""
1164+
last_row = self.LastRow()
1165+
1166+
# Check if enough rows exist
1167+
if len(self.window) > period:
1168+
past_row = self.window[-period-1] # Row N periods ago
1169+
if len(past_row)<colIDX+1:
1170+
roc = None
1171+
else:
1172+
current_value = last_row[colIDX]
1173+
past_value = past_row[colIDX]
1174+
1175+
if current_value is not None and past_value not in (None, 0):
1176+
roc = ((current_value - past_value) / past_value) * 100
1177+
else:
1178+
roc = None
1179+
else:
1180+
roc = None
1181+
1182+
# Add ROC as a new column
1183+
self.AddColumn(roc)
1184+
return self.window
1185+
1186+
# On Balance Volume
1187+
1188+
def OBV(self, closeIDX=4, volumeIDX=5):
1189+
"""
1190+
Calculate On-Balance Volume (OBV) based on closing prices and volume.
1191+
OBV increases by volume when the closing price rises,
1192+
decreases by volume when the closing price falls,
1193+
remains unchanged if the price is the same.
1194+
1195+
Parameters:
1196+
closeIDX (int): Column index of the closing price
1197+
volumeIDX (int): Column index of the volume
1198+
"""
1199+
if len(self.window)<1:
1200+
self.AddColumn(None)
1201+
return self.window
1202+
1203+
last_row = self.LastRow()
1204+
prevIDX=len(last_row) # Get the right idx for the OBV
1205+
prev_row = self.window[-2] # Previous row
1206+
1207+
close_now = last_row[closeIDX]
1208+
close_prev = prev_row[closeIDX]
1209+
vol_now = last_row[volumeIDX]
1210+
if len(prev_row)<prevIDX+1:
1211+
self.AddColumn(vol_now)
1212+
return self.window
1213+
1214+
obv_prev = prev_row[prevIDX] if prev_row[prevIDX] is not None else vol_now # Use last column as previous OBV
1215+
1216+
if close_now is not None and close_prev is not None and vol_now is not None:
1217+
if close_now > close_prev:
1218+
obv = obv_prev + vol_now
1219+
elif close_now < close_prev:
1220+
obv = obv_prev - vol_now
1221+
else:
1222+
obv = obv_prev
1223+
else:
1224+
obv = vol_now
1225+
1226+
# Add OBV as a new column
1227+
self.AddColumn(obv)
1228+
return self.window
1229+
10451230
###
10461231
### END of code
10471232
###

0 commit comments

Comments
 (0)