公式解决方案
很难理解您在寻找什么。我希望我明白了。这是我的“Book10”(简短的每日文件)单元格 M2 的公式:
=IFERROR((IF($H2<$I2,INDEX([0MAS.xlsb]MAS!$G:$G,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$D:$D>$B2+M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1))-$G2,"")&IF($H2>$I2,INDEX([0MAS.xlsb]MAS!$G:$G,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$C:$C<$B2-M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1))-$G2,""))*1,"")
把它切成小块,我们得到这个:
=IFERROR( 'In case of error, nothing will be shown.
( 'This brackets is to multiply the result by one to turn it into a number.
IF(
$H2<$I2, 'If "Buy" is greater than "Sell"
INDEX( 'we target
[0MAS.xlsb]MAS!$G:$G, 'a cell in the column G of 0MAS
AGGREGATE( 'with
15, 'the smallest
6, 'valid value
ROW([0MAS.xlsb]MAS!$D:$D) 'thanks to their row (in column D)
*1/([0MAS.xlsb]MAS!$D:$D>$B2+M$1) 'filtered according to the cells that contains a value greater than Last plus the M-AA extra,
*1/ISNUMBER([0MAS.xlsb]MAS!$D:$D) 'that is not a header (text) and
*1/([0MAS.xlsb]MAS!$A:$A=$A2), 'has the same Symbol of the actual row.
1
)
) - $G2, 'Such cell has a date value that we subtract to the cell in the column G of the actual row.
""
)
&
IF(
$H2>$I2, 'If "Sell" is greater than "Buy"
INDEX( 'we target
[0MAS.xlsb]MAS!$G:$G, 'a cell in the column G of 0MAS
AGGREGATE( 'with
15, 'the smallest
6, 'valid value
ROW([0MAS.xlsb]MAS!$C:$C) 'thanks to their row (in column C)
*1/([0MAS.xlsb]MAS!$C:$C<$B2-M$1) 'filtered according to the cells that contains a value smaller than Last minus the M-AA extra,
*1/ISNUMBER([0MAS.xlsb]MAS!$C:$C) 'that is not a header (text) and
*1/([0MAS.xlsb]MAS!$A:$A=$A2), 'has the same Symbol of the actual row.
1
)
) - $G2, 'Such cell has a date value that we subtract to the cell in the column G of the actual row.
""
)
) * 1, 'The result is multiplied to return a number.
""
)
如您所见,如果“买入”等于“卖出”,则不会有结果。
奖励公式
您可以使用此公式来反馈该公式所针对的单元格:
=IF($H2<$I2,"Targeting cell D","")&IF($H2>$I2,"Targeting cell C","")& IFERROR((IF($H2<$I2,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$D:$D>$B2+M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1),"")&IF($H2>$I2,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$C:$C<$B2-M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1),""))*1,"") & " (value " & IFERROR((IF($H2<$I2,INDEX([0MAS.xlsb]MAS!$G:$G,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$D:$D>$B2+M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1)),"")&IF($H2>$I2,INDEX([0MAS.xlsb]MAS!$G:$G,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$C:$C<$B2-M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1)),""))*1,"") & "), Sybol: " & IFERROR(IF($H2<$I2,INDEX([0MAS.xlsb]MAS!$A:$A,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$D:$D>$B2+M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1)),"")&IF($H2>$I2,INDEX([0MAS.xlsb]MAS!$A:$A,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$C:$C<$B2-M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1)),""),"")&", Low: " & IFERROR(IF($H2<$I2,INDEX([0MAS.xlsb]MAS!$C:$C,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$D:$D>$B2+M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$D:$D)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1)),"")&IF($H2>$I2,INDEX([0MAS.xlsb]MAS!$C:$C,AGGREGATE(15,6,ROW([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$C:$C<$B2-M$1)*1/ISNUMBER([0MAS.xlsb]MAS!$C:$C)*1/([0MAS.xlsb]MAS!$A:$A=$A2),1)),""),"") & ", Starting data: " & $A2 & ", " & $B2 & ", " & $G2 & ", " & $H2 & ", " & $I2 & ", " & $M1
VBA 解决方案
试试这个代码:
Sub SubFirstLowestValue()
'Declarations.
Dim WksSource As Worksheet
Dim RngSource As Range
Dim WksRecord As Worksheet
Dim RngRecord As Range
Dim RngRecordList As Range
Dim WksResult As Worksheet
Dim RngResult As Range
Dim BytExtraMargin As Byte
Dim BytExtraMarginMin As Byte
Dim BytExtraMarginMax As Byte
Dim DblSourceIndex As Double
Dim DblSourceIndexCount As Double
Dim RngSourceSYMBOL As Range
Dim RngSourceLAST As Range
Dim RngSourceDATE As Range
Dim RngSourceBUY As Range
Dim RngSourceSELL As Range
Dim RngRecordSYMBOL As Range
Dim RngRecordLOW As Range
Dim RngRecordHIGH As Range
Dim RngRecordDATE As Range
Dim RngTarget As Range
Dim StrExistingAutoFilterAddress As String
Dim StrSYMBOL As String
Dim StrDATE As String
Dim BytCounter01 As Double
Dim BlnStatusBar As Boolean
'Settings.
Set WksSource = ActiveSheet
Set RngSource = WksSource.Range("A2")
Set WksRecord = ActiveSheet
'Checking for eventual autofilter.
If WksRecord.AutoFilterMode = True Then
'Setting StrExistingAutoFilterAddress.
StrExistingAutoFilterAddress = ActiveSheet.AutoFilter.Range.Address
End If
'Removing eventual autofilter.
WksRecord.AutoFilterMode = False
'Settings.
Set RngRecord = WksRecord.Range("A2")
Set RngRecordList = WksRecord.Range(RngRecord, RngRecord.End(xlDown).End(xlToRight))
Set WksResult = ActiveSheet
Set RngResult = WksResult.Range("M1")
Set RngSourceSYMBOL = WksSource.Range("A2")
Set RngSourceLAST = WksSource.Range("B2")
Set RngSourceDATE = WksSource.Range("G2")
Set RngSourceBUY = WksSource.Range("H2")
Set RngSourceSELL = WksSource.Range("I2")
Set RngRecordSYMBOL = WksRecord.Range("A2")
Set RngRecordLOW = WksRecord.Range("C2")
Set RngRecordHIGH = WksRecord.Range("D2")
Set RngRecordDATE = WksRecord.Range("G2")
BytExtraMarginMin = 1
BytExtraMarginMax = 15
DblSourceIndexCount = RngSource.End(xlDown).Row - RngSource.Row
'Setting an autofilter on RngRecordList.
RngRecordList.Offset(-1, 0).Resize(RngRecordList.Rows.Count + 1, RngRecordList.Columns.Count).AutoFilter
'Compiling extra margins' list headers.
For BytCounter01 = 0 To (BytExtraMarginMax - BytExtraMarginMin)
RngResult.Offset(0, BytCounter01).Value = "+-" & BytExtraMarginMin + BytCounter01
Next
'Resetting RngResult.
Set RngResult = RngResult.Offset(1, 0)
'Sorting RngRecordList.
With WksRecord.Sort
.SortFields.Clear
.SortFields.Add Key:=Intersect(RngRecordList, RngRecordSYMBOL.EntireColumn), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
.SortFields.Add Key:=Intersect(RngRecordList, RngRecordDATE.EntireColumn), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
.SetRange RngRecordList
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
.SortFields.Clear
End With
'Setting status bar.
BlnStatusBar = Application.DisplayStatusBar
Application.DisplayStatusBar = True
'Covering the whole list in WksSource.
For DblSourceIndex = 1 To DblSourceIndexCount
'Refreshing status bar message.
Application.StatusBar = "Progress: " & Round(DblSourceIndex / DblSourceIndexCount * 100, 0) & "% (" & DblSourceIndex & " of " & DblSourceIndexCount & ")"
'Clearing the result row.
For BytExtraMargin = BytExtraMarginMin To BytExtraMarginMax
RngResult.Offset(0, BytExtraMargin - BytExtraMarginMin).Value = ""
Next
'Filtering RngRecordList for the given symbol if changed.
If RngSourceSYMBOL <> StrSYMBOL Then
RngRecordList.AutoFilter Field:=RngRecordSYMBOL.Column - RngRecordList.Column + 1, Criteria1:=RngSourceSYMBOL.Value
StrSYMBOL = RngSourceSYMBOL.Value
End If
'Filtering RngRecodlist for the given date if changed.
If RngSourceDATE <> StrDATE Then
RngRecordList.AutoFilter Field:=RngRecordDATE.Column - RngRecordList.Column + 1, Criteria1:=">" & Month(RngSourceDATE.Value) & "/" & Day(RngSourceDATE.Value) & "/" & Year(RngSourceDATE.Value)
StrDATE = RngSourceDATE.Value
End If
'Checking if RngSourceBUY is greater than RngSourceSELL or vice versa.
Select Case True
Case Is = RngSourceBUY < RngSourceSELL
'Covering each visible (alias: unfiltered) cell in the "High" column of RngRecordList.
For Each RngTarget In RngRecordList.Columns(RngRecordHIGH.Column - RngRecordList.Column + 1).SpecialCells(xlCellTypeVisible)
'Covering each extra margin.
For BytExtraMargin = BytExtraMarginMin To BytExtraMarginMax
'Checking if the given cell's value is bigger than the value of RngSourceLAST plus the given extra margin.
If RngTarget.Value > RngSourceLAST + BytExtraMargin And RngResult.Offset(0, BytExtraMargin - BytExtraMarginMin).Value = "" Then
'Reporting the dates' difference between the given row in the RngRecordList and the date in RngSourceDATE.
RngResult.Offset(0, BytExtraMargin - BytExtraMarginMin).Value = WksRecord.Cells(RngTarget.Row, RngRecordDATE.Column).Value - RngSourceDATE.Value
End If
'If all the results for the given row has been specified, the code is skipped to CP_Result_row_filled.
If Excel.WorksheetFunction.CountBlank(WksResult.Range(RngResult, RngResult.Offset(0, BytExtraMarginMax - BytExtraMarginMin))) = 0 Then GoTo CP_Result_row_filled
Next
Next
Case Is = RngSourceBUY > RngSourceSELL
'Covering each visible (alias: unfiltered) cell in the "Low" column of RngRecordList.
For Each RngTarget In RngRecordList.Columns(RngRecordLOW.Column - RngRecordList.Column + 1).SpecialCells(xlCellTypeVisible)
'Covering each extra margin.
For BytExtraMargin = BytExtraMarginMin To BytExtraMarginMax
'Checking if the given cell's value is smaller than the value of RngSourceLAST minus the given extra margin.
If RngTarget.Value < RngSourceLAST - BytExtraMargin And RngResult.Offset(0, BytExtraMargin - BytExtraMarginMin).Value = "" Then
'Reporting the dates' difference between the given row in the RngRecordList and the date in RngSourceDATE.
RngResult.Offset(0, BytExtraMargin - BytExtraMarginMin).Value = WksRecord.Cells(RngTarget.Row, RngRecordDATE.Column).Value - RngSourceDATE.Value
End If
'If all the results for the given row has been specified, the code is skipped to CP_Result_row_filled.
If Excel.WorksheetFunction.CountBlank(WksResult.Range(RngResult, RngResult.Offset(0, BytExtraMarginMax - BytExtraMarginMin))) = 0 Then GoTo CP_Result_row_filled
Next
Next
End Select
CP_Result_row_filled:
'Setting the variables for the next row in WksSource.
Set RngSourceSYMBOL = RngSourceSYMBOL.Offset(1, 0)
Set RngSourceLAST = RngSourceLAST.Offset(1, 0)
Set RngSourceDATE = RngSourceDATE.Offset(1, 0)
Set RngSourceBUY = RngSourceBUY.Offset(1, 0)
Set RngSourceSELL = RngSourceSELL.Offset(1, 0)
Set RngResult = RngResult.Offset(1, 0)
Next
'Restoring eventual previous autofiler.
WksRecord.AutoFilterMode = False
If StrExistingAutoFilterAddress <> "" Then
WksRecord.Range(StrExistingAutoFilterAddress).AutoFilter
End If
'Resetting status bar.
Application.StatusBar = False
Application.DisplayStatusBar = BlnStatusBar
End Sub
代码可以定制,但现在它被设计用于活动工作表,该工作表将包含一个列表,该列表将是“记录”列表(例如 0MAS 文件)以及起始数据列表(比如 Book10 文件)。测试它:
- 打开0MAS文件;
- 将代码复制粘贴到 VBA 模块中;
- 选择 MAS 表;
- 启动代码。
您很可能必须将文件的扩展名从 .xlsb 更改为 .xlsm 才能保留宏。