|
1 registered members (TipmyPip),
22,211
guests, and 3
spiders. |
|
Key:
Admin,
Global Mod,
Mod
|
|
|
Order Book Fuzzy Strangle
[Re: TipmyPip]
#489701
10/13/26 17:08
10/13/26 17:08
|
Joined: Sep 2017
Posts: 340
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 340
|
We can extend your original strategy into a market-depth-aware options trading system, combining Zorro's native Level II order-book analysis, fuzzy logic, volatility estimation, and dynamic options selection. The important improvement is that the strategy will no longer rely exclusively on historical closing prices. It will also examine the current distribution of buy and sell liquidity before deciding whether to open an options position. 1. Zorro's built-in order-book functions Zorro provides three native functions for market-depth analysis, documented in its official manual. They require Zorro S. Function Purpose orderUpdate() Retrieves market-depth quotes from the broker or historical data orderCVD() Constructs a cumulative distribution of order-book liquidity cpd() Measures cumulative relative bid or ask volume at a price These can be combined into three analytical signals: - Bid pressure: Relative liquidity on the buying side. - Ask pressure: Relative liquidity on the selling side. - Order-book imbalance: Difference between buying and selling pressure, normalized by their combined liquidity. A useful mathematical representation is: \[ I_t=\frac{B_t-A_t}{B_t+A_t} \] Where \(B_t\) and \(A_t\) are comparable bid-side and ask-side cumulative liquidity measurements. A positive imbalance indicates more visible bid liquidity; a negative imbalance indicates more visible ask liquidity. Neither guarantees the next price movement, because displayed orders can be canceled or moved. (Latex formula) // OrderBookFuzzyStrangle_v1.c
// Zorro S / lite-C -- SPY long strangle with underlying order-book filter.
// Requires synchronized historical .t2 book snapshots and .t8 options chains
// for Test/Train; GET_BOOK and options support from broker for Trade.
// Research prototype: validate fills, execution, and broker behavior in demo.
#include <contract.c>
#define BOOK_HANDLE 1
#define VOL_PERIOD 20
#define MIN_BOOK_QUOTES 6
#define MIN_DTE 30
#define EXIT_DTE 10
#define MIN_IMBALANCE 0.15
#define EXIT_IMBALANCE 0.07
#define MIN_FUZZY 0.65
#define MOVE_MULTIPLIER 1.10
#define MAX_DEBIT_USD 1500.0
#define PROFIT_FRACTION 0.50
#define LOSS_FRACTION 0.35
// Average absolute close-to-close change, in SPY dollars per hourly bar.
var meanAbsoluteMove(vars Prices, int Period)
{
var Sum = 0;
int i;
for(i = 1; i <= Period; i++)
Sum += abs(Prices[i-1] - Prices[i]);
return Sum / Period;
}
// Returns 1 if a usable book snapshot exists and fills the imbalance output.
int readBookImbalance(var Spot, var* Imbalance)
{
int N = orderUpdate(0,BOOK_HANDLE); // History\SPY.t2 or broker GET_BOOK
if(N < MIN_BOOK_QUOTES) return 0;
T2* Quotes = dataStr(BOOK_HANDLE,OrderRow,0);
if(!Quotes) return 0;
var Distance = 0.01*Spot; // visible liquidity within +/-1% of SPY
if(orderCVD(Quotes,N,Distance) < MIN_BOOK_QUOTES) return 0;
// cpd returns values in 0..100 normalized to the larger book side.
var BidDepth = cpd(-(Spot-Distance));
var AskDepth = cpd(Spot+Distance);
var Total = BidDepth+AskDepth;
if(Total <= 0) return 0;
*Imbalance = (BidDepth-AskDepth)/Total;
return 1;
}
// Returns 1 if open options should be liquidated on risk/expiration rules.
int exitByRisk()
{
var Cost = 0;
var OpenPnL = 0;
int ExpireSoon = 0;
int Legs = 0;
for(current_trades) {
if(!TradeIsOpen || !TradeIsLong || !TradeIsContract) continue;
Cost += TradePriceOpen*TradeUnits;
OpenPnL += TradeProfit;
if(contractDays(ThisTrade) <= EXIT_DTE) ExpireSoon = 1;
Legs++;
}
if(!Legs) return 0;
if(ExpireSoon) return 1;
if(Cost <= 0) return 0;
if(OpenPnL >= PROFIT_FRACTION*Cost) return 1;
if(OpenPnL <= -LOSS_FRACTION*Cost) return 1;
return 0;
}
void buyStrangle(var Spot, var AvgMove)
{
// Dynamic OTM strike targets in dollars. Select nearest listed strikes.
var Gap = max(0.01*Spot,8*AvgMove);
var CallTarget = Spot+Gap;
var PutTarget = Spot-Gap;
if(PutTarget <= 0) return;
// 6 means select by strike (the 4th contractFind parameter is mandatory).
CONTRACT* Call = contractFind(CALL,MIN_DTE,CallTarget,6);
CONTRACT* Put = contractFind(PUT,MIN_DTE,PutTarget,6);
if(!Call || !Put) return;
if(Call->fStrike <= Spot || Put->fStrike >= Spot) return;
if(Call->Expiry != Put->Expiry) return;
if(contractDays(Call) <= EXIT_DTE || contractDays(Put) <= EXIT_DTE)
return;
if(contractPrice(Call) <= 0) return;
if(contractPrice(Put) <= 0) return;
if(Call->fAsk <= 0 || Put->fAsk <= 0) return;
if(Call->fBid <= 0 || Put->fBid <= 0) return;
var Debit = (Call->fAsk+Put->fAsk)*Multiplier;
if(Debit <= 0 || Debit > MAX_DEBIT_USD) return;
if(combo(Call,1,Put,1,0,0,0,0) != 2) return;
MarginCost = comboMargin(1,3); // Stock/ETF options; long positions.
// The broker receives the combo when the second leg is submitted, if
// SET_COMBO_LEG is supported. Otherwise separate orders can be submitted.
enterLong(comboLeg(1));
enterLong(comboLeg(2));
printf("\nStrangle order attempted: SPY %.2f, call %.2f, put %.2f, debit %.2f",
Spot,Call->fStrike,Put->fStrike,Debit);
}
void run()
{
set(LOGFILE);
BarPeriod = 60;
LookBack = 100;
StartDate = 2020;
EndDate = 2024;
History = ".t8"; // Workshop 8: historical options/unadjusted underlying.
assetList("AssetsIB");
asset("SPY");
algo("OBStrangle");
Multiplier = 100; // Standard SPY option contract; verify with broker.
Capital = 100000; // Research backtest account, not a live risk control.
if(is(EXITRUN)) return;
static int LastChainDate = 0;
if(is(INITRUN)) {
LastChainDate = 0;
return;
}
vars Prices = series(priceClose());
var AvgMove = meanAbsoluteMove(Prices,VOL_PERIOD);
if(AvgMove <= 0) return;
FuzzyRange = max(0.01,0.25*AvgMove);
var Bullish = max(risingF(Prices),valleyF(Prices));
var Bearish = max(fallingF(Prices),peakF(Prices));
// In historical tests, options must be refreshed every bar.
// For live mode, cache the costly IB chain download for the session day.
int Today = ymd(wdate(0));
if(!Live || Today != LastChainDate || NumContracts <= 0) {
if(!contractUpdate(Asset,0,CALL|PUT)) return;
LastChainDate = Today;
}
// These exits must work even when the current order book is unavailable.
if(NumOpenLong > 0 && exitByRisk()) {
exitLong("cp"); // Only call and put contracts of this asset/algo.
return;
}
var Imbalance = 0;
if(!readBookImbalance(priceClose(),&Imbalance)) return;
plot("Order book imbalance",Imbalance,NEW,BLUE);
if(NumOpenLong > 0 || NumPendingLong > 0) {
// Fuzzy bearish price signal plus disappearing book pressure.
if(Bearish > MIN_FUZZY && abs(Imbalance) < EXIT_IMBALANCE)
exitLong("cp");
return;
}
// Require a meaningful move, visible bid/ask pressure, and corresponding
// fuzzy price direction. Long strangle still buys BOTH call and put.
if(abs(Prices[0]-Prices[1]) < MOVE_MULTIPLIER*AvgMove) return;
if(abs(Imbalance) < MIN_IMBALANCE) return;
if(Imbalance > 0 && Bullish >= MIN_FUZZY)
buyStrangle(priceClose(),AvgMove);
else if(Imbalance < 0 && Bearish >= MIN_FUZZY)
buyStrangle(priceClose(),AvgMove);
}
|
|
|
Markov Rulebook v.01
[Re: TipmyPip]
#489704
38 minutes ago
38 minutes ago
|
Joined: Sep 2017
Posts: 340
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 340
|
VWAP Markov State Trading Strategy for Zorro The Strategy consist of an algorithmic trading strategy that combines VWAP volatility bands, candlestick price action, and Markov state-transition probabilities to identify trading opportunities across 28 currency pairs. How the strategy works 1. VWAP Analysis: Calculates VWAP and six volatility bands at ±1, ±2, and ±2.5 standard deviations to identify important market price zones. 2. Market Classification: Classifies completed candles according to their bullish, bearish, or doji structure and their positions relative to the VWAP bands. 3. Markov Probability Model: Learns historical transitions between market states and estimates the probability that the next candle will be bullish, bearish, or neutral. 4. Trading Decisions: Supports 36 predefined research strategies, 300 VWAP event/action rules, custom filters, and Markov-directed trading. 5. Risk Management: Includes configurable stop-loss levels, pending entries, VWAP-based exits, and portfolio position limits. Key technical characteristics Feature Configuration Currency pairs 28 Default timeframe 5 minutes (configurable to H4) Markov states 780 Transition matrix 780 × 780 per pair Historical learning window 1,024 observations VWAP period 20 Default execution Market orders Default stop-loss buffer 10% of candle range Important observations Although the filename says Markov720, the actual code defines 780 states: 330 bullish, 330 bearish, and 120 doji. The current default configuration runs research strategy M00 while the Markov model monitors transitions. Markov-directed trading must be explicitly enabled. I also noticed that the code's sweep-direction logic does not match your earlier bullish-candle SELL / bearish-candle BUY reversal specification. That should be corrected if the original reversal behavior is intended. // VWAP_Rulebook_28Pair_Markov780.c
#include <default.c>
// ---- EDIT THESE CONFIGURATION PARAMETERS (native lite-C, no MQL input syntax) ----
#define ENGINE_MODE 0 // 0 M00-M35, 1 Catalog 0..299, 2 S/B custom, 3 Markov
#define RESEARCH_ID 0 // M00..M35
#define CATALOG_ID 0 // 0..299 (100 observable events x BUY/SELL/EXIT)
#define SELL_STATIC_ID 0 // 0..30
#define BUY_STATIC_ID 0 // 0..30
#define SELL_EVENT_ID 1 // 1..12
#define BUY_EVENT_ID 1 // 1..12
#define CANDLE_RULE_ID 0 // 0 disabled, 1..25
#define SIGNATURE_FILTER -1 // -1 disabled; 0..779
#define TIMEFRAME_MINUTES 5 // 5 M5 default, 240 for H4
#define SINGLE_ASSET 0 // 0 = 28 assets, 1 = named symbol only
#define SINGLE_ASSET_NAME "EUR/USD"
#define ASSET_LIST "AssetsForex" // Broker-compatible list MUST contain requested pairs
#define ENTRY_STYLE 0 // 0 market; 1 Zorro-managed Entry stop
#define PENDING_PERCENT 10.0
#define STOP_PERCENT 10.0
#define ENTRY_WAIT_BARS 2
#define ENABLE_BROKER_STOP 1 // Zorro StopFactor=1, when broker plugin supports it
#define ENABLE_SOFTWARE_EXIT 1
#define EXIT_RULE_ID 14 // X01..X20
#define MAX_SPREAD_PIPS 0.0 // 0 disabled
#define ORDER_LOTS 1 // Zorro INTEGER micro/mini/standard lots, NOT MQL "0.01"
#define MAX_PORTFOLIO_POSITIONS 28
#define VWAP_PERIOD 20
#define VWAP_MATCH_MQL 1 // N+1 VWAP bars, N STD bars
#define VWAP_VOLUME_MODE 1 // 1 use marketVol (Zorro S required), 0 unweighted approximation
#define VWAP_PRICE_MODE 0 // 0 close; 1 open; 2 high; 3 low; 4 median; 5 typical; 6 weighted
#define DEVIATION_1 1.0
#define DEVIATION_2 2.0
#define DEVIATION_3 2.5
#define MARKOV_MODE 0 // 0 monitor only, 1 gate existing engines
#define MARKOV_MIN_COUNT 8
#define MARKOV_MIN_DIRECTION 0.50
#define MARKOV_PRIOR 0.01
#define MARKOV_BACKOFF 1
#define MARKOV_BLOCK_SPARSE 0
#define MARKOV_NEED_SWEEP 1
#define MK_WINDOW 1024
#define MAX_PAIRS 28
#define MK_STATES 780
#define Z_THRESHOLD 1.0
#define WICK_THRESHOLD 0.35
#define EPSILON_PIPS 1.0
#define MIN_REWARD_PIPS 1.0
#define MIN_RISK_REWARD 1.0
#define SMALL_CANDLE_SPREAD_MULTIPLE 3.0
#define SWEEP_TIMEOUT_SECONDS 60
#define MIN_RETURN_TICKS 3
#define EXIT_SIGMA_MULTIPLE 1.0
#define EXIT_RANGE_MULTIPLE 1.0
#define EXIT_RANGE_PERCENT 10.0
#define EXIT_WAIT_BARS 3
#define EXIT_DEADLINE_MINUTES 120
#define PARTIAL_PERCENT 50.0
#define TRAIL_SIGMA_MULTIPLE 1.0
#define PH_SWEEP 0
#define PH_RETURN 1
#define PH_READY 2
#define PH_RETEST 3
// Exact 28-currency universe, Zorro's usual slash format.
string ForexNames[28];
void initForexNames()
{
// lite-C requires explicit initialization for arrays of string pointers.
ForexNames[0]="EUR/USD";ForexNames[1]="GBP/USD";ForexNames[2]="USD/JPY";
ForexNames[3]="GBP/JPY";ForexNames[4]="USD/CAD";ForexNames[5]="EUR/AUD";
ForexNames[6]="EUR/JPY";ForexNames[7]="AUD/CAD";ForexNames[8]="AUD/JPY";
ForexNames[9]="AUD/NZD";ForexNames[10]="AUD/USD";ForexNames[11]="CAD/JPY";
ForexNames[12]="EUR/CAD";ForexNames[13]="EUR/CHF";ForexNames[14]="EUR/GBP";
ForexNames[15]="EUR/NZD";ForexNames[16]="GBP/CAD";ForexNames[17]="GBP/CHF";
ForexNames[18]="NZD/CAD";ForexNames[19]="NZD/JPY";ForexNames[20]="NZD/USD";
ForexNames[21]="USD/CHF";ForexNames[22]="CHF/JPY";ForexNames[23]="AUD/CHF";
ForexNames[24]="GBP/NZD";ForexNames[25]="NZD/CHF";ForexNames[26]="CAD/CHF";
ForexNames[27]="GBP/AUD";
}
typedef struct
{
int usable;
string symbol;
int valid,signature,dir;
int phase,crossMask,retouch,quoteKnown,activeBefore;
int observedBar,lastEntryBar,lastPersistBar;
int ringHead,ringSize;
int predictionSource,nEffective,topNext,topCount;
int seenPosition;
int partialDone;
var o,h,l,c,range,body,uw,lw;
var v,sigma,band[7]; // D3 D2 D1 VWAP U1 U2 U3
var previousAsk,sweepStamp;
var probabilityBull,probabilityBear,probabilityDoji;
var frozenCenter,highWater,lowWater;
var lastEntryLevel,entryStamp;
} FXSTATE;
FXSTATE FX[MAX_PAIRS];
int* MarkovEdges = 0;
int* MarkovOutgoing = 0;
int* MarkovBull = 0;
int* MarkovBear = 0;
int* MarkovDoji = 0;
int* MarkovRing = 0;
int MarkovDirection[MK_STATES],MarkovCloseZone[MK_STATES];
int ActivePairs = 0;
// ---- Signature ranking: 0..329 bullish, 330..659 bearish, 660..779 doji ----
// a=zone(L), b=zone(min(O,C)), c=zone(max(O,C)), d=zone(H).
int signatureRank(int a,int b,int c,int d,int doji)
{
int n=0,i,j,k,h;
if(doji) {
for(i=0;i<8;i++)for(j=i;j<8;j++)for(k=j;k<8;k++) {
if(i==a && j==b && k==d)return n;
n++;
}
return -1;
}
for(i=0;i<8;i++)for(j=i;j<8;j++)
for(k=j;k<8;k++)for(h=k;h<8;h++) {
if(i==a && j==b && k==c && h==d)return n;
n++;
}
return -1;
}
void makeMetadata()
{
int n=0,i,j,k,h;
for(i=0;i<8;i++)for(j=i;j<8;j++)
for(k=j;k<8;k++)for(h=k;h<8;h++) {
MarkovDirection[n]=1; MarkovCloseZone[n]=k;
MarkovDirection[330+n]=-1; MarkovCloseZone[330+n]=j;
n++;
}
n=0;
for(i=0;i<8;i++)for(j=i;j<8;j++)for(k=j;k<8;k++) {
MarkovDirection[660+n]=0; MarkovCloseZone[660+n]=j;
n++;
}
}
int initMarkov()
{
int edgeCount = ActivePairs*MK_STATES*MK_STATES;
int rowCount=ActivePairs*MK_STATES;
MarkovEdges = malloc(edgeCount*sizeof(int));
MarkovOutgoing = malloc(rowCount*sizeof(int));
MarkovBull = malloc(rowCount*sizeof(int));
MarkovBear = malloc(rowCount*sizeof(int));
MarkovDoji = malloc(rowCount*sizeof(int));
MarkovRing = malloc(ActivePairs*MK_WINDOW*sizeof(int));
if(!MarkovEdges || !MarkovOutgoing || !MarkovBull || !MarkovBear ||
!MarkovDoji || !MarkovRing) return 0;
memset(MarkovEdges,0,edgeCount*sizeof(int));
memset(MarkovOutgoing,0,rowCount*sizeof(int));
memset(MarkovBull,0,rowCount*sizeof(int));
memset(MarkovBear,0,rowCount*sizeof(int));
memset(MarkovDoji,0,rowCount*sizeof(int));
int i;
for(i=0;i<ActivePairs*MK_WINDOW;i++)MarkovRing[i]=-1;
makeMetadata();
return 1;
}
void releaseMarkov()
{
if(MarkovEdges)free(MarkovEdges);
if(MarkovOutgoing)free(MarkovOutgoing);
if(MarkovBull)free(MarkovBull);
if(MarkovBear)free(MarkovBear);
if(MarkovDoji)free(MarkovDoji);
if(MarkovRing)free(MarkovRing);
MarkovEdges=0;MarkovOutgoing=0;MarkovBull=0;MarkovBear=0;
MarkovDoji=0;MarkovRing=0;
}
int mkRow(int id,int from) {return id*MK_STATES+from;}
int mkEdge(int id,int from,int to) {return (id*MK_STATES+from)*MK_STATES+to;}
int mkRingIndex(int id,int offset) {return id*MK_WINDOW+offset;}
void adjustTransition(int id,int from,int to,int delta)
{
if(from<0 || to<0 || from>=MK_STATES || to>=MK_STATES)return;
int e=mkEdge(id,from,to),r=mkRow(id,from);
if(delta<0 && MarkovEdges[e]<=0)return;
MarkovEdges[e]+=delta;MarkovOutgoing[r]+=delta;
if(to<330)MarkovBull[r]+=delta;
else if(to<660)MarkovBear[r]+=delta;
else MarkovDoji[r]+=delta;
}
void addObservation(int id,int sig)
{
if(sig<-1 || sig>=MK_STATES)return;
int head=FX[id].ringHead,n=FX[id].ringSize;
if(n>=MK_WINDOW) {
int old=MarkovRing[mkRingIndex(id,head)];
int next=MarkovRing[mkRingIndex(id,(head+1)%MK_WINDOW)];
adjustTransition(id,old,next,-1);
head=(head+1)%MK_WINDOW;n--;
}
if(n>0) {
int last=MarkovRing[mkRingIndex(id,(head+n-1)%MK_WINDOW)];
adjustTransition(id,last,sig,1);
}
MarkovRing[mkRingIndex(id,(head+n)%MK_WINDOW)]=sig;
FX[id].ringHead=head;FX[id].ringSize=n+1;
}
int zoneAt(int id,var p)
{
int j;for(j=0;j<7;j++)if(p<FX[id].band[j])return j;
return 7;
}
int signatureOf(int id)
{
int zl=zoneAt(id,FX[id].l);
int zb=zoneAt(id,min(FX[id].o,FX[id].c));
int zt=zoneAt(id,max(FX[id].o,FX[id].c));
int zh=zoneAt(id,FX[id].h);
if(FX[id].dir==0)return 660+signatureRank(zl,zb,zt,zh,1);
if(FX[id].dir>0)return signatureRank(zl,zb,zt,zh,0);
return 330+signatureRank(zl,zb,zt,zh,0);
}
void predict(int id)
{
FXSTATE* F=&FX[id];
int st=F->signature;
F->nEffective=0;F->predictionSource=0;
F->probabilityBull=0;F->probabilityBear=0;F->probabilityDoji=0;
F->topNext=-1;F->topCount=0;
if(st<0 || st>=MK_STATES)return;
int row=mkRow(id,st),n=MarkovOutgoing[row],bull=MarkovBull[row];
int bear=MarkovBear[row],doji=MarkovDoji[row],source=1,k,r;
if(MARKOV_BACKOFF && n<MARKOV_MIN_COUNT) {
n=0;bull=0;bear=0;doji=0;source=2;
for(k=0;k<MK_STATES;k++)
if(MarkovDirection[k]==MarkovDirection[st] &&
MarkovCloseZone[k]==MarkovCloseZone[st]) {
r=mkRow(id,k);
n+=MarkovOutgoing[r];bull+=MarkovBull[r];
bear+=MarkovBear[r];doji+=MarkovDoji[r];
}
if(n<MARKOV_MIN_COUNT) {
n=0;bull=0;bear=0;doji=0;source=3;
for(k=0;k<MK_STATES;k++)if(MarkovDirection[k]==MarkovDirection[st]) {
r=mkRow(id,k);
n+=MarkovOutgoing[r];bull+=MarkovBull[r];
bear+=MarkovBear[r];doji+=MarkovDoji[r];
}
}
if(n<MARKOV_MIN_COUNT) {
n=0;bull=0;bear=0;doji=0;source=4;
for(k=0;k<MK_STATES;k++) {
r=mkRow(id,k);
n+=MarkovOutgoing[r];bull+=MarkovBull[r];
bear+=MarkovBear[r];doji+=MarkovDoji[r];
}
}
}
F->predictionSource=source;F->nEffective=n;
var denom=n+MK_STATES*MARKOV_PRIOR;
if(denom<=0)return;
F->probabilityBull=(bull+330.0*MARKOV_PRIOR)/denom;
F->probabilityBear=(bear+330.0*MARKOV_PRIOR)/denom;
F->probabilityDoji=(doji+120.0*MARKOV_PRIOR)/denom;
for(k=0;k<MK_STATES;k++) {
int count=MarkovEdges[mkEdge(id,st,k)];
if(count>F->topCount) { F->topCount=count;F->topNext=k; }
}
}
int markovDecision(int id)
{
FXSTATE* F=&FX[id];
if(F->nEffective<MARKOV_MIN_COUNT)return 0;
if(F->probabilityBear>=MARKOV_MIN_DIRECTION &&
F->probabilityBear>F->probabilityBull)return -1;
if(F->probabilityBull>=MARKOV_MIN_DIRECTION &&
F->probabilityBull>F->probabilityBear)return 1;
return 0;
}
int passMarkov(int id,int side)
{
if(ENGINE_MODE!=3 && MARKOV_MODE==0)return 1;
if(FX[id].nEffective<MARKOV_MIN_COUNT)
return (ENGINE_MODE!=3 && !MARKOV_BLOCK_SPARSE);
return markovDecision(id)==side;
}
// VWAP original: N+1 prices in numerator but N deviations around same center.
// In Zorro, marketVol may be unavailable without Zorro S or compatible .t6 feed.
// VWAP_VOLUME_MODE=0 intentionally uses equal weights, NOT exact MQL tick-volume VWAP.
// marketVol() in Zorro S can be trade volume, quote rate or historical fVol.
// Matching MQL's iVolume() requires verified matching tick-volume history.
var selectedPrice(int offset)
{
var o=priceO(offset),h=priceH(offset),l=priceL(offset),c=priceC(offset);
if(VWAP_PRICE_MODE==1)return o;
if(VWAP_PRICE_MODE==2)return h;
if(VWAP_PRICE_MODE==3)return l;
if(VWAP_PRICE_MODE==4)return (h+l)*0.5;
if(VWAP_PRICE_MODE==5)return (h+l+c)/3.;
if(VWAP_PRICE_MODE==6)return (h+l+2*c)/4.;
return c;
}
int calculateBands(int id)
{
FXSTATE* F=&FX[id];
int window=VWAP_PERIOD, j;
if(VWAP_MATCH_MQL)window++;
var sv=0,pv=0,p,w;
for(j=0;j<window;j++) {
p=selectedPrice(j);
if(p<=0)return 0;
w=1;
if(VWAP_VOLUME_MODE==1)w=marketVol(j);
if(w<=0)return 0;
pv+=p*w;sv+=w;
}
if(sv<=0)return 0;
F->v=pv/sv;
var sq=0;
for(j=0;j<VWAP_PERIOD;j++) {
p=selectedPrice(j);
sq+=(p-F->v)*(p-F->v);
}
F->sigma=sqrt(sq/VWAP_PERIOD);
if(F->sigma<=0)return 0;
F->band[0]=F->v-DEVIATION_3*F->sigma;
F->band[1]=F->v-DEVIATION_2*F->sigma;
F->band[2]=F->v-DEVIATION_1*F->sigma;
F->band[3]=F->v;
F->band[4]=F->v+DEVIATION_1*F->sigma;
F->band[5]=F->v+DEVIATION_2*F->sigma;
F->band[6]=F->v+DEVIATION_3*F->sigma;
for(j=1;j<7;j++)if(F->band[j]<=F->band[j-1])return 0;
return 1;
}
void resetPhase(int id)
{
FX[id].phase=PH_SWEEP;FX[id].crossMask=0;
FX[id].sweepStamp=0;FX[id].retouch=0;
}
int findFX(string name)
{
int id;for(id=0;id<ActivePairs;id++)
if(FX[id].usable && !strcmp(FX[id].symbol,name))return id;
return -1;
}
var clockSeconds() {return wdate(0)*86400.;} // simulated/bar clock, not PC wall time
void completeCandle(int id)
{
FXSTATE* F=&FX[id];
F->o=priceO(0);F->h=priceH(0);F->l=priceL(0);F->c=priceC(0);
F->range=F->h-F->l;
F->valid=0;F->signature=-1;
F->dir=0;F->body=abs(F->c-F->o);
F->uw=F->h-max(F->o,F->c);F->lw=min(F->o,F->c)-F->l;
if(F->c>F->o)F->dir=1; else if(F->c<F->o)F->dir=-1;
if(F->range>0)F->valid=calculateBands(id);
if(F->valid)F->signature=signatureOf(id);
addObservation(id,F->signature);
predict(id);
if(!is(LOOKBACK) && F->valid)
printf("\n%s VWAP %.5f sig %i N %i Pbull %.3f Pbear %.3f",
F->symbol,F->v,F->signature,F->nEffective,
F->probabilityBull,F->probabilityBear);
resetPhase(id);
F->quoteKnown=0;
F->observedBar=Bar;
}
int crossedMask(int id,var before,var after)
{
int k,m=0;
for(k=0;k<7;k++) {
var b=FX[id].band[k];
if(before<b && after>=b)m |= (1<<k);
if(before>b && after<=b)m |= (1<<(7+k));
}
return m;
}
int crossedBit(int level,int up)
{
if(up)return (1<<level);
return (1<<(level+7));
}
void sweepEvent(int id,var before,var after,int newQuote)
{
if(!newQuote || FX[id].dir==0)return;
FXSTATE* F=&FX[id];
var t=PIP;
int m=crossedMask(id,before,after);
if(F->phase!=PH_SWEEP)F->crossMask |= m;
if(F->phase==PH_SWEEP) {
if(F->dir<0 && before<F->h && after>=F->h) {
F->phase=PH_RETURN;F->sweepStamp=clockSeconds();F->crossMask=m;
} else if(F->dir>0 && before>=F->l && after<F->l) {
F->phase=PH_RETURN;F->sweepStamp=clockSeconds();F->crossMask=m;
}
return;
}
if(ENGINE_MODE==0 && RESEARCH_ID==30 && SWEEP_TIMEOUT_SECONDS>0 &&
clockSeconds()-F->sweepStamp>SWEEP_TIMEOUT_SECONDS) {
resetPhase(id);return;
}
if(F->phase==PH_RETURN) {
if(F->dir<0 && after<F->h) {
F->phase=PH_READY;
if(ENGINE_MODE==0 && RESEARCH_ID==24)F->phase=PH_RETEST;
} else if(F->dir>0 && after>F->l) {
F->phase=PH_READY;
if(ENGINE_MODE==0 && RESEARCH_ID==24)F->phase=PH_RETEST;
}
return;
}
if(F->phase==PH_RETEST) {
if(F->dir<0) {
if(after>F->h+t) {resetPhase(id);return;}
if(after>=F->h-t)F->retouch=1;
if(F->retouch && before>after && after<F->h-t)F->phase=PH_READY;
} else {
if(after<F->l-t) {resetPhase(id);return;}
if(after<=F->l+t)F->retouch=1;
if(F->retouch && before<after && after>F->l+t)F->phase=PH_READY;
}
return;
}
if(F->phase==PH_READY) {
if(F->dir<0 && after>=F->h-t && after<F->h)F->retouch=1;
if(F->dir>0 && after<=F->l+t && after>F->l)F->retouch=1;
if(F->dir<0 && after>=F->h)resetPhase(id);
if(F->dir>0 && after<=F->l)resetPhase(id);
}
}
// All filters use side=+1 BUY and -1 SELL; candle sign is separate.
int payoffOK(int id,int side,var ask,var minPips)
{
if(side<0)return (ask-FX[id].v)>=minPips*PIP;
return (FX[id].v-ask)>=minPips*PIP;
}
int anatomy(int id,int rule,var ask)
{
FXSTATE* F=&FX[id];var R=F->range,B=F->body;
var u=F->uw,l=F->lw,o=F->o,c=F->c,h=F->h,lo=F->l;
if(rule==0)return 1;
if(R<=0)return 0;
if(rule==1)return c>o;
if(rule==2)return c<o;
if(rule==3)return c==o;
if(rule==4)return B/R>=0.70;
if(rule==5)return B/R<=0.20;
if(rule==6)return u/R>=0.50;
if(rule==7)return l/R>=0.50;
if(rule==8)return u>=2*l;
if(rule==9)return l>=2*u;
if(rule==10)return (c-lo)/R>=0.90;
if(rule==11)return (c-lo)/R<=0.10;
if(rule==12)return (o-lo)/R>=0.90;
if(rule==13)return (o-lo)/R<=0.10;
if(rule==14)return min(o,c)>lo+0.5*R;
if(rule==15)return max(o,c)<lo+0.5*R;
if(rule==16)return min(o,c)<=lo+0.5*R && max(o,c)>=lo+0.5*R;
if(rule==17)return abs(u-l)/R<=0.10;
if(rule==18)return B/R>=0.80;
if(rule==19) {if(F->dir<0)return u>=B;return l>=B;}
if(rule==20)return R<=SMALL_CANDLE_SPREAD_MULTIPLE*Spread;
if(rule==21)return abs(h-o)/R<=0.10 || abs(o-lo)/R<=0.10;
if(rule==22)return abs(h-c)/R<=0.10 || abs(c-lo)/R<=0.10;
if(rule==23)return c>o && u>B;
if(rule==24)return c<o && l>B;
if(rule==25)return F->sigma>0 && R/F->sigma>=Z_THRESHOLD;
return 0;
}
int bandRule(int id,int side,int n,var q,var ask)
{
FXSTATE* F=&FX[id];
var O=F->o,H=F->h,L=F->l,C=F->c,R=F->range;
var B=F->body,W=F->lw;if(side<0)W=F->uw;
var v=F->v,sd=F->sigma,d1=F->band[2],d2=F->band[1],d3=F->band[0];
var u1=F->band[4],u2=F->band[5],u3=F->band[6];
var eps=EPSILON_PIPS*PIP;
if(side<0) {
if(n==0)return q<=u3;
if(n==1)return H>=u1;if(n==2)return H>=u2;if(n==3)return H>=u3;
if(n==4)return C>=v;if(n==5)return C>=u1;
if(n==6)return O<v && C>=v;
if(n==7)return O<u1 && C>=u1;
if(n==8)return O<u2 && C>=u2;
if(n==9)return H>=u3 && C<u3;
if(n==10)return H>=u3 && C<u2;
if(n==11)return H>=u2 && C<u1;
if(n==12)return H>=u1 && C<v;
if(n==13)return q>=v && q<=u1;
if(n==14)return q>=u1 && q<=u2;
if(n==15)return q>=u2 && q<=u3;
if(n==16)return q<=v;
if(n==17)return q>v && q<=u3;
if(n==18)return H>=u3 && q>v && q<=u2;
if(n==19)return H>=u3 && q>v && q<=u1;
if(n==20)return H>=u2 && W>B;
if(n==21)return H>=u3 && W/R>=0.35;
if(n==22)return sd>0 && (H-v)/sd>=Z_THRESHOLD;
if(n==23)return sd>0 && (q-v)/sd>=0.5 && (q-v)/sd<=2.5;
if(n==24)return abs(q-u2)<=eps;
if(n==25)return abs(H-u3)<=eps;
if(n==26)return H>u3 && q<u3;
if(n==27)return q<u3 && H>=u3 && C>O;
if(n==28)return sd>0 && R/sd>=Z_THRESHOLD;
if(n==29)return W/R>=WICK_THRESHOLD && q>v;
if(n==30)return q>v && payoffOK(id,side,ask,MIN_REWARD_PIPS);
} else {
if(n==0)return q>=d3;
if(n==1)return L<=d1;if(n==2)return L<=d2;if(n==3)return L<=d3;
if(n==4)return C<=v;if(n==5)return C<=d1;
if(n==6)return O>v && C<=v;
if(n==7)return O>d1 && C<=d1;
if(n==8)return O>d2 && C<=d2;
if(n==9)return L<=d3 && C>d3;
if(n==10)return L<=d3 && C>d2;
if(n==11)return L<=d2 && C>d1;
if(n==12)return L<=d1 && C>v;
if(n==13)return q>=d1 && q<=v;
if(n==14)return q>=d2 && q<=d1;
if(n==15)return q>=d3 && q<=d2;
if(n==16)return q>=v;
if(n==17)return q>=d3 && q<v;
if(n==18)return L<=d3 && q>=d2 && q<v;
if(n==19)return L<=d3 && q>=d1 && q<v;
if(n==20)return L<=d2 && W>B;
if(n==21)return L<=d3 && W/R>=0.35;
if(n==22)return sd>0 && (v-L)/sd>=Z_THRESHOLD;
if(n==23)return sd>0 && (q-v)/sd>=-2.5 && (q-v)/sd<=-0.5;
if(n==24)return abs(q-d2)<=eps;
if(n==25)return abs(L-d3)<=eps;
if(n==26)return L<d3 && q>d3;
if(n==27)return q>d3 && L<=d3 && C<O;
if(n==28)return sd>0 && R/sd>=Z_THRESHOLD;
if(n==29)return W/R>=WICK_THRESHOLD && q<v;
if(n==30)return q<v && payoffOK(id,side,ask,MIN_REWARD_PIPS);
}
return 0;
}
int confirmation(int id,int side,int n,var q)
{
FXSTATE* F=&FX[id];
if(n==1)return 1;
if(n>=2 && n<=5) {
int b=3;
if(side<0) {
if(n==2)b=6;if(n==3)b=5;if(n==4)b=4;
return (F->crossMask & crossedBit(b,0))!=0;
}
if(n==2)b=0;if(n==3)b=1;if(n==4)b=2;
return (F->crossMask & crossedBit(b,1))!=0;
}
if(n>=6 && n<=8) {
var ratio=0.10;
if(n==7)ratio=0.25;
if(n==8)ratio=0.50;
if(side<0)return q<=F->h-ratio*F->range;
return q>=F->l+ratio*F->range;
}
if(n==9)return F->sweepStamp>0 && !F->retouch &&
clockSeconds()-F->sweepStamp>=SWEEP_TIMEOUT_SECONDS;
if(n==10)return F->retouch;
if(n==11) {
if(side<0)return (F->crossMask & crossedBit(6,0))!=0 &&
q>=F->band[5] && q<=F->band[6];
return (F->crossMask & crossedBit(0,1))!=0 &&
q>=F->band[0] && q<=F->band[1];
}
if(n==12) {
if(side<0)return (F->crossMask & crossedBit(5,0))!=0 &&
q>=F->band[4] && q<=F->band[5];
return (F->crossMask & crossedBit(1,1))!=0 &&
q>=F->band[1] && q<=F->band[2];
}
return 0;
}
var protectiveStop(int id,int side)
{
if(side<0)return FX[id].h+STOP_PERCENT*0.01*FX[id].range;
return FX[id].l-STOP_PERCENT*0.01*FX[id].range;
}
int currencyConflict(int id)
{
// All 28 are known 6-letter FX roots. Split by currency strings.
int k;
string a=ForexNames[id];
string base=strmid(a,0,3),quote=strmid(a,4,3);
for(k=0;k<ActivePairs;k++) {
if(k==id || !FX[k].usable)continue;
if(!asset(FX[k].symbol))continue;
algo("MK780");
if(NumOpenLong+NumOpenShort<=0)continue;
string b=strmid(ForexNames[k],0,3),q=strmid(ForexNames[k],4,3);
if(!strcmp(base,b) || !strcmp(base,q) ||
!strcmp(quote,b) || !strcmp(quote,q)) {
asset(FX[id].symbol);algo("MK780");return 1;
}
}
asset(FX[id].symbol);algo("MK780");
return 0;
}
int researchGate(int id,int n,int side,var q,var ask)
{
FXSTATE* F=&FX[id];int s=(side<0);
var o=F->o,h=F->h,l=F->l,c=F->c,r=F->range;
var u1=F->band[4],u2=F->band[5],u3=F->band[6];
var d1=F->band[2],d2=F->band[1],d3=F->band[0],v=F->v;
var wick=F->lw;if(s)wick=F->uw;
if(n==0 || n==26)return bandRule(id,side,0,q,ask);
if(n>=1 && n<=3)return bandRule(id,side,n,q,ask);
if(n==4) {if(s)return h>=u3 && q<u3;return l<=d3 && q>d3;}
if(n==5 || n==19) {if(s)return h>=u3 && q<=u2;return l<=d3 && q>=d2;}
if(n==6 || n==20) {if(s)return h>=u3 && q<=u1;return l<=d3 && q>=d1;}
if(n==7) {if(s)return h>=u2 && wick>F->body;return l<=d2 && wick>F->body;}
if(n==8) {if(s)return h>=u3 && wick/r>=0.35;return l<=d3 && wick/r>=0.35;}
if(n==9) {if(s)return h>=u2 && c<u1;return l<=d2 && c>d1;}
if(n==10) {if(s)return o<u1 && c>=u1;return o>d1 && c<=d1;}
if(n==11) {if(s)return o<u2 && c>=u2;return o>d2 && c<=d2;}
if(n==12) {if(s)return q>v && q<=u1;return q>=d1 && q<v;}
if(n==13) {if(s)return q>=u1 && q<=u2;return q>=d2 && q<=d1;}
if(n==14) {if(s)return q>=u2 && q<=u3;return q>=d3 && q<=d2;}
if(n==15)return payoffOK(id,side,ask,MIN_REWARD_PIPS);
if(n==16)return r/F->sigma>=1.;
if(n==17)return r/F->sigma<=1.;
if(n==18) {if(s)return h>=u3 && wick/r>=0.4;return l<=d3 && wick/r>=0.4;}
if(n>=21 && n<=23) {
if(s && h<u3)return 0;
if(!s && l>d3)return 0;
return confirmation(id,side,n-19,q);
}
if(n==24) {
if(s)return h>=u3 && F->retouch && F->phase==PH_READY;
return l<=d3 && F->retouch && F->phase==PH_READY;
}
if(n==25) {if(s)return h>=u3 && q<=h-0.1*r;return l<=d3 && q>=l+0.1*r;}
if(n==27) {
var stop=protectiveStop(id,side);
var risk=ask-stop,reward=v-ask;
if(s){risk=stop-ask;reward=ask-v;}
return risk>0 && reward>0 && reward/risk>=MIN_RISK_REWARD;
}
if(n==28) {
if(s)return h>=u3 && c>=u2 && zoneAt(id,q)==6;
return l<=d3 && c<=d2 && zoneAt(id,q)==1;
}
if(n==29) {
if(s)return h>=u3 && c<u2 && zoneAt(id,q)==5;
return l<=d3 && c>d2 && zoneAt(id,q)==2;
}
if(n==30) {
if(s && h<u3)return 0;if(!s && l>d3)return 0;
return clockSeconds()-F->sweepStamp<=60.;
}
if(n==31) {
if(s)return h>=u3 && q<=h-MIN_RETURN_TICKS*PIP;
return l<=d3 && q>=l+MIN_RETURN_TICKS*PIP;
}
if(n==32) {
if(s)return h>=u3 && Spread<=PIP;
return l<=d3 && Spread<=PIP;
}
if(n==33) {
if(s && h<u3)return 0;if(!s && l>d3)return 0;
return !currencyConflict(id);
}
if(n==34 || n==35) {if(s)return h>=u3;return l<=d3;}
return 0;
}
// Universal 300-event event catalog from uploaded MQL4:
// event=CATALOG_ID/3 (0..99), action=CATALOG_ID%3 (BUY/SELL/EXIT).
int catalogEvent(int id,var prior,var current,int observed,int newBar)
{
FXSTATE* F=&FX[id];
int evt=CATALOG_ID/3;
int beforeZone=7-zoneAt(id,prior),nowZone=7-zoneAt(id,current);
int bandIndex, up, source,dest,counter;
var line;
if(evt<8)return observed && beforeZone!=nowZone && nowZone==evt;
if(evt<22) {
if(!observed)return 0;
bandIndex=(evt-8)/2;up=((evt-8)%2)==0;
line=F->band[6-bandIndex];
if(up)return prior<=line && current>line;
return prior>=line && current<line;
}
if(evt<36) {
if(!newBar)return 0;
bandIndex=(evt-22)/2;up=((evt-22)%2)==0;
line=F->band[6-bandIndex];
if(up)return F->h>=line && F->c<line;
return F->l<=line && F->c>line;
}
if(evt<92) {
if(!observed || beforeZone==nowZone)return 0;
counter=evt-36;
for(source=0;source<8;source++)for(dest=0;dest<8;dest++) {
if(source==dest)continue;
if(counter==0)return beforeZone==source && nowZone==dest;
counter--;
}
return 0;
}
// One persistence signal per completed bar after two observations in same zone.
if(!observed || nowZone!=evt-92 || beforeZone!=nowZone ||
F->lastPersistBar==Bar)return 0;
F->lastPersistBar=Bar;return 1;
}
int effectiveExit()
{
if(ENGINE_MODE==0 && (RESEARCH_ID==18 || RESEARCH_ID==19))return 2;
if(ENGINE_MODE==0 && RESEARCH_ID==34)return 1;
return EXIT_RULE_ID;
}
var exitTarget(int id,int side,var entry,int rule)
{
FXSTATE* F=&FX[id];
var v=F->v,sd=F->sigma,r=F->range;
if(rule==2) {if(side<0)return F->band[4];return F->band[2];}
if(rule==3) {if(side<0)return F->band[5];return F->band[1];}
if(rule==4) {if(side<0)return F->band[2];return F->band[4];}
if(rule==5) {if(side<0)return F->band[1];return F->band[5];}
if(rule==6) {if(side<0)return F->band[0];return F->band[6];}
if(rule==7)return (entry+v)*0.5;
if(rule==8)return entry+side*EXIT_SIGMA_MULTIPLE*sd;
if(rule==9)return entry+side*EXIT_RANGE_MULTIPLE*r;
if(rule==10)return v+side*EXIT_RANGE_PERCENT*0.01*r;
return v;
}
int crossedExit(int side,var entry,var price,var target,int allowAdverse)
{
if(side<0) {
if(target<entry)return price<=target;
return allowAdverse && price>=target;
}
if(target>entry)return price>=target;
return allowAdverse && price<=target;
}
int managePosition(int id,int newBar)
{
FXSTATE* F=&FX[id];
int rule=effectiveExit();
int wanted=0;
// for(current_trades) loops only current Asset AND Algo.
for(current_trades) {
if(!TradeIsOpen || TradeIsPhantom)continue;
int side=1;
if(TradeIsShort)side=-1;
var entry=TradePriceOpen; // Zorro's ask-based entry reference
var ask=AskPrice,bid=BidPrice;
var target=exitTarget(id,side,entry,rule);
if(ENGINE_MODE==0 && RESEARCH_ID==34 && F->frozenCenter>0)
target=F->frozenCenter;
wanted=0;
if(rule>=1 && rule<=15) {
if(rule!=15 || TradeBars>=EXIT_WAIT_BARS)
if(side<0)wanted=crossedExit(side,entry,ask,target,rule==14);
else wanted=crossedExit(side,entry,bid,target,rule==14);
}
if(rule==16)wanted=(TradeBars*TIMEFRAME_MINUTES>=EXIT_DEADLINE_MINUTES);
if(rule==17 && newBar)wanted=(F->dir==-side);
if(rule==18)wanted=(F->dir==-side && F->phase==PH_READY);
if(rule==19) {
if(!F->partialDone) {
var first=exitTarget(id,side,entry,2);
int hitFirst=0;
if(side<0)hitFirst=crossedExit(side,entry,ask,first,0);
else hitFirst=crossedExit(side,entry,bid,first,0);
if(hitFirst) {
int partial=(int)(TradeLots*PARTIAL_PERCENT/100.);
if(partial>=1 && TradeLots-partial>=1) {
if(exitTrade(ThisTrade,0,partial))F->partialDone=1;
} else F->partialDone=1;
}
}
if(F->partialDone)
{ if(side<0)wanted=crossedExit(side,entry,ask,F->v,0);
else wanted=crossedExit(side,entry,bid,F->v,0); }
}
if(rule==20) {
var gap=TRAIL_SIGMA_MULTIPLE*F->sigma;
if(F->highWater==0 || bid>F->highWater)F->highWater=bid;
if(F->lowWater==0 || ask<F->lowWater)F->lowWater=ask;
if(side>0 && F->highWater>entry+gap)
wanted=(bid<=max(F->v-gap,F->highWater-gap));
if(side<0 && F->lowWater<entry-gap)
wanted=(ask>=min(F->v+gap,F->lowWater+gap));
}
if(wanted && ENABLE_SOFTWARE_EXIT)
exitTrade(ThisTrade); // auto-retry on subsequent quote if still open
}
return wanted;
}
int countPortfolio()
{
int n=0;
for(open_trades)
if(TradeIsOpen && !TradeIsPhantom && !strcmp(TradeAlgo,"MK780"))n++;
return n;
}
int entryEligible(int id,int side,var ask)
{
if(!FX[id].valid || FX[id].range<=0)return 0;
if(NumOpenLong+NumOpenShort+NumPendingLong+NumPendingShort>0)return 0;
if(countPortfolio()>=MAX_PORTFOLIO_POSITIONS)return 0;
var spreadLimit=MAX_SPREAD_PIPS;
if(spreadLimit>0 && Spread>spreadLimit*PIP)return 0;
if(side<0 && protectiveStop(id,side)<=ask+PIP)return 0;
if(side>0 && protectiveStop(id,side)>=ask-Spread-PIP)return 0;
return 1;
}
int placeSignal(int id,int side,var ask)
{
FXSTATE* F=&FX[id];
if(!entryEligible(id,side,ask))return 0;
var stop=protectiveStop(id,side);
var trigger=0;
if(ENTRY_STYLE==1) {
if(side<0)trigger=F->h-PENDING_PERCENT*0.01*F->range;
else trigger=F->l+PENDING_PERCENT*0.01*F->range;
// Zorro Entry is always an ASK price; SELL stop needs ask conversion.
// Current Spread is only an estimate of future spread at execution.
if(side<0)trigger+=Spread;
if(side<0 && !(trigger<ask-PIP))return 0;
if(side>0 && !(trigger>ask+PIP))return 0;
}
Entry=trigger;
Stop=stop; // absolute ASK-level; original MQL broker stops may differ
TakeProfit=0; // software VWAP exits, do not set a wrong-side TP
StopFactor=0;
if(ENABLE_BROKER_STOP)StopFactor=1;
EntryTime=ENTRY_WAIT_BARS;
MaxLong=1;MaxShort=1;
Lots=ORDER_LOTS;
TRADE* tr=0;
if(side<0)tr=enterShort();
else tr=enterLong();
Entry=0;Stop=0;TakeProfit=0;
if(!tr)return 0;
F->activeBefore=1;
F->frozenCenter=F->v;
F->highWater=0;F->lowWater=0;
F->partialDone=0;
F->lastEntryBar=Bar;
F->lastEntryLevel=trigger;
F->entryStamp=clockSeconds();
resetPhase(id);
return 1;
}
void cancelOldPending()
{
// Called after a new completed candle (reference levels changed).
for(current_trades) {
if(TradeIsPending)exitTrade(ThisTrade);
}
}
void onQuote(int id,int newBar)
{
FXSTATE* F=&FX[id];
if(!F->usable || !F->valid || F->signature<0)return;
// Set the current asset/algo context before invoking trade statistics/functions.
if(!asset(F->symbol))return;
algo("MK780");
var ask=AskPrice;
if(ask<=0)return;
var spreadLimit=MAX_SPREAD_PIPS;
if(spreadLimit>0 && Spread>spreadLimit*PIP)return;
int present=NumOpenLong+NumOpenShort+NumPendingLong+NumPendingShort;
if(F->activeBefore && present==0) {
resetPhase(id);
F->quoteKnown=0;F->activeBefore=0;
F->partialDone=0;F->frozenCenter=0;
}
if(present>0)F->activeBefore=1;
int observed=F->quoteKnown && abs(ask-F->previousAsk)>PIP*0.001;
var prev=F->previousAsk;
F->previousAsk=ask;
F->quoteKnown=1;
if(observed && (present==0 || effectiveExit()==18))
sweepEvent(id,prev,ask,observed);
int cat=0;
if(ENGINE_MODE==1)cat=catalogEvent(id,prev,ask,observed,newBar);
if(NumOpenLong+NumOpenShort>0) {
if(ENGINE_MODE==1 && cat && CATALOG_ID%3==2) {
exitLong();exitShort();
return;
}
managePosition(id,newBar);
return;
}
if(NumPendingLong+NumPendingShort>0)return;
if(is(LOOKBACK))return;
if(SIGNATURE_FILTER>=0 && F->signature!=SIGNATURE_FILTER)return;
if(!anatomy(id,CANDLE_RULE_ID,ask))return;
if(ENGINE_MODE==3 && !MARKOV_NEED_SWEEP) {
if(F->lastEntryBar==Bar)return;
int decision=markovDecision(id);
if(decision && passMarkov(id,decision))placeSignal(id,decision,ask);
return;
}
if(ENGINE_MODE==1) {
if(!cat || CATALOG_ID%3==2)return;
int side=-1;
if(CATALOG_ID%3==0)side=1;
if(passMarkov(id,side))placeSignal(id,side,ask);
return;
}
if(F->phase!=PH_READY || F->dir==0)return;
int side=F->dir;
if(!passMarkov(id,side))return;
if(ENGINE_MODE==0 && !researchGate(id,RESEARCH_ID,side,ask,ask))return;
if(ENGINE_MODE==2) {
if(side<0 && (!bandRule(id,side,SELL_STATIC_ID,ask,ask) ||
!confirmation(id,side,SELL_EVENT_ID,ask)))return;
if(side>0 && (!bandRule(id,side,BUY_STATIC_ID,ask,ask) ||
!confirmation(id,side,BUY_EVENT_ID,ask)))return;
}
placeSignal(id,side,ask);
}
// Zorro run() is called after each completed bar. In run, [0] is the
// finished bar; in tick, [0] is still changing, and must NOT update VWAP.
void run()
{
if(is(INITRUN)) {
initForexNames();
BarPeriod=TIMEFRAME_MINUTES;
LookBack=MK_WINDOW+VWAP_PERIOD+40;
set(TICKS);
TickTime=0;
MaxLong=1;MaxShort=1;
ActivePairs=MAX_PAIRS;
if(SINGLE_ASSET)ActivePairs=1;
if(!initMarkov()) {quit("!Cannot allocate 780-state Markov tables");return;}
int loaded=assetList(ASSET_LIST);
if(!loaded) {quit("!Asset list unavailable");return;}
int i;
for(i=0;i<ActivePairs;i++) {
FX[i].symbol=ForexNames[i];
if(SINGLE_ASSET)FX[i].symbol=SINGLE_ASSET_NAME;
FX[i].usable=0;FX[i].signature=-1;FX[i].lastEntryBar=-1;
FX[i].lastPersistBar=-1;FX[i].ringHead=0;FX[i].ringSize=0;
resetPhase(i);
// Assets must be selected in INITRUN to load/subscribed histories.
if(asset(FX[i].symbol)) {
FX[i].usable=1;
algo("MK780");
} else
printf("\nUnavailable asset: %s (check %s)",FX[i].symbol,ASSET_LIST);
}
return;
}
if(is(EXITRUN)) {releaseMarkov();return;}
int i;
for(i=0;i<ActivePairs;i++) {
if(!FX[i].usable)continue;
if(!asset(FX[i].symbol))continue;
algo("MK780");
// reference bar data + new Markov state, no future data or files.
completeCandle(i);
if(!is(LOOKBACK)) {
cancelOldPending(); // new candle invalidates prior candle's STOP level
onQuote(i,1); // completed-candle rejection and X17 exit
}
}
}
void tick()
{
if(is(INITRUN) || is(EXITRUN))return;
// tick() is called for the subscribed asset that just got a new quote.
int id=findFX(Asset);
if(id<0)return;
onQuote(id,0);
}
|
|
|
|